From 94e138a27795139f8bf7de31dc3c64bcdf936bc3 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 7 Feb 2022 17:19:27 +0100 Subject: [PATCH 0001/2609] Update database.md (#7663) --- database.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database.md b/database.md index f584d198015..0e9d392db48 100644 --- a/database.md +++ b/database.md @@ -12,10 +12,11 @@ ## Introduction -Almost every modern web application interacts with a database. Laravel makes interacting with databases extremely simple across a variety of supported databases using raw SQL, a [fluent query builder](/docs/{{version}}/queries), and the [Eloquent ORM](/docs/{{version}}/eloquent). Currently, Laravel provides first-party support for four databases: +Almost every modern web application interacts with a database. Laravel makes interacting with databases extremely simple across a variety of supported databases using raw SQL, a [fluent query builder](/docs/{{version}}/queries), and the [Eloquent ORM](/docs/{{version}}/eloquent). Currently, Laravel provides first-party support for five databases:
+- MariaDB 10.2+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) - MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) - PostgreSQL 9.6+ ([Version Policy](https://www.postgresql.org/support/versioning/)) - SQLite 3.8.8+ From d1fa1931d5313d6793339ef074a6313ef0b61057 Mon Sep 17 00:00:00 2001 From: Luca <72138632+lucaxue@users.noreply.github.com> Date: Tue, 8 Feb 2022 18:58:17 +0000 Subject: [PATCH 0002/2609] Correct typo for `Rule::forEach` validation section (#7678) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index cf01172049d..859927ad5c1 100644 --- a/releases.md +++ b/releases.md @@ -347,7 +347,7 @@ Laravel now includes pagination views built using [Bootstrap 5](https://getboots _Improved validation of nested array inputs was contributed by [Steve Bauman](https://github.com/stevebauman)_. -Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may now accomplish this using the `Rule::foreEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: +Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may now accomplish this using the `Rule::forEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: use App\Rules\HasPermission; use Illuminate\Support\Facades\Validator; From 9871b627f83ecf235ff439e7599be0ca95b39623 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 9 Feb 2022 17:52:50 +0100 Subject: [PATCH 0003/2609] Update contributions.md --- contributions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributions.md b/contributions.md index baee2ac952b..2e6720e1342 100644 --- a/contributions.md +++ b/contributions.md @@ -74,7 +74,7 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest stable branch or to the [current LTS branch](/docs/{{version}}/releases#support-policy). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest stable branch. Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. **Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch. From 0a39be938131d1ba954f54fd5fc01bcbcc130f97 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 9 Feb 2022 17:53:54 +0100 Subject: [PATCH 0004/2609] Update releases.md --- releases.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releases.md b/releases.md index a82382c6782..9740703c934 100644 --- a/releases.md +++ b/releases.md @@ -23,14 +23,14 @@ At this time, PHP's [named arguments](https://www.php.net/manual/en/functions.ar ## Support Policy -For LTS releases, such as Laravel 9, bug fixes are provided for 2 years and security fixes are provided for 3 years. These releases provide the longest window of support and maintenance. For general releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). +For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | | --- | --- | --- | --- | --- | | 6 (LTS) | 7.2 - 8.0 | September 3rd, 2019 | January 25th, 2022 | September 6th, 2022 | | 7 | 7.2 - 8.0 | March 3rd, 2020 | October 6th, 2020 | March 3rd, 2021 | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | -| 9 (LTS) | 8.0 - 8.1 | February 8th, 2022 | February 8th, 2024 | February 8th, 2025 | +| 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 8th, 2024 | | 10 | 8.0 - 8.1 | February 7th, 2023 | August 7th, 2024 | February 7th, 2025 |
From bdc1629ce5b121d6d0e65f7d4c154cba70bb5ff0 Mon Sep 17 00:00:00 2001 From: skys215 Date: Thu, 10 Feb 2022 22:58:24 +0800 Subject: [PATCH 0005/2609] Added version number when install via Composer (#7691) --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 112207793dd..2acb8a91b7e 100644 --- a/installation.md +++ b/installation.md @@ -157,7 +157,7 @@ If you do not specify which services you would like configured, a default stack If your computer already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: - composer create-project laravel/laravel example-app + composer create-project laravel/laravel:^8.0 example-app cd example-app From c24b35e43f354f8a20311fb7b7f75fa3b77cdc17 Mon Sep 17 00:00:00 2001 From: Script47 Date: Mon, 14 Feb 2022 17:11:29 +0000 Subject: [PATCH 0006/2609] Fix unescaped character breaking docs (#7713) --- scheduling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index 7c819e7f048..04c969fd107 100644 --- a/scheduling.md +++ b/scheduling.md @@ -162,7 +162,7 @@ Method | Description `->thursdays();` | Limit the task to Thursday `->fridays();` | Limit the task to Friday `->saturdays();` | Limit the task to Saturday -`->days(array|mixed);` | Limit the task to specific days +`->days(array\|mixed);` | Limit the task to specific days `->between($startTime, $endTime);` | Limit the task to run between start and end times `->unlessBetween($startTime, $endTime);` | Limit the task to not run between start and end times `->when(Closure);` | Limit the task based on a truth test From 4bff2121eb2d1ffe7234c020cb75cfd3ba5b9381 Mon Sep 17 00:00:00 2001 From: PHPGuus Date: Fri, 18 Feb 2022 20:46:51 +0000 Subject: [PATCH 0007/2609] Update validate.md (#7729) Fix `foreEach` typo. --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index a6270d91c9e..afb9e30ae67 100644 --- a/validation.md +++ b/validation.md @@ -1669,7 +1669,7 @@ Likewise, you may use the `*` character when specifying [custom validation messa #### Accessing Nested Array Data -Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may accomplish this using the `Rule::foreEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: +Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may accomplish this using the `Rule::forEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: use App\Rules\HasPermission; use Illuminate\Support\Facades\Validator; From dcde006dc6107636ce372d540b16405cbcd201ad Mon Sep 17 00:00:00 2001 From: Alan Hardman Date: Fri, 18 Feb 2022 17:24:55 -0700 Subject: [PATCH 0008/2609] Adding Scout query callback documentation --- scout.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scout.md b/scout.md index 52ba3a809bb..95bb36d5b9a 100644 --- a/scout.md +++ b/scout.md @@ -24,6 +24,7 @@ - [Pagination](#pagination) - [Soft Deleting](#soft-deleting) - [Customizing Engine Searches](#customizing-engine-searches) + - [Working with Eloquent Results](#working-with-eloquent-results) - [Custom Engines](#custom-engines) - [Builder Macros](#builder-macros) @@ -550,6 +551,20 @@ If you need to perform advanced customization of the search behavior of an engin } )->get(); + +### Working with Eloquent Results + +If you need to add additional Eloquent behavior to the search results, you may use the `query` method. This callback is run after building the initial Eloquent query for the matched result set, but before executing the query to load the final models. It works well for things like specifying which columns to load, and eager-loading relations: + + use App\Models\Order; + use Illuminate\Database\Eloquent\Builder; + + $orders = Order::search('Star Trek') + ->query(fn (Builder $builder) => $builder->with('comments')) + ->get(); + +> {note} As this callback is typically triggered after the results are already queried from the search engine, the `query` method should not be used for filtering results. You should use [where clauses](#where-clauses) instead. + ## Custom Engines From 8d725abba8d0868fa133c2c32fa400f0ca67b403 Mon Sep 17 00:00:00 2001 From: JD Saravaiya <39241212+jaydeepsaravaiya@users.noreply.github.com> Date: Mon, 21 Feb 2022 20:44:48 +0530 Subject: [PATCH 0009/2609] fixed typo `broadcastsOn` @ LN:885 (#7732) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index a88db424701..155e7cc3547 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -882,7 +882,7 @@ It is common to broadcast events when your application's [Eloquent models](/docs However, if you are not using these events for any other purposes in your application, it can be cumbersome to create event classes for the sole purpose of broadcasting them. To remedy this, Laravel allows you to indicate that an Eloquent model should automatically broadcast its state changes. -To get started, your Eloquent model should use the `Illuminate\Database\Eloquent\BroadcastsEvents` trait. In addition, the model should define a `broadcastsOn` method, which will return an array of channels that the model's events should broadcast on: +To get started, your Eloquent model should use the `Illuminate\Database\Eloquent\BroadcastsEvents` trait. In addition, the model should define a `broadcastOn` method, which will return an array of channels that the model's events should broadcast on: ```php Date: Mon, 21 Feb 2022 22:15:42 +0700 Subject: [PATCH 0010/2609] [9.x] Fix Rule::foreEach typo (#7731) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index a6270d91c9e..afb9e30ae67 100644 --- a/validation.md +++ b/validation.md @@ -1669,7 +1669,7 @@ Likewise, you may use the `*` character when specifying [custom validation messa #### Accessing Nested Array Data -Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may accomplish this using the `Rule::foreEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: +Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may accomplish this using the `Rule::forEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: use App\Rules\HasPermission; use Illuminate\Support\Facades\Validator; From a61b6d9fc4655dddab009afd54c0746021262438 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 21 Feb 2022 09:40:17 -0600 Subject: [PATCH 0011/2609] formatting --- scout.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scout.md b/scout.md index 95bb36d5b9a..578da6ad02e 100644 --- a/scout.md +++ b/scout.md @@ -24,7 +24,6 @@ - [Pagination](#pagination) - [Soft Deleting](#soft-deleting) - [Customizing Engine Searches](#customizing-engine-searches) - - [Working with Eloquent Results](#working-with-eloquent-results) - [Custom Engines](#custom-engines) - [Builder Macros](#builder-macros) @@ -551,19 +550,20 @@ If you need to perform advanced customization of the search behavior of an engin } )->get(); - -### Working with Eloquent Results + +#### Customizing The Eloquent Results Query -If you need to add additional Eloquent behavior to the search results, you may use the `query` method. This callback is run after building the initial Eloquent query for the matched result set, but before executing the query to load the final models. It works well for things like specifying which columns to load, and eager-loading relations: +After Scout retrieves a list of matching Eloquent models from your application's search engine, Eloquent is used to retrieve all of the matching models by their primary keys. You may customize this query by invoking the `query` method. The `query` method accepts a closure that will receive the Eloquent query builder instance as an argument: - use App\Models\Order; - use Illuminate\Database\Eloquent\Builder; +```php +use App\Models\Order; - $orders = Order::search('Star Trek') - ->query(fn (Builder $builder) => $builder->with('comments')) - ->get(); +$orders = Order::search('Star Trek') + ->query(fn ($query) => $query->with('invoices')) + ->get(); +``` -> {note} As this callback is typically triggered after the results are already queried from the search engine, the `query` method should not be used for filtering results. You should use [where clauses](#where-clauses) instead. +Since this callback is invoked after the relevant models have already been retrieved from your application's search engine, the `query` method should not be used for "filtering" results. Instead, you should use [Scout where clauses](#where-clauses). ## Custom Engines From e24a8df206eb75d05df2deb5695025e8b6838d0c Mon Sep 17 00:00:00 2001 From: Angelin Calu Date: Mon, 21 Feb 2022 23:40:06 +0200 Subject: [PATCH 0012/2609] Update deployment.md (#7738) * Update deployment.md cURL is now required by `spatie/laravel-ignition` * Update deployment.md Co-authored-by: Taylor Otwell --- deployment.md | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment.md b/deployment.md index 5f67cef82af..132becb2454 100644 --- a/deployment.md +++ b/deployment.md @@ -27,6 +27,7 @@ The Laravel framework has a few system requirements. You should ensure that your - PHP >= 8.0 - BCMath PHP Extension - Ctype PHP Extension +- cURL PHP Extension - DOM PHP Extension - Fileinfo PHP Extension - JSON PHP Extension From 22617f96c882284b972019e40c2529aaa358abf3 Mon Sep 17 00:00:00 2001 From: Steven Kemp Date: Mon, 21 Feb 2022 17:16:07 -0500 Subject: [PATCH 0013/2609] Minor typo fix (#7739) --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index 77f6be663cb..24069979ec8 100644 --- a/octane.md +++ b/octane.md @@ -231,7 +231,7 @@ Since your application is loaded in memory once when the Octane server starts, a php artisan octane:start --watch ``` -Before using this feature, you should ensure that [Node](https://nodejs.org) is installed within your local development environment. In addition, you should install the [Chokidar](https://github.com/paulmillr/chokidar) file-watching library within your project:library: +Before using this feature, you should ensure that [Node](https://nodejs.org) is installed within your local development environment. In addition, you should install the [Chokidar](https://github.com/paulmillr/chokidar) file-watching library within your project: ```shell npm install --save-dev chokidar From 4da3df803f6fc31e74b2ba85d27f2198dd7055fd Mon Sep 17 00:00:00 2001 From: Yoeri Boven Date: Tue, 22 Feb 2022 15:44:44 +0100 Subject: [PATCH 0014/2609] [9.x] Adds `lang_path()` documentation (#7741) * Update helpers.md * Update helpers.md Co-authored-by: Taylor Otwell --- helpers.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/helpers.md b/helpers.md index d26757a8f99..92000e1c0a6 100644 --- a/helpers.md +++ b/helpers.md @@ -75,6 +75,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [base_path](#method-base-path) [config_path](#method-config-path) [database_path](#method-database-path) +[lang_path](#method-lang-path) [mix](#method-mix) [public_path](#method-public-path) [resource_path](#method-resource-path) @@ -1068,6 +1069,15 @@ The `database_path` function returns the fully qualified path to your applicatio $path = database_path('factories/UserFactory.php'); + +#### `lang_path()` {.collection-method} + +The `lang_path` function returns the fully qualified path to your application's `lang` directory. You may also use the `lang_path` function to generate a fully qualified path to a given file within the directory: + + $path = lang_path(); + + $path = lang_path('en/messages.php'); + #### `mix()` {.collection-method} From 2961182d0f705bb634386dbe66b843575de2a460 Mon Sep 17 00:00:00 2001 From: Yoeri Boven Date: Tue, 22 Feb 2022 15:45:09 +0100 Subject: [PATCH 0015/2609] Update helpers.md (#7740) --- helpers.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/helpers.md b/helpers.md index 92000e1c0a6..2241cdde146 100644 --- a/helpers.md +++ b/helpers.md @@ -98,6 +98,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::before](#method-str-before) [Str::beforeLast](#method-str-before-last) [Str::between](#method-str-between) +[Str::betweenFirst](#method-str-between-first) [Str::camel](#method-camel-case) [Str::contains](#method-str-contains) [Str::containsAll](#method-str-contains-all) @@ -163,6 +164,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [before](#method-fluent-str-before) [beforeLast](#method-fluent-str-before-last) [between](#method-fluent-str-between) +[betweenFirst](#method-fluent-str-between-first) [camel](#method-fluent-str-camel) [contains](#method-fluent-str-contains) [containsAll](#method-fluent-str-contains-all) @@ -1220,6 +1222,17 @@ The `Str::between` method returns the portion of a string between two values: $slice = Str::between('This is my name', 'This', 'name'); // ' is my ' + + +#### `Str::betweenFirst()` {.collection-method} + +The `Str::betweenFirst` method returns the smallest possible portion of a string between two values: + + use Illuminate\Support\Str; + + $slice = Str::betweenFirst('[a] bc [d]', '[', ']'); + + // 'a' #### `Str::camel()` {.collection-method} @@ -2004,6 +2017,17 @@ The `between` method returns the portion of a string between two values: $converted = Str::of('This is my name')->between('This', 'name'); // ' is my ' + + +#### `betweenFirst` {.collection-method} + +The `betweenFirst` method returns the smallest possible portion of a string between two values: + + use Illuminate\Support\Str; + + $converted = Str::of('[a] bc [d]')->betweenFirst('[', ']'); + + // 'a' #### `camel` {.collection-method} From f6286d44a55b3ba99de17769e42ac99ab811e670 Mon Sep 17 00:00:00 2001 From: ARI <99682351+bvtterfly@users.noreply.github.com> Date: Tue, 22 Feb 2022 19:12:59 +0330 Subject: [PATCH 0016/2609] [9.x] Attribute::make documentation (#7720) --- eloquent-mutators.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index c112ebd8c1f..1cee3b59396 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -48,7 +48,7 @@ In this example, we'll define an accessor for the `first_name` attribute. The ac */ protected function firstName(): Attribute { - return new Attribute( + return Attribute::make( get: fn ($value) => ucfirst($value), ); } @@ -82,7 +82,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; */ public function address(): Attribute { - return new Attribute( + return Attribute::make( get: fn ($value, $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], @@ -112,12 +112,12 @@ If you would like to disable the object caching behavior of attributes, you may */ public function address(): Attribute { - return (new Attribute( + return Attribute::make( get: fn ($value, $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), - ))->withoutObjectCaching(); + )->withoutObjectCaching(); } ``` @@ -143,7 +143,7 @@ A mutator transforms an Eloquent attribute value when it is set. To define a mut */ protected function firstName(): Attribute { - return new Attribute( + return Attribute::make( get: fn ($value) => ucfirst($value), set: fn ($value) => strtolower($value), ); @@ -176,7 +176,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; */ public function address(): Attribute { - return new Attribute( + return Attribute::make( get: fn ($value, $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], From 8a66b35ac1cfba01bb09a75805b630e4b4d96d95 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 22 Feb 2022 09:54:31 -0600 Subject: [PATCH 0017/2609] document arr keyby --- helpers.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/helpers.md b/helpers.md index 2241cdde146..fcb6d17119e 100644 --- a/helpers.md +++ b/helpers.md @@ -43,6 +43,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::hasAny](#method-array-hasany) [Arr::isAssoc](#method-array-isassoc) [Arr::isList](#method-array-islist) +[Arr::keyBy](#method-array-keyby) [Arr::last](#method-array-last) [Arr::only](#method-array-only) [Arr::pluck](#method-array-pluck) @@ -599,6 +600,27 @@ The `Arr::isList` method returns `true` if the given array's keys are sequential // false + +#### `Arr::keyBy()` {.collection-method} + +The `Arr::keyBy` method keys the array by the given key. If multiple items have the same key, only the last one will appear in the new array: + + use Illuminate\Support\Arr; + + $array = [ + ['product_id' => 'prod-100', 'name' => 'Desk'], + ['product_id' => 'prod-200', 'name' => 'Chair'], + ]; + + $keyed = Arr::keyBy($array, 'product_id'); + + /* + [ + 'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'], + 'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'], + ] + */ + #### `Arr::last()` {.collection-method} From d981eabb91cba35db859e6fb5d6f024864f587ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEuris?= Date: Wed, 23 Feb 2022 11:07:27 +0200 Subject: [PATCH 0018/2609] Fix typo in facade import --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index 72c2641f278..fe8735814f7 100644 --- a/authorization.md +++ b/authorization.md @@ -226,7 +226,7 @@ Similar to the `before` method, if the `after` closure returns a non-null result Occasionally, you may wish to determine if the currently authenticated user is authorized to perform a given action without writing a dedicate gate that corresponds to the action. Laravel allows you to perform these types of "inline" authorization checks via the `Gate::allowIf` and `Gate::denyIf` methods: ```php -use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Gate; Gate::allowIf(fn ($user) => $user->isAdministrator()); From fdbdff7d9bb5521e69d66d0e653187a1379af37e Mon Sep 17 00:00:00 2001 From: Alexander Shchukin Date: Wed, 23 Feb 2022 18:03:51 +0300 Subject: [PATCH 0019/2609] Update filesystem.md (#7742) after using flysystem 3.0, Util class no longer available, using WhitespacePathNormalizer instead --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index a4b724747aa..7b32707c38f 100644 --- a/filesystem.md +++ b/filesystem.md @@ -396,7 +396,7 @@ You may also use the `putFileAs` method on the `Storage` facade, which will perf 'avatars', $request->file('avatar'), $request->user()->id ); -> {note} Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\Util::normalizePath` method. +> {note} Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. #### Specifying A Disk From 0b478c24497d15f2f1196ee53902e1e19954d85e Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 24 Feb 2022 15:50:37 +0100 Subject: [PATCH 0020/2609] Update valet.md (#7745) --- valet.md | 1 - 1 file changed, 1 deletion(-) diff --git a/valet.md b/valet.md index 603fb775526..fea25e5f557 100644 --- a/valet.md +++ b/valet.md @@ -38,7 +38,6 @@ Out of the box, Valet support includes, but is not limited to:
- [Laravel](https://laravel.com) -- [Lumen](https://lumen.laravel.com) - [Bedrock](https://roots.io/bedrock/) - [CakePHP 3](https://cakephp.org) - [Concrete5](https://www.concrete5.org/) From 66fc433c3c91713ded8e36de2bd60ab99bb61fcc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 24 Feb 2022 09:44:26 -0600 Subject: [PATCH 0021/2609] add batch option --- migrations.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/migrations.md b/migrations.md index 3154a90fe6d..b52fd4d02a4 100644 --- a/migrations.md +++ b/migrations.md @@ -179,6 +179,12 @@ You may roll back a limited number of migrations by providing the `step` option php artisan migrate:rollback --step=5 ``` +You may roll back a specific "batch" of migrations by providing the `batch` option to the `rollback` command, where the `batch` option corresponds to a batch value within your application's `migrations` database table. For example, the following command will roll back all migrations in batch three: + + ```shell + php artisan migrate:rollback --batch=3 + ``` + The `migrate:reset` command will roll back all of your application's migrations: ```shell From e4bd19adf26ef826ad3b1241d2d02d3ec72e40a7 Mon Sep 17 00:00:00 2001 From: Nico Orfanos Date: Fri, 25 Feb 2022 15:33:57 +0100 Subject: [PATCH 0022/2609] Update octane.md --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index 24069979ec8..e660c61182c 100644 --- a/octane.md +++ b/octane.md @@ -521,7 +521,7 @@ use Illuminate\Support\Str; Cache::store('octane')->interval('random', function () { return Str::random(10); -}, seconds: 5) +}, seconds: 5); ``` From cf6dfa3b0696c2e1797a996dd757880b2289e8d5 Mon Sep 17 00:00:00 2001 From: Daniel Polito Date: Fri, 25 Feb 2022 16:57:38 -0300 Subject: [PATCH 0023/2609] Adding note about upsert to Eloquent page (#7746) --- eloquent.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eloquent.md b/eloquent.md index b378ad1caa9..aad88bd1d9d 100644 --- a/eloquent.md +++ b/eloquent.md @@ -761,6 +761,8 @@ If you would like to perform multiple "upserts" in a single query, then you shou ['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. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. ## Deleting Models From 3c8f983bd493ec66b1e543da11e24aeceec038b0 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Fri, 25 Feb 2022 21:01:26 +0100 Subject: [PATCH 0024/2609] [9.x] Clarify allowed characters for site name. (#7749) * Update installation.md * formatting Co-authored-by: Taylor Otwell --- installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/installation.md b/installation.md index d6b81486d42..1c1f9a1ee7b 100644 --- a/installation.md +++ b/installation.md @@ -65,7 +65,7 @@ If you're developing on a Mac and [Docker Desktop](https://www.docker.com/produc curl -s "https://laravel.build/example-app" | bash ``` -Of course, you can change "example-app" in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. +Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: @@ -94,7 +94,7 @@ Next, you are ready to create your first Laravel project. Launch [Windows Termin curl -s https://laravel.build/example-app | bash ``` -Of course, you can change "example-app" in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. +Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: @@ -125,7 +125,7 @@ If you're developing on Linux and [Docker Compose](https://docs.docker.com/compo curl -s https://laravel.build/example-app | bash ``` -Of course, you can change "example-app" in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. +Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: From 410a8bf5f79c4ec57421b192477f43cd4b4ef904 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 28 Feb 2022 17:19:38 +0100 Subject: [PATCH 0025/2609] [9.x] Specify exception throwing for Filesystem in upgrade guide (#7755) * Update upgrade.md * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 9ded856ed74..fe626211161 100644 --- a/upgrade.md +++ b/upgrade.md @@ -320,7 +320,11 @@ Before using the S3, FTP, or SFTP drivers, you will need to install the appropri #### Overwriting Existing Files -Write operations such as `put`, `write`, `writeStream` now overwrite existing files by default. If you do not want to overwrite existing files, you should manually check for the file's existence before performing the write operation. +Write operations such as `put`, `write`, and `writeStream` now overwrite existing files by default. If you do not want to overwrite existing files, you should manually check for the file's existence before performing the write operation. + +#### Write Exceptions + +Write operations such as `put`, `write`, and `writeStream` no longer throw an exception when a write operation fails. Instead, `false` is returned. #### Reading Missing Files From 56d4248f8720f9f7abe345f64959873e53c76c4c Mon Sep 17 00:00:00 2001 From: Markus Machatschek Date: Mon, 28 Feb 2022 17:38:10 +0100 Subject: [PATCH 0026/2609] Update scout.md (#7747) * Update scout.md * Add link to resource * Update scout.md * Update scout.md Co-authored-by: Taylor Otwell --- scout.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scout.md b/scout.md index 578da6ad02e..63b1a6eea11 100644 --- a/scout.md +++ b/scout.md @@ -109,6 +109,8 @@ Once you have configured a queue driver, set the value of the `queue` option in 'queue' => true, +Even when the `queue` option is set to `false`, it's important to remember that some Scout drivers like Algolia and Meilisearch always index records asynchronously. Meaning, even though the index operation has completed within your Laravel application, the search engine itself may not reflect the new and updated records immediately. + ## Configuration From 55af362bdac8d4251c63d8213ee381025ff0e850 Mon Sep 17 00:00:00 2001 From: Zohaib Hassan Date: Tue, 1 Mar 2022 19:16:48 +0500 Subject: [PATCH 0027/2609] Added missing Schema Events (#7756) * Added missing Schema Events Add missing Schema Events of SchemaDumped and SchemaLoaded * Update migrations.md Co-authored-by: Taylor Otwell --- migrations.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migrations.md b/migrations.md index 3154a90fe6d..cba8ddca6f2 100644 --- a/migrations.md +++ b/migrations.md @@ -1203,4 +1203,6 @@ For convenience, each migration operation will dispatch an [event](/docs/{{versi | `Illuminate\Database\Events\MigrationsEnded` | A batch of migrations has finished executing. | | `Illuminate\Database\Events\MigrationStarted` | A single migration is about to be executed. | | `Illuminate\Database\Events\MigrationEnded` | A single migration has finished executing. | +| `Illuminate\Database\Events\SchemaDumped` | A database schema dump has completed. | +| `Illuminate\Database\Events\SchemaLoaded` | An existing database schema dump has loaded. | From 9a214df5cae33283d0a3546911d43fc0d3db8732 Mon Sep 17 00:00:00 2001 From: HPWebdeveloper <16323354+HPWebdeveloper@users.noreply.github.com> Date: Wed, 2 Mar 2022 11:20:48 -0300 Subject: [PATCH 0028/2609] [9.x] Add FortifyServiceProvider to the list of published Fortify's resources (#7759) * update fortify.md * Update fortify.md Co-authored-by: Taylor Otwell --- fortify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortify.md b/fortify.md index 50d9cfdb5b2..82e6ca28a40 100644 --- a/fortify.md +++ b/fortify.md @@ -80,7 +80,7 @@ Next, publish Fortify's resources using the `vendor:publish` command: php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider" ``` -This command will publish Fortify's actions to your `app/Actions` directory, which will be created if it does not exist. In addition, Fortify's configuration file and migrations will be published. +This command will publish Fortify's actions to your `app/Actions` directory, which will be created if it does not exist. In addition, the `FortifyServiceProvider`, configuration file, and all necessary database migrations will be published. Next, you should migrate your database: From 9e59796a8b1b6b475d4215be988d2504520ef472 Mon Sep 17 00:00:00 2001 From: Gabriel Akinyosoye Date: Wed, 2 Mar 2022 15:21:11 +0100 Subject: [PATCH 0029/2609] Update pagination.md (#7758) Add method to indicate if user is on the last page. --- pagination.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pagination.md b/pagination.md index e68e3d5a5ce..135c642c1d5 100644 --- a/pagination.md +++ b/pagination.md @@ -372,6 +372,7 @@ Method | Description `$paginator->nextCursor()` | Get the cursor instance for the next set of items. `$paginator->nextPageUrl()` | Get the URL for the next page. `$paginator->onFirstPage()` | Determine if the paginator is on the first page. +`$paginator->onLastPage()` | Determine if the paginator is on the last page. `$paginator->perPage()` | The number of items to be shown per page. `$paginator->previousCursor()` | Get the cursor instance for the previous set of items. `$paginator->previousPageUrl()` | Get the URL for the previous page. From a7d1b92b58c27981f3e309748f4051bdd4b69622 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 2 Mar 2022 18:12:37 -0600 Subject: [PATCH 0030/2609] document route list --- routing.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/routing.md b/routing.md index a155d937a30..f41353a4098 100644 --- a/routing.md +++ b/routing.md @@ -3,6 +3,7 @@ - [Basic Routing](#basic-routing) - [Redirect Routes](#redirect-routes) - [View Routes](#view-routes) + - [The Route List](#the-route-list) - [Route Parameters](#route-parameters) - [Required Parameters](#required-parameters) - [Optional Parameters](#parameters-optional-parameters) @@ -124,6 +125,27 @@ If your route only needs to return a [view](/docs/{{version}}/views), you may us > {note} When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. + +### The Route List + +The `route:list` Artisan command can easily provide an overview of all of the routes that are defined by your application: + +```shell +php artisan route:list +``` + +By default, the route middleware that are assigned to each route will not be displayed in the `route:list` output; however, you can instruct Laravel to display the route middleware by adding the `-v` option to the command: + +```shell +php artisan route:list -v +``` + +In addition, you may instruct Laravel to hide any routes that are defined by third-party packages by providing the `--except-vendor` option when executing the `route:list` command: + +```shell +php artisan route:list --except-vendor +``` + ## Route Parameters From 4b639895e4a56fc15f282334824dbd1b3f350c16 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 2 Mar 2022 18:15:33 -0600 Subject: [PATCH 0031/2609] document whereNot --- queries.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/queries.md b/queries.md index 3ff1407c9d4..79a24ea7457 100644 --- a/queries.md +++ b/queries.md @@ -12,6 +12,7 @@ - [Basic Where Clauses](#basic-where-clauses) - [Where Clauses](#where-clauses) - [Or Where Clauses](#or-where-clauses) + - [Where Not Clauses](#where-not-clauses) - [JSON Where Clauses](#json-where-clauses) - [Additional Where Clauses](#additional-where-clauses) - [Logical Grouping](#logical-grouping) @@ -462,6 +463,18 @@ 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. + +### Where Not Clauses + +The `whereNot` and `orWhereNot` methods may be used to negate a given group of query constraints. For example, the following query excludes products that are on clearance or which have a price that is less than ten: + + $products = DB::table('products') + ->whereNot(function ($query) { + $query->where('clearance', true) + ->orWhere('price', '<', 10); + }) + ->get(); + ### JSON Where Clauses From 3da0c10fb400e8d4cb9de1e517c404644c4d8ad0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 2 Mar 2022 18:24:21 -0600 Subject: [PATCH 0032/2609] position and index placeholders --- validation.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/validation.md b/validation.md index afb9e30ae67..5a303c21b57 100644 --- a/validation.md +++ b/validation.md @@ -27,6 +27,7 @@ - [Conditionally Adding Rules](#conditionally-adding-rules) - [Validating Arrays](#validating-arrays) - [Validating Nested Array Input](#validating-nested-array-input) + - [Error Message Indexes & Positions](#error-message-indexes-and-positions) - [Validating Passwords](#validating-passwords) - [Custom Validation Rules](#custom-validation-rules) - [Using Rule Objects](#using-rule-objects) @@ -1684,6 +1685,34 @@ Sometimes you may need to access the value for a given nested array element when }), ]); + +### Error Message Indexes & Positions + +When validating arrays, you may want to reference the index or position of a particular item that failed validation within the error message displayed by your application. To accomplish this, you may include the `:index` and `:position` place-holders within your [custom validation message](#manual-customizing-the-error-messages): + + use Illuminate\Support\Facades\Validator; + + $input = [ + 'photos' => [ + [ + 'name' => 'BeachVacation.jpg', + 'description' => 'A photo of my beach vacation!', + ], + [ + 'name' => 'GrandCanyon.jpg', + 'description' => '', + ], + ], + ]; + + Validator::validate($input, [ + 'photos.*.description' => 'required', + ], [ + 'photos.*.description.required' => 'Please describe photo #:position.', + ]); + +Given the example above, validation will fail and the user will be presented with the following error of _"Please describe photo #2."_ + ## Validating Passwords From 30c96d98e94d6d4404c72436a7b4bd7419340cb2 Mon Sep 17 00:00:00 2001 From: Dean Morgan Date: Thu, 3 Mar 2022 14:06:55 +0000 Subject: [PATCH 0033/2609] Remove invalid semi-colon from code snippet (#7762) * Remove invalid semi-colon from code snippet * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/upgrade.md b/upgrade.md index fe626211161..be3fe153aa4 100644 --- a/upgrade.md +++ b/upgrade.md @@ -369,9 +369,9 @@ use Spatie\Dropbox\Client as DropboxClient; use Spatie\FlysystemDropbox\DropboxAdapter; Storage::extend('dropbox', function ($app, $config) { - $adapter = new DropboxAdapter(new DropboxClient( - $config['authorization_token'] - );); + $adapter = new DropboxAdapter( + new DropboxClient($config['authorization_token']) + ); return new FilesystemAdapter( new Filesystem($adapter, $config), From 5e1d87908acf1d417166df30f05cb5450b34fa6a Mon Sep 17 00:00:00 2001 From: Jared Dunham Date: Thu, 3 Mar 2022 12:31:45 -0600 Subject: [PATCH 0034/2609] [9.x] Added new env options for v3 sftp adaptor (#7757) * Added new options for v3 sftp adaptor * Update filesystem.md Co-authored-by: Taylor Otwell --- filesystem.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filesystem.md b/filesystem.md index 7b32707c38f..386f1f0fd43 100644 --- a/filesystem.md +++ b/filesystem.md @@ -129,9 +129,13 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu 'password' => env('SFTP_PASSWORD'), // 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, ], From c000773147ca0f3a48327bfa56e6d884344f6ba5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 3 Mar 2022 12:35:02 -0600 Subject: [PATCH 0035/2609] remove constructor --- artisan.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/artisan.md b/artisan.md index 2dcb9afac22..768c94ec296 100644 --- a/artisan.md +++ b/artisan.md @@ -142,16 +142,6 @@ Let's take a look at an example command. Note that we are able to request any de */ protected $description = 'Send a marketing email to a user'; - /** - * Create a new command instance. - * - * @return void - */ - public function __construct() - { - parent::__construct(); - } - /** * Execute the console command. * From 1dd27514af844efe2f5fd15f410ec90fd9a76322 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 3 Mar 2022 18:38:13 +0000 Subject: [PATCH 0036/2609] add `char` to modifiable column types (#7764) --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index cba8ddca6f2..680c664f36d 100644 --- a/migrations.md +++ b/migrations.md @@ -991,7 +991,7 @@ We could also modify a column to be nullable: $table->string('name', 50)->nullable()->change(); }); -> {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`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). +> {note} The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). #### Renaming Columns From f166c4fd2c87c594c9dd44841f1f0602be6dabb6 Mon Sep 17 00:00:00 2001 From: Camilo Payan Date: Fri, 4 Mar 2022 09:30:55 -0500 Subject: [PATCH 0037/2609] Add warning about the possibility of losing encrypted data (#7765) * Add warning about the possibility of losing encrypted data If a user isn't aware that the `APP_KEY` isn't being used, they may regenerate an `APP_KEY` using the key generator command. * Update eloquent-mutators.md * Update eloquent-mutators.md * Update eloquent-mutators.md Co-authored-by: Taylor Otwell --- eloquent-mutators.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 1cee3b59396..bc43510602a 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -440,6 +440,11 @@ The `encrypted` cast will encrypt a model's attribute value using Laravel's buil As the final length of the encrypted text is not predictable and is longer than its plain text counterpart, make sure the associated database column is of `TEXT` type or larger. In addition, since the values are encrypted in the database, you will not be able to query or search encrypted attribute values. + +#### Key Rotation + +As you may know, Laravel encrypts strings using the `key` configuration value specified in your application's `app` configuration file. Typically, this value corresponds to the value of the `APP_KEY` environment variable. If you need to rotate your application's encryption key, you will need to manually re-encrypt your encrypted attributes using the new key. + ### Query Time Casting From 9499be637a9dfa6a8d9bf576e317f076337a9e4c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 4 Mar 2022 12:22:52 -0600 Subject: [PATCH 0038/2609] wip --- middleware.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware.md b/middleware.md index 28c7f128e09..d6cd5fcc1b8 100644 --- a/middleware.md +++ b/middleware.md @@ -237,7 +237,7 @@ Rarely, you may need your middleware to execute in a specific order but not have \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, - \Illuminate\Session\Middleware\AuthenticateSession::class, + \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class, ]; From 875cd1a69c1036a10e504943389ed1fea9ea7711 Mon Sep 17 00:00:00 2001 From: Michael Barclay Date: Fri, 4 Mar 2022 15:04:36 -0500 Subject: [PATCH 0039/2609] Link to reference (#7768) Was confused what was a signed route in the testing doc so this is adding a reference to the signed route/url documentation for convenience. --- http-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index 1b3855f2368..df177d57422 100644 --- a/http-tests.md +++ b/http-tests.md @@ -954,7 +954,7 @@ Assert whether the response is redirecting to a URI that contains the given stri #### assertRedirectToSignedRoute -Assert that the response is a redirect to the given signed route: +Assert that the response is a redirect to the given [signed route](/docs/{{version}}/urls#signed-urls): $response->assertRedirectToSignedRoute($name = null, $parameters = []); From 4b06067d7e337832ef729e070849397dbf0e8091 Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 6 Mar 2022 17:25:43 +0100 Subject: [PATCH 0040/2609] Fix typo (#7772) Co-authored-by: Tom --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index ef8beae7d02..d68cff1c1e5 100644 --- a/blade.md +++ b/blade.md @@ -44,7 +44,7 @@ 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. -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: +Blade views may be returned from routes or controllers 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: Route::get('/', function () { return view('greeting', ['name' => 'Finn']); From 508c2882b5dc2c5ecc9f55a709dca10a5b300a1d Mon Sep 17 00:00:00 2001 From: Iman Date: Sun, 6 Mar 2022 19:57:04 +0330 Subject: [PATCH 0041/2609] use arrow functions in validation.md sample codes (#7771) --- validation.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/validation.md b/validation.md index 5a303c21b57..951146720ab 100644 --- a/validation.md +++ b/validation.md @@ -1388,9 +1388,7 @@ If you would like to construct a more complex condition for the `required_if` ru ]); Validator::make($request->all(), [ - 'role_id' => Rule::requiredIf(function () use ($request) { - return $request->user()->is_admin; - }), + 'role_id' => Rule::requiredIf(fn () => $request->user()->is_admin), ]); @@ -1515,9 +1513,7 @@ By default, the `unique` rule will check the uniqueness of the column matching t 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); - }) + 'email' => Rule::unique('users')->where(fn ($query) => $query->where('account_id', 1)) #### url From b6714a209e45410038114197f39e4c7424c78262 Mon Sep 17 00:00:00 2001 From: Mohamed Ilies <35309918+medilies@users.noreply.github.com> Date: Sun, 6 Mar 2022 17:29:12 +0100 Subject: [PATCH 0042/2609] Add Pagination Blade folder to tailwind3 content (#7770) --- mix.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mix.md b/mix.md index fc6c7b07ab3..33bea22bf20 100644 --- a/mix.md +++ b/mix.md @@ -118,6 +118,7 @@ content: [ './resources/**/*.blade.php', './resources/**/*.js', './resources/**/*.vue', + "./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php", ], ``` From 4e5faff917463217ae6b38a077b48f6025e94353 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 7 Mar 2022 16:00:28 +0100 Subject: [PATCH 0043/2609] [9.x] Clarify "which branch" further (#7778) * Update contributions.md * Update contributions.md * Update contributions.md Co-authored-by: Taylor Otwell --- contributions.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/contributions.md b/contributions.md index 2e6720e1342..2f918eb0dfd 100644 --- a/contributions.md +++ b/contributions.md @@ -74,13 +74,11 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest stable branch. Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `8.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. -**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch. +**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `9.x`). -**Major** new features should always be sent to the `master` branch, which contains the upcoming release. - -If you are unsure if your feature qualifies as a major or minor, please ask Taylor Otwell in the `#internals` channel of the [Laravel Discord server](https://discord.gg/laravel). +**Major** new features or features with breaking changes should always be sent to the `master` branch, which contains the upcoming release. ## Compiled Assets From acf3d3c4a5ab790cd5b02ef723892f66afb40442 Mon Sep 17 00:00:00 2001 From: "Chan, Danny" Date: Mon, 7 Mar 2022 23:01:11 +0800 Subject: [PATCH 0044/2609] Add miss line break between list tags (#7777) --- homestead.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homestead.md b/homestead.md index 25c43197487..6e5746ea814 100644 --- a/homestead.md +++ b/homestead.md @@ -56,6 +56,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M
+ - Ubuntu 20.04 - Git - PHP 8.1 @@ -82,6 +83,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Xdebug - XHProf / Tideways / XHGui - wp-cli +
@@ -96,6 +98,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M
+ - Apache - Blackfire - Cassandra @@ -125,6 +128,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - TimescaleDB - Trader (PHP extension) - Webdriver & Laravel Dusk Utilities +
From 69b6f7738f7392e9a588b28d23af899242998c2f Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Mon, 7 Mar 2022 09:01:39 -0600 Subject: [PATCH 0045/2609] [9.x] A few missing words in events.md (#7776) * Two missing words --- events.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/events.md b/events.md index 653d4424a82..148803986f6 100644 --- a/events.md +++ b/events.md @@ -444,7 +444,7 @@ If your queue connection's `after_commit` configuration option is set to `false` ### Handling Failed Jobs -Sometimes your queued event listeners may fail. If queued listener exceeds the maximum number of attempts as defined by your queue worker, the `failed` method will be called on your listener. The `failed` method receives the event instance and the `Throwable` that caused the failure: +Sometimes your queued event listeners may fail. If the queued listener exceeds the maximum number of attempts as defined by your queue worker, the `failed` method will be called on your listener. The `failed` method receives the event instance and the `Throwable` that caused the failure: Date: Mon, 7 Mar 2022 09:01:55 -0600 Subject: [PATCH 0046/2609] [9.x] Missing article "a" (#7775) * Missing word in sentence --- database-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database-testing.md b/database-testing.md index 1032b4180f9..b149c7b1888 100644 --- a/database-testing.md +++ b/database-testing.md @@ -93,7 +93,7 @@ To see an example of how to write a factory, take a look at the `database/factor } } -As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. +As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. Via the `faker` property, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing. From f2df3959ac9afbdc612543efbd3e233bf5c148d8 Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Mon, 7 Mar 2022 09:02:08 -0600 Subject: [PATCH 0047/2609] [9.x] Replace "to" with "the" (#7774) * Very small typo on this page --- testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing.md b/testing.md index 1293f246a27..837e0947f58 100644 --- a/testing.md +++ b/testing.md @@ -181,7 +181,7 @@ Using the `ParallelTesting` facade, you may specify code to be executed on the ` #### Accessing The Parallel Testing Token -If you would like to access to current parallel process "token" from any other location in your application's test code, you may use the `token` method. This token is a unique, string identifier for an individual test process and may be used to segment resources across parallel test processes. For example, Laravel automatically appends this token to the end of the test databases created by each parallel testing process: +If you would like to access the current parallel process "token" from any other location in your application's test code, you may use the `token` method. This token is a unique, string identifier for an individual test process and may be used to segment resources across parallel test processes. For example, Laravel automatically appends this token to the end of the test databases created by each parallel testing process: $token = ParallelTesting::token(); From 029b64a5ad0c6553f8da6e39e4de7a0f1ce1b79e Mon Sep 17 00:00:00 2001 From: Mubashir Rasool Razvi Date: Mon, 7 Mar 2022 20:03:05 +0500 Subject: [PATCH 0048/2609] [9.x] minor grammar/usage change: 'actions which are' --> 'actions that are' (#7773) --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index fe8735814f7..9124ed00bad 100644 --- a/authorization.md +++ b/authorization.md @@ -30,7 +30,7 @@ In addition to providing built-in [authentication](/docs/{{version}}/authenticat 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 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. +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 that 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 From 01f66a41271b83cd3360f4557c0862d8390d8b47 Mon Sep 17 00:00:00 2001 From: Vincent Prat Date: Mon, 7 Mar 2022 17:13:05 +0100 Subject: [PATCH 0049/2609] Document the lcfirst function (#7779) added in accepted PR: https://github.com/laravel/framework/pull/41384 --- helpers.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/helpers.md b/helpers.md index fcb6d17119e..17e74802a3a 100644 --- a/helpers.md +++ b/helpers.md @@ -111,6 +111,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::isAscii](#method-str-is-ascii) [Str::isUuid](#method-str-is-uuid) [Str::kebab](#method-kebab-case) +[Str::lcfirst](#method-str-lcfirst) [Str::length](#method-str-length) [Str::limit](#method-str-limit) [Str::lower](#method-str-lower) @@ -181,6 +182,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [isNotEmpty](#method-fluent-str-is-not-empty) [isUuid](#method-fluent-str-is-uuid) [kebab](#method-fluent-str-kebab) +[lcfirst](#method-fluent-str-lcfirst) [length](#method-fluent-str-length) [limit](#method-fluent-str-limit) [lower](#method-fluent-str-lower) @@ -1432,6 +1434,17 @@ The `Str::kebab` method converts the given string to `kebab-case`: $converted = Str::kebab('fooBar'); // foo-bar + + +#### `Str::lcfirst()` {.collection-method} + +The `Str::lcfirst` method returns the given string with the first character lowercased: + + use Illuminate\Support\Str; + + $string = Str::lcfirst('Foo Bar'); + + // foo Bar #### `Str::length()` {.collection-method} @@ -2283,6 +2296,18 @@ The `kebab` method converts the given string to `kebab-case`: $converted = Str::of('fooBar')->kebab(); // foo-bar + + +#### `lcfirst()` {.collection-method} + +The `lcfirst` method returns the given string with the first character lowercased: + + use Illuminate\Support\Str; + + $string = Str::of('Foo Bar')->lcfirst(); + + // foo Bar + #### `length` {.collection-method} From b4cfdecdbcba623941c44451de8c449d41ef98bf Mon Sep 17 00:00:00 2001 From: Mubashir Rasool Razvi Date: Tue, 8 Mar 2022 00:04:24 +0500 Subject: [PATCH 0050/2609] [9.x] minor grammar/usage change: 'dedicate' --> 'dedicated' (#7780) --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index 9124ed00bad..67c80938e67 100644 --- a/authorization.md +++ b/authorization.md @@ -223,7 +223,7 @@ Similar to the `before` method, if the `after` closure returns a non-null result ### Inline Authorization -Occasionally, you may wish to determine if the currently authenticated user is authorized to perform a given action without writing a dedicate gate that corresponds to the action. Laravel allows you to perform these types of "inline" authorization checks via the `Gate::allowIf` and `Gate::denyIf` methods: +Occasionally, you may wish to determine if the currently authenticated user is authorized to perform a given action without writing a dedicated gate that corresponds to the action. Laravel allows you to perform these types of "inline" authorization checks via the `Gate::allowIf` and `Gate::denyIf` methods: ```php use Illuminate\Support\Facades\Gate; From ff4b2e60aa35cf10abcff28090683e535c76f98c Mon Sep 17 00:00:00 2001 From: Mubashir Rasool Razvi Date: Tue, 8 Mar 2022 00:23:44 +0500 Subject: [PATCH 0051/2609] For compatibility reasons all table rows should have borders (pipe symbols) at the start and at the end (#7782) --- blade.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/blade.md b/blade.md index d68cff1c1e5..ff13a181d3c 100644 --- a/blade.md +++ b/blade.md @@ -399,18 +399,18 @@ If you are in a nested loop, you may access the parent loop's `$loop` variable v The `$loop` variable also contains a variety of other useful properties: -Property | Description -------------- | ------------- -`$loop->index` | The index of the current loop iteration (starts at 0). -`$loop->iteration` | The current loop iteration (starts at 1). -`$loop->remaining` | The iterations remaining in the loop. -`$loop->count` | The total number of items in the array being iterated. -`$loop->first` | Whether this is the first iteration through the loop. -`$loop->last` | Whether this is the last iteration through the loop. -`$loop->even` | Whether this is an even iteration through the loop. -`$loop->odd` | Whether this is an odd iteration through the loop. -`$loop->depth` | The nesting level of the current loop. -`$loop->parent` | When in a nested loop, the parent's loop variable. +| Property | Description | +|--------------------|--------------------------------------------------------| +| `$loop->index` | The index of the current loop iteration (starts at 0). | +| `$loop->iteration` | The current loop iteration (starts at 1). | +| `$loop->remaining` | The iterations remaining in the loop. | +| `$loop->count` | The total number of items in the array being iterated. | +| `$loop->first` | Whether this is the first iteration through the loop. | +| `$loop->last` | Whether this is the last iteration through the loop. | +| `$loop->even` | Whether this is an even iteration through the loop. | +| `$loop->odd` | Whether this is an odd iteration through the loop. | +| `$loop->depth` | The nesting level of the current loop. | +| `$loop->parent` | When in a nested loop, the parent's loop variable. | ### Conditional Classes From f296009ff1524077f5a9e6c92c64374ed0b8a8b7 Mon Sep 17 00:00:00 2001 From: Mubashir Rasool Razvi Date: Tue, 8 Mar 2022 00:28:51 +0500 Subject: [PATCH 0052/2609] For compatibility reasons all table rows should have borders (pipe symbols) at the start and at the end (#7783) --- configuration.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/configuration.md b/configuration.md index ba2622647cb..b44f1369a9a 100644 --- a/configuration.md +++ b/configuration.md @@ -45,16 +45,16 @@ Before loading your application's environment variables, Laravel determines if e All variables in your `.env` files are typically parsed as strings, so some reserved values have been created to allow you to return a wider range of types from the `env()` function: -`.env` Value | `env()` Value -------------- | ------------- -true | (bool) true -(true) | (bool) true -false | (bool) false -(false) | (bool) false -empty | (string) '' -(empty) | (string) '' -null | (null) null -(null) | (null) null +| `.env` Value | `env()` Value | +|--------------|---------------| +| true | (bool) true | +| (true) | (bool) true | +| false | (bool) false | +| (false) | (bool) false | +| empty | (string) '' | +| (empty) | (string) '' | +| null | (null) null | +| (null) | (null) null | If you need to define an environment variable with a value that contains spaces, you may do so by enclosing the value in double quotes: From 8f565bd1a69cf1d980cd1f7ff0164e80e8dc3ddd Mon Sep 17 00:00:00 2001 From: Mubashir Rasool Razvi Date: Tue, 8 Mar 2022 00:28:59 +0500 Subject: [PATCH 0053/2609] For compatibility reasons all table rows should have borders (pipe symbols) at the start and at the end (#7784) --- contracts.md | 162 +++++++++++++++++++++++++-------------------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/contracts.md b/contracts.md index 635c7eba54c..f589cd4730e 100644 --- a/contracts.md +++ b/contracts.md @@ -85,84 +85,84 @@ When the event listener is resolved, the service container will read the type-hi This table provides a quick reference to all of the Laravel contracts and their equivalent facades: -Contract | References Facade -------------- | ------------- -[Illuminate\Contracts\Auth\Access\Authorizable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Authorizable.php) |   -[Illuminate\Contracts\Auth\Access\Gate](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Gate.php) | `Gate` -[Illuminate\Contracts\Auth\Authenticatable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Authenticatable.php) |   -[Illuminate\Contracts\Auth\CanResetPassword](https://github.com/illuminate/contracts/blob/{{version}}/Auth/CanResetPassword.php) |   -[Illuminate\Contracts\Auth\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Factory.php) | `Auth` -[Illuminate\Contracts\Auth\Guard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Guard.php) | `Auth::guard()` -[Illuminate\Contracts\Auth\PasswordBroker](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBroker.php) | `Password::broker()` -[Illuminate\Contracts\Auth\PasswordBrokerFactory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBrokerFactory.php) | `Password` -[Illuminate\Contracts\Auth\StatefulGuard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/StatefulGuard.php) |   -[Illuminate\Contracts\Auth\SupportsBasicAuth](https://github.com/illuminate/contracts/blob/{{version}}/Auth/SupportsBasicAuth.php) |   -[Illuminate\Contracts\Auth\UserProvider](https://github.com/illuminate/contracts/blob/{{version}}/Auth/UserProvider.php) |   -[Illuminate\Contracts\Bus\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/Dispatcher.php) | `Bus` -[Illuminate\Contracts\Bus\QueueingDispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/QueueingDispatcher.php) | `Bus::dispatchToQueue()` -[Illuminate\Contracts\Broadcasting\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Factory.php) | `Broadcast` -[Illuminate\Contracts\Broadcasting\Broadcaster](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Broadcaster.php) | `Broadcast::connection()` -[Illuminate\Contracts\Broadcasting\ShouldBroadcast](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcast.php) |   -[Illuminate\Contracts\Broadcasting\ShouldBroadcastNow](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcastNow.php) |   -[Illuminate\Contracts\Cache\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Factory.php) | `Cache` -[Illuminate\Contracts\Cache\Lock](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Lock.php) |   -[Illuminate\Contracts\Cache\LockProvider](https://github.com/illuminate/contracts/blob/{{version}}/Cache/LockProvider.php) |   -[Illuminate\Contracts\Cache\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Repository.php) | `Cache::driver()` -[Illuminate\Contracts\Cache\Store](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Store.php) |   -[Illuminate\Contracts\Config\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Config/Repository.php) | `Config` -[Illuminate\Contracts\Console\Application](https://github.com/illuminate/contracts/blob/{{version}}/Console/Application.php) |   -[Illuminate\Contracts\Console\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Console/Kernel.php) | `Artisan` -[Illuminate\Contracts\Container\Container](https://github.com/illuminate/contracts/blob/{{version}}/Container/Container.php) | `App` -[Illuminate\Contracts\Cookie\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/Factory.php) | `Cookie` -[Illuminate\Contracts\Cookie\QueueingFactory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/QueueingFactory.php) | `Cookie::queue()` -[Illuminate\Contracts\Database\ModelIdentifier](https://github.com/illuminate/contracts/blob/{{version}}/Database/ModelIdentifier.php) |   -[Illuminate\Contracts\Debug\ExceptionHandler](https://github.com/illuminate/contracts/blob/{{version}}/Debug/ExceptionHandler.php) |   -[Illuminate\Contracts\Encryption\Encrypter](https://github.com/illuminate/contracts/blob/{{version}}/Encryption/Encrypter.php) | `Crypt` -[Illuminate\Contracts\Events\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Events/Dispatcher.php) | `Event` -[Illuminate\Contracts\Filesystem\Cloud](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Cloud.php) | `Storage::cloud()` -[Illuminate\Contracts\Filesystem\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Factory.php) | `Storage` -[Illuminate\Contracts\Filesystem\Filesystem](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Filesystem.php) | `Storage::disk()` -[Illuminate\Contracts\Foundation\Application](https://github.com/illuminate/contracts/blob/{{version}}/Foundation/Application.php) | `App` -[Illuminate\Contracts\Hashing\Hasher](https://github.com/illuminate/contracts/blob/{{version}}/Hashing/Hasher.php) | `Hash` -[Illuminate\Contracts\Http\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Http/Kernel.php) |   -[Illuminate\Contracts\Mail\MailQueue](https://github.com/illuminate/contracts/blob/{{version}}/Mail/MailQueue.php) | `Mail::queue()` -[Illuminate\Contracts\Mail\Mailable](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailable.php) |   -[Illuminate\Contracts\Mail\Mailer](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailer.php) | `Mail` -[Illuminate\Contracts\Notifications\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Dispatcher.php) | `Notification` -[Illuminate\Contracts\Notifications\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Factory.php) | `Notification` -[Illuminate\Contracts\Pagination\LengthAwarePaginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/LengthAwarePaginator.php) |   -[Illuminate\Contracts\Pagination\Paginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/Paginator.php) |   -[Illuminate\Contracts\Pipeline\Hub](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Hub.php) |   -[Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) |   -[Illuminate\Contracts\Queue\EntityResolver](https://github.com/illuminate/contracts/blob/{{version}}/Queue/EntityResolver.php) |   -[Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Factory.php) | `Queue` -[Illuminate\Contracts\Queue\Job](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Job.php) |   -[Illuminate\Contracts\Queue\Monitor](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Monitor.php) | `Queue` -[Illuminate\Contracts\Queue\Queue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Queue.php) | `Queue::connection()` -[Illuminate\Contracts\Queue\QueueableCollection](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableCollection.php) |   -[Illuminate\Contracts\Queue\QueueableEntity](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableEntity.php) |   -[Illuminate\Contracts\Queue\ShouldQueue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/ShouldQueue.php) |   -[Illuminate\Contracts\Redis\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Redis/Factory.php) | `Redis` -[Illuminate\Contracts\Routing\BindingRegistrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/BindingRegistrar.php) | `Route` -[Illuminate\Contracts\Routing\Registrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/Registrar.php) | `Route` -[Illuminate\Contracts\Routing\ResponseFactory](https://github.com/illuminate/contracts/blob/{{version}}/Routing/ResponseFactory.php) | `Response` -[Illuminate\Contracts\Routing\UrlGenerator](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlGenerator.php) | `URL` -[Illuminate\Contracts\Routing\UrlRoutable](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlRoutable.php) |   -[Illuminate\Contracts\Session\Session](https://github.com/illuminate/contracts/blob/{{version}}/Session/Session.php) | `Session::driver()` -[Illuminate\Contracts\Support\Arrayable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Arrayable.php) |   -[Illuminate\Contracts\Support\Htmlable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Htmlable.php) |   -[Illuminate\Contracts\Support\Jsonable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Jsonable.php) |   -[Illuminate\Contracts\Support\MessageBag](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageBag.php) |   -[Illuminate\Contracts\Support\MessageProvider](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageProvider.php) |   -[Illuminate\Contracts\Support\Renderable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Renderable.php) |   -[Illuminate\Contracts\Support\Responsable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Responsable.php) |   -[Illuminate\Contracts\Translation\Loader](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Loader.php) |   -[Illuminate\Contracts\Translation\Translator](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Translator.php) | `Lang` -[Illuminate\Contracts\Validation\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Factory.php) | `Validator` -[Illuminate\Contracts\Validation\ImplicitRule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ImplicitRule.php) |   -[Illuminate\Contracts\Validation\Rule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Rule.php) |   -[Illuminate\Contracts\Validation\ValidatesWhenResolved](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidatesWhenResolved.php) |   -[Illuminate\Contracts\Validation\Validator](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Validator.php) | `Validator::make()` -[Illuminate\Contracts\View\Engine](https://github.com/illuminate/contracts/blob/{{version}}/View/Engine.php) |   -[Illuminate\Contracts\View\Factory](https://github.com/illuminate/contracts/blob/{{version}}/View/Factory.php) | `View` -[Illuminate\Contracts\View\View](https://github.com/illuminate/contracts/blob/{{version}}/View/View.php) | `View::make()` +| Contract | References Facade | +|--------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------| +| [Illuminate\Contracts\Auth\Access\Authorizable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Authorizable.php) |    | +| [Illuminate\Contracts\Auth\Access\Gate](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Gate.php) | `Gate` | +| [Illuminate\Contracts\Auth\Authenticatable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Authenticatable.php) |    | +| [Illuminate\Contracts\Auth\CanResetPassword](https://github.com/illuminate/contracts/blob/{{version}}/Auth/CanResetPassword.php) |   | +| [Illuminate\Contracts\Auth\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Factory.php) | `Auth` | +| [Illuminate\Contracts\Auth\Guard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Guard.php) | `Auth::guard()` | +| [Illuminate\Contracts\Auth\PasswordBroker](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBroker.php) | `Password::broker()` | +| [Illuminate\Contracts\Auth\PasswordBrokerFactory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBrokerFactory.php) | `Password` | +| [Illuminate\Contracts\Auth\StatefulGuard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/StatefulGuard.php) |   | +| [Illuminate\Contracts\Auth\SupportsBasicAuth](https://github.com/illuminate/contracts/blob/{{version}}/Auth/SupportsBasicAuth.php) |   | +| [Illuminate\Contracts\Auth\UserProvider](https://github.com/illuminate/contracts/blob/{{version}}/Auth/UserProvider.php) |   | +| [Illuminate\Contracts\Bus\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/Dispatcher.php) | `Bus` | +| [Illuminate\Contracts\Bus\QueueingDispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/QueueingDispatcher.php) | `Bus::dispatchToQueue()` | +| [Illuminate\Contracts\Broadcasting\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Factory.php) | `Broadcast` | +| [Illuminate\Contracts\Broadcasting\Broadcaster](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Broadcaster.php) | `Broadcast::connection()` | +| [Illuminate\Contracts\Broadcasting\ShouldBroadcast](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcast.php) |   | +| [Illuminate\Contracts\Broadcasting\ShouldBroadcastNow](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcastNow.php) |   | +| [Illuminate\Contracts\Cache\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Factory.php) | `Cache` | +| [Illuminate\Contracts\Cache\Lock](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Lock.php) |   | +| [Illuminate\Contracts\Cache\LockProvider](https://github.com/illuminate/contracts/blob/{{version}}/Cache/LockProvider.php) |   | +| [Illuminate\Contracts\Cache\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Repository.php) | `Cache::driver()` | +| [Illuminate\Contracts\Cache\Store](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Store.php) |   | +| [Illuminate\Contracts\Config\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Config/Repository.php) | `Config` | +| [Illuminate\Contracts\Console\Application](https://github.com/illuminate/contracts/blob/{{version}}/Console/Application.php) |   | +| [Illuminate\Contracts\Console\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Console/Kernel.php) | `Artisan` | +| [Illuminate\Contracts\Container\Container](https://github.com/illuminate/contracts/blob/{{version}}/Container/Container.php) | `App` | +| [Illuminate\Contracts\Cookie\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/Factory.php) | `Cookie` | +| [Illuminate\Contracts\Cookie\QueueingFactory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/QueueingFactory.php) | `Cookie::queue()` | +| [Illuminate\Contracts\Database\ModelIdentifier](https://github.com/illuminate/contracts/blob/{{version}}/Database/ModelIdentifier.php) |   | +| [Illuminate\Contracts\Debug\ExceptionHandler](https://github.com/illuminate/contracts/blob/{{version}}/Debug/ExceptionHandler.php) |   | +| [Illuminate\Contracts\Encryption\Encrypter](https://github.com/illuminate/contracts/blob/{{version}}/Encryption/Encrypter.php) | `Crypt` | +| [Illuminate\Contracts\Events\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Events/Dispatcher.php) | `Event` | +| [Illuminate\Contracts\Filesystem\Cloud](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Cloud.php) | `Storage::cloud()` | +| [Illuminate\Contracts\Filesystem\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Factory.php) | `Storage` | +| [Illuminate\Contracts\Filesystem\Filesystem](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Filesystem.php) | `Storage::disk()` | +| [Illuminate\Contracts\Foundation\Application](https://github.com/illuminate/contracts/blob/{{version}}/Foundation/Application.php) | `App` | +| [Illuminate\Contracts\Hashing\Hasher](https://github.com/illuminate/contracts/blob/{{version}}/Hashing/Hasher.php) | `Hash` | +| [Illuminate\Contracts\Http\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Http/Kernel.php) |   | +| [Illuminate\Contracts\Mail\MailQueue](https://github.com/illuminate/contracts/blob/{{version}}/Mail/MailQueue.php) | `Mail::queue()` | +| [Illuminate\Contracts\Mail\Mailable](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailable.php) |   | +| [Illuminate\Contracts\Mail\Mailer](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailer.php) | `Mail` | +| [Illuminate\Contracts\Notifications\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Dispatcher.php) | `Notification` | +| [Illuminate\Contracts\Notifications\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Factory.php) | `Notification` | +| [Illuminate\Contracts\Pagination\LengthAwarePaginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/LengthAwarePaginator.php) |   | +| [Illuminate\Contracts\Pagination\Paginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/Paginator.php) |   | +| [Illuminate\Contracts\Pipeline\Hub](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Hub.php) |   | +| [Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) |   | +| [Illuminate\Contracts\Queue\EntityResolver](https://github.com/illuminate/contracts/blob/{{version}}/Queue/EntityResolver.php) |   | +| [Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Factory.php) | `Queue` | +| [Illuminate\Contracts\Queue\Job](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Job.php) |   | +| [Illuminate\Contracts\Queue\Monitor](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Monitor.php) | `Queue` | +| [Illuminate\Contracts\Queue\Queue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Queue.php) | `Queue::connection()` | +| [Illuminate\Contracts\Queue\QueueableCollection](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableCollection.php) |   | +| [Illuminate\Contracts\Queue\QueueableEntity](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableEntity.php) |   | +| [Illuminate\Contracts\Queue\ShouldQueue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/ShouldQueue.php) |   | +| [Illuminate\Contracts\Redis\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Redis/Factory.php) | `Redis` | +| [Illuminate\Contracts\Routing\BindingRegistrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/BindingRegistrar.php) | `Route` | +| [Illuminate\Contracts\Routing\Registrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/Registrar.php) | `Route` | +| [Illuminate\Contracts\Routing\ResponseFactory](https://github.com/illuminate/contracts/blob/{{version}}/Routing/ResponseFactory.php) | `Response` | +| [Illuminate\Contracts\Routing\UrlGenerator](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlGenerator.php) | `URL` | +| [Illuminate\Contracts\Routing\UrlRoutable](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlRoutable.php) |   | +| [Illuminate\Contracts\Session\Session](https://github.com/illuminate/contracts/blob/{{version}}/Session/Session.php) | `Session::driver()` | +| [Illuminate\Contracts\Support\Arrayable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Arrayable.php) |   | +| [Illuminate\Contracts\Support\Htmlable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Htmlable.php) |   | +| [Illuminate\Contracts\Support\Jsonable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Jsonable.php) |   | +| [Illuminate\Contracts\Support\MessageBag](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageBag.php) |   | +| [Illuminate\Contracts\Support\MessageProvider](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageProvider.php) |   | +| [Illuminate\Contracts\Support\Renderable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Renderable.php) |   | +| [Illuminate\Contracts\Support\Responsable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Responsable.php) |   | +| [Illuminate\Contracts\Translation\Loader](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Loader.php) |   | +| [Illuminate\Contracts\Translation\Translator](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Translator.php) | `Lang` | +| [Illuminate\Contracts\Validation\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Factory.php) | `Validator` | +| [Illuminate\Contracts\Validation\ImplicitRule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ImplicitRule.php) |   | +| [Illuminate\Contracts\Validation\Rule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Rule.php) |   | +| [Illuminate\Contracts\Validation\ValidatesWhenResolved](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidatesWhenResolved.php) |   | +| [Illuminate\Contracts\Validation\Validator](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Validator.php) | `Validator::make()` | +| [Illuminate\Contracts\View\Engine](https://github.com/illuminate/contracts/blob/{{version}}/View/Engine.php) |   | +| [Illuminate\Contracts\View\Factory](https://github.com/illuminate/contracts/blob/{{version}}/View/Factory.php) | `View` | +| [Illuminate\Contracts\View\View](https://github.com/illuminate/contracts/blob/{{version}}/View/View.php) | `View::make()` | From 6e32fdd0a57bde7994efc472393af908b2225715 Mon Sep 17 00:00:00 2001 From: Mubashir Rasool Razvi Date: Tue, 8 Mar 2022 00:29:12 +0500 Subject: [PATCH 0054/2609] must be same as title in overview (#7781) --- billing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/billing.md b/billing.md index 02142de24ee..b4bc82f7fcc 100644 --- a/billing.md +++ b/billing.md @@ -1571,7 +1571,7 @@ Similary, if the customer has multiple subscriptions, you can also retrieve the $invoice = $user->subscription('default')->upcomingInvoice(); -### Previewing Subscription Invoice +### Previewing Subscription Invoices Using the `previewInvoice` method, you can preview an invoice before making price changes. This will allow you to determine what your customer's invoice will look like when a given price change is made: From a4e24bbcb6f77517cfd6a4b069c23a9ddaac0cd1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Mar 2022 11:26:25 -0600 Subject: [PATCH 0055/2609] wip --- upgrade.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index be3fe153aa4..42fb433a0bb 100644 --- a/upgrade.md +++ b/upgrade.md @@ -324,7 +324,15 @@ Write operations such as `put`, `write`, and `writeStream` now overwrite existin #### Write Exceptions -Write operations such as `put`, `write`, and `writeStream` no longer throw an exception when a write operation fails. Instead, `false` is returned. +Write operations such as `put`, `write`, and `writeStream` no longer throw an exception when a write operation fails. Instead, `false` is returned. If you would like to preserve the previous behavior which threw exceptions, you may define the `throw` option within a filesystem disk's configuration array: + +```php +'public' => [ + 'driver' => 'local', + // ... + 'throw' => true, +], +``` #### Reading Missing Files From 16dab63083d7df9e763253a069c8fc4bff7da833 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Mar 2022 11:33:13 -0600 Subject: [PATCH 0056/2609] document write failures --- filesystem.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/filesystem.md b/filesystem.md index 386f1f0fd43..d62e94b83c8 100644 --- a/filesystem.md +++ b/filesystem.md @@ -313,6 +313,23 @@ The `put` method may be used to store file contents on a disk. You may also pass Storage::put('file.jpg', $resource); + +#### Failed Writes + +If the `put` method (or other "write" operations) is unable to write the file to disk, `false` will be returned: + + if (! Storage::put('file.jpg', $contents)) { + // The file could not be written to disk... + } + +Alternatively, you may define the `throw` option within your filesystem disk's configuration array. When this option is defined as `true`, "write" methods such as `put` will throw an instance of `League\Flysystem\UnableToWriteFile` when write operations fail: + + 'public' => [ + 'driver' => 'local', + // ... + 'throw' => true, + ], + #### Automatic Streaming From 3230705a2e4e5a2ef0ce4d93d04feb75f4ab773e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Mar 2022 11:33:52 -0600 Subject: [PATCH 0057/2609] wip --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index d62e94b83c8..12d2cf5bf2a 100644 --- a/filesystem.md +++ b/filesystem.md @@ -322,7 +322,7 @@ If the `put` method (or other "write" operations) is unable to write the file to // The file could not be written to disk... } -Alternatively, you may define the `throw` option within your filesystem disk's configuration array. When this option is defined as `true`, "write" methods such as `put` will throw an instance of `League\Flysystem\UnableToWriteFile` when write operations fail: +If you wish, you may define the `throw` option within your filesystem disk's configuration array. When this option is defined as `true`, "write" methods such as `put` will throw an instance of `League\Flysystem\UnableToWriteFile` when write operations fail: 'public' => [ 'driver' => 'local', From 2d4ec798de59ddd06f8e77d59e9e65ebd21aa2fa Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Mar 2022 11:35:52 -0600 Subject: [PATCH 0058/2609] wip --- filesystem.md | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/filesystem.md b/filesystem.md index 12d2cf5bf2a..6fd92b1d7e6 100644 --- a/filesystem.md +++ b/filesystem.md @@ -13,6 +13,9 @@ - [File URLs](#file-urls) - [File Metadata](#file-metadata) - [Storing Files](#storing-files) + - [Prepending & Appending To Files](#prepending-appending-to-files) + - [Copying & Moving Files](#copying-moving-files) + - [Automatic Streaming](#automatic-streaming) - [File Uploads](#file-uploads) - [File Visibility](#file-visibility) - [Deleting Files](#deleting-files) @@ -330,8 +333,26 @@ If you wish, you may define the `throw` option within your filesystem disk's con 'throw' => true, ], + +### Prepending & Appending To Files + +The `prepend` and `append` methods allow you to write to the beginning or end of a file: + + Storage::prepend('file.log', 'Prepended Text'); + + Storage::append('file.log', 'Appended Text'); + + +### Copying & Moving Files + +The `copy` method may be used to copy an existing file to a new location on the disk, while the `move` method may be used to rename or move an existing file to a new location: + + Storage::copy('old/file.jpg', 'new/file.jpg'); + + Storage::move('old/file.jpg', 'new/file.jpg'); + -#### Automatic Streaming +### Automatic Streaming Streaming files to storage offers significantly reduced memory usage. If you would like Laravel to automatically manage streaming a given file to your storage location, you may use the `putFile` or `putFileAs` method. This method accepts either an `Illuminate\Http\File` or `Illuminate\Http\UploadedFile` instance and will automatically stream the file to your desired location: @@ -350,24 +371,6 @@ The `putFile` and `putFileAs` methods also accept an argument to specify the "vi Storage::putFile('photos', new File('/path/to/photo'), 'public'); - -#### Prepending & Appending To Files - -The `prepend` and `append` methods allow you to write to the beginning or end of a file: - - Storage::prepend('file.log', 'Prepended Text'); - - Storage::append('file.log', 'Appended Text'); - - -#### Copying & Moving Files - -The `copy` method may be used to copy an existing file to a new location on the disk, while the `move` method may be used to rename or move an existing file to a new location: - - Storage::copy('old/file.jpg', 'new/file.jpg'); - - Storage::move('old/file.jpg', 'new/file.jpg'); - ### File Uploads From 915fa91d49a6975f40a04449585a422514e80253 Mon Sep 17 00:00:00 2001 From: Brian Essig Date: Thu, 10 Mar 2022 10:13:50 -0500 Subject: [PATCH 0059/2609] Update horizon.md (#7798) * Update horizon.md Added a note for RHEL distributions on horizon setup. * Update horizon.md Co-authored-by: Taylor Otwell --- horizon.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/horizon.md b/horizon.md index f049137a925..359fabb6902 100644 --- a/horizon.md +++ b/horizon.md @@ -239,7 +239,9 @@ stdout_logfile=/home/forge/example.com/horizon.log stopwaitsecs=3600 ``` -> {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. +When defining your Supervisor configuration, 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. + +> {note} While the examples above are valid for Ubuntu based servers, the location and file extension expected of Supervisor configuration files may vary between other server operating systems. Please consult your server's documentation for more information. #### Starting Supervisor From ac34ab9462508940e3a7313a13e935a580263e80 Mon Sep 17 00:00:00 2001 From: Jose Jimenez Date: Thu, 10 Mar 2022 07:15:28 -0800 Subject: [PATCH 0060/2609] The anchor was changed from #streaming-results-lazily to #chunking-using-lazy-collections but some links still pointed to the old anchors. (#7793) --- eloquent.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent.md b/eloquent.md index aad88bd1d9d..928eddfeeb4 100644 --- a/eloquent.md +++ b/eloquent.md @@ -401,7 +401,7 @@ Similar to the `lazy` method, the `cursor` method may be used to significantly r The `cursor` method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor. -> {note} Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#streaming-results-lazily) instead. +> {note} Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality: @@ -427,7 +427,7 @@ foreach ($users as $user) { } ``` -Although the `cursor` method uses far less memory than a regular query (by only holding a single Eloquent model in memory at a time), it will still eventually run out of memory. This is [due to PHP's PDO driver internally caching all raw query results in its buffer](https://www.php.net/manual/en/mysqlinfo.concepts.buffering.php). If you're dealing with a very large number of Eloquent records, consider using [the `lazy` method](#streaming-results-lazily) instead. +Although the `cursor` method uses far less memory than a regular query (by only holding a single Eloquent model in memory at a time), it will still eventually run out of memory. This is [due to PHP's PDO driver internally caching all raw query results in its buffer](https://www.php.net/manual/en/mysqlinfo.concepts.buffering.php). If you're dealing with a very large number of Eloquent records, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. ### Advanced Subqueries From 737571d839774d45fa7bab90edccacc817103b17 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 10 Mar 2022 16:26:43 +0100 Subject: [PATCH 0061/2609] Update socialite.md (#7796) --- socialite.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socialite.md b/socialite.md index 726966e5c43..0e4efd044e7 100644 --- a/socialite.md +++ b/socialite.md @@ -35,7 +35,7 @@ When upgrading to a new major version of Socialite, it's important that you care ## Configuration -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: +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` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin`, `google`, `github`, `gitlab`, or `bitbucket`, depending on the providers your application requires: 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), @@ -187,4 +187,4 @@ The `stateless` method may be used to disable session state verification. This i return Socialite::driver('google')->stateless()->user(); -> {note} Stateless authentication is not available for the Twitter driver, which uses OAuth 1.0 for authentication. +> {note} Stateless authentication is not available for the Twitter OAuth 1.0 driver. From cab1f1d782a593dd30eb6590b08fe26969111cb1 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 10 Mar 2022 15:27:21 +0000 Subject: [PATCH 0062/2609] add soft delete `trashed` and `forceDeleted` to the events list (#7792) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 928eddfeeb4..7d16cf03309 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1262,7 +1262,7 @@ The `is` and `isNot` methods are also available when using the `belongsTo`, `has > {tip} Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). -Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `restoring`, `restored`, and `replicating`. +Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `trashed`, `forceDeleted`, `restoring`, `restored`, and `replicating`. The `retrieved` event will dispatch when an existing model is retrieved from the database. When a new model is saved for the first time, the `creating` and `created` events will dispatch. The `updating` / `updated` events will dispatch when an existing model is modified and the `save` method is called. The `saving` / `saved` events will dispatch when a model is created or updated - even if the model's attributes have not been changed. Event names ending with `-ing` are dispatched before any changes to the model are persisted, while events ending with `-ed` are dispatched after the changes to the model are persisted. From 3de884e6a28a230525c0315561fc3cf1f1efac9a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 10 Mar 2022 09:40:53 -0600 Subject: [PATCH 0063/2609] wip --- http-tests.md | 1 - 1 file changed, 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index df177d57422..15f6728e0ee 100644 --- a/http-tests.md +++ b/http-tests.md @@ -651,7 +651,6 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertSessionHasNoErrors](#assert-session-has-no-errors) [assertSessionDoesntHaveErrors](#assert-session-doesnt-have-errors) [assertSessionMissing](#assert-session-missing) -[assertSimilarJson](#assert-similar-json) [assertStatus](#assert-status) [assertSuccessful](#assert-successful) [assertUnauthorized](#assert-unauthorized) From 947a4a7fa9efaa51296ff2c85aafbd5773ff629e Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 10 Mar 2022 13:14:08 -0500 Subject: [PATCH 0064/2609] [9.x] Add mail/notification tags & metadata docs (#7791) * add mail/notification tags & metadata docs * formatting Co-authored-by: Taylor Otwell --- mail.md | 22 ++++++++++++++++++++++ notifications.md | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/mail.md b/mail.md index 9ced46384c8..be9c61ef492 100644 --- a/mail.md +++ b/mail.md @@ -11,6 +11,7 @@ - [View Data](#view-data) - [Attachments](#attachments) - [Inline Attachments](#inline-attachments) + - [Tags & Metadata](#tags-and-metadata) - [Customizing The Symfony Message](#customizing-the-symfony-message) - [Markdown Mailables](#markdown-mailables) - [Generating Markdown Mailables](#generating-markdown-mailables) @@ -470,6 +471,27 @@ If you already have a raw image data string you wish to embed into an email temp ``` + +### Tags & Metadata + +Some third-party email providers such as Mailgun and Postmark support message "tags" and "metadata", which may be used to group and track emails sent by your application. You may add tags and metadata to an email message via the `tag` and `metadata` methods: + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->view('emails.orders.shipped') + ->tag('shipment') + ->metadata('order_id', $this->order->id); + } + +If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) and [metadata](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). + +If your application is using Amazon SES to send emails, you should use the `metadata` method to attach [SES "tags"](https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html) to the message. + ### Customizing The Symfony Message diff --git a/notifications.md b/notifications.md index e2ea7962859..51f2e33b490 100644 --- a/notifications.md +++ b/notifications.md @@ -16,6 +16,7 @@ - [Customizing The Mailer](#customizing-the-mailer) - [Customizing The Templates](#customizing-the-templates) - [Attachments](#mail-attachments) + - [Adding Tags & Metadata](#adding-tags-metadata) - [Using Mailables](#using-mailables) - [Previewing Mail Notifications](#previewing-mail-notifications) - [Markdown Mail Notifications](#markdown-mail-notifications) @@ -526,6 +527,25 @@ The `attachData` method may be used to attach a raw string of bytes as an attach ]); } + +### Adding Tags & Metadata + +Tags and metadata can be added to the `MailMessage` - these are used by your email service for filtering/processing: + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->greeting('Comment Upvoted!') + ->tag('upvote') + ->metadata('comment_id', $this->comment->id); + } + ### Using Mailables From 3e75e14e4efbfbea92ca715c6126025d84a30c05 Mon Sep 17 00:00:00 2001 From: Erik Sadewater <72336569+esadewater@users.noreply.github.com> Date: Thu, 10 Mar 2022 20:02:10 +0100 Subject: [PATCH 0065/2609] Update routing.md (#7799) * Update routing.md Added requirement of string-backed enums to "Implicit Enum Binding" as stated in https://github.com/laravel/framework/pull/40281: >Now, it's important to keep in mind, that the given Enum needs to be a Backed Enum with a string backing type. Meaning that, the Enum header needs to look like this: >enum {{ EnumName }}: string * Update routing.md * Update routing.md Co-authored-by: Taylor Otwell --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index f41353a4098..bb322186e31 100644 --- a/routing.md +++ b/routing.md @@ -510,7 +510,7 @@ Typically, a 404 HTTP response will be generated if an implicitly bound model is ### Implicit Enum Binding -PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To compliment this feature, Laravel allows you to type-hint an Enum on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: +PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To compliment this feature, Laravel allows you to type-hint a "backed" Enum on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: ```php Date: Thu, 10 Mar 2022 13:03:38 -0600 Subject: [PATCH 0066/2609] wip --- eloquent-mutators.md | 2 +- releases.md | 2 +- routing.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index bc43510602a..1beefdc50b1 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -412,7 +412,7 @@ If a custom format is applied to the `date` or `datetime` cast, such as `datetim > {note} Enum casting is only available for PHP 8.1+. -Eloquent also allows you to cast your attribute values to PHP ["backed" enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: +Eloquent also allows you to cast your attribute values to PHP ["backed" Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: use App\Enums\ServerStatus; diff --git a/releases.md b/releases.md index 4656e8d8a16..9eb89864126 100644 --- a/releases.md +++ b/releases.md @@ -135,7 +135,7 @@ public function address(): Attribute _Enum casting was contributed by [Mohamed Said](https://github.com/themsaid)_. -Eloquent now allows you to cast your attribute values to PHP ["backed" enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: +Eloquent now allows you to cast your attribute values to PHP ["backed" Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: use App\Enums\ServerStatus; diff --git a/routing.md b/routing.md index bb322186e31..c6000632707 100644 --- a/routing.md +++ b/routing.md @@ -510,7 +510,7 @@ Typically, a 404 HTTP response will be generated if an implicitly bound model is ### Implicit Enum Binding -PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To compliment this feature, Laravel allows you to type-hint a "backed" Enum on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: +PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To compliment this feature, Laravel allows you to type-hint a [backed Enum](https://www.php.net/manual/en/language.enumerations.backed.php) on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: ```php Date: Fri, 11 Mar 2022 15:24:33 +0100 Subject: [PATCH 0067/2609] Remove reduceMany from the Collection docs (#7801) The `reduceMany` function does not actually exist on the Collection class, instead its called `reduceSpread` as is documented below the `reduceMany` documentation. This causes confusion if one tries to use `reduceMany` instead of `reduceSpread`. --- collections.md | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/collections.md b/collections.md index 725992567e3..1984740105d 100644 --- a/collections.md +++ b/collections.md @@ -166,7 +166,6 @@ For the majority of the remaining collection documentation, we'll discuss each m [random](#method-random) [range](#method-range) [reduce](#method-reduce) -[reduceMany](#method-reduce-many) [reduceSpread](#method-reduce-spread) [reject](#method-reject) [replace](#method-replace) @@ -1821,24 +1820,7 @@ The `reduce` method also passes array keys in associative collections to the giv }); // 4264 - - -#### `reduceMany()` {.collection-method} - -The `reduceMany` method reduces the collection to an array of values, passing the results of each iteration into the subsequent iteration. This method is similar to the `reduce` method; however, it can accept multiple initial values: - - [$creditsRemaining, $batch] = Image::where('status', 'unprocessed') - ->get() - ->reduceMany(function ($creditsRemaining, $batch, $image) { - if ($creditsRemaining >= $image->creditsRequired()) { - $batch->push($image); - - $creditsRemaining -= $image->creditsRequired(); - } - - return [$creditsRemaining, $batch]; - }, $creditsAvailable, collect()); - + #### `reduceSpread()` {.collection-method} From a90161101b883cf73a019dd7166964a6af12f900 Mon Sep 17 00:00:00 2001 From: woodspire Date: Fri, 11 Mar 2022 09:31:43 -0500 Subject: [PATCH 0068/2609] Update releases.md (#7800) Laravel 10 will not support php 8.0 --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 9eb89864126..69a2d4d7186 100644 --- a/releases.md +++ b/releases.md @@ -27,7 +27,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 7 | 7.2 - 8.0 | March 3rd, 2020 | October 6th, 2020 | March 3rd, 2021 | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 8th, 2024 | -| 10 | 8.0 - 8.1 | February 7th, 2023 | August 7th, 2024 | February 7th, 2025 | +| 10 | 8.1 | February 7th, 2023 | August 7th, 2024 | February 7th, 2025 |
From 917e85a2e3be0cffa3b235cc8c23bf57ac50c677 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 11 Mar 2022 15:25:01 -0600 Subject: [PATCH 0069/2609] formatting --- socialite.md | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/socialite.md b/socialite.md index 0e4efd044e7..44eacc80128 100644 --- a/socialite.md +++ b/socialite.md @@ -14,9 +14,9 @@ ## 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 via 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. +> {tip} Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. ## Installation @@ -51,7 +51,7 @@ Before using Socialite, you will need to add credentials for the OAuth providers ### Routing -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: +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 routes below demonstrate the implementation of both routes: use Laravel\Socialite\Facades\Socialite; @@ -65,7 +65,7 @@ To authenticate users using an OAuth provider, you will need two routes: one for // $user->token }); -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. +The `redirect` method provided by the `Socialite` facade takes care of redirecting the user to the OAuth provider, while the `user` method will examine the incoming request and retrieve the user's information from the provider after they have approved the authentication request. ### Authentication & Storage @@ -79,22 +79,14 @@ Once the user has been retrieved from the OAuth provider, you may determine if t Route::get('/auth/callback', function () { $githubUser = Socialite::driver('github')->user(); - $user = User::where('github_id', $githubUser->id)->first(); - - if ($user) { - $user->update([ - 'github_token' => $githubUser->token, - 'github_refresh_token' => $githubUser->refreshToken, - ]); - } else { - $user = User::create([ - 'name' => $githubUser->name, - 'email' => $githubUser->email, - 'github_id' => $githubUser->id, - 'github_token' => $githubUser->token, - 'github_refresh_token' => $githubUser->refreshToken, - ]); - } + $user = User::updateOrCreate([ + 'github_id' => $githubUser->id, + ], [ + 'name' => $githubUser->name, + 'email' => $githubUser->email, + 'github_token' => $githubUser->token, + 'github_refresh_token' => $githubUser->refreshToken, + ]); Auth::login($user); @@ -106,7 +98,7 @@ Once the user has been retrieved from the OAuth provider, you may determine if t ### 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 use the `scopes` method to specify the "scopes" that should be included in the authentication request. This method will merge all previously specified scopes scopes with the scopes that you specify: use Laravel\Socialite\Facades\Socialite; @@ -123,7 +115,7 @@ You can overwrite all existing scopes on the authentication request using the `s ### 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: +A number of OAuth providers support other optional parameters on the redirect request. To include any optional parameters in the request, call the `with` method with an associative array: use Laravel\Socialite\Facades\Socialite; @@ -136,7 +128,9 @@ A number of OAuth providers support optional parameters in the redirect request. ## Retrieving User Details -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: +After the user is redirected back to your application's 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. + +Differing properties and methods may be available on this object depending on whether the OAuth provider you are authenticating with supports OAuth 1.0 or OAuth 2.0: use Laravel\Socialite\Facades\Socialite; @@ -163,7 +157,7 @@ After the user is redirected back to your authentication callback route, you may #### Retrieving User Details From A Token (OAuth2) -If you already have a valid access token for a user, you can retrieve their details using Socialite's `userFromToken` method: +If you already have a valid access token for a user, you can retrieve their user details using Socialite's `userFromToken` method: use Laravel\Socialite\Facades\Socialite; @@ -172,7 +166,7 @@ If you already have a valid access token for a user, you can retrieve their deta #### Retrieving User Details From A Token And Secret (OAuth1) -If you already have a valid token and secret for a user, you can retrieve their details using Socialite's `userFromTokenAndSecret` method: +If you already have a valid token and secret for a user, you can retrieve their user details using Socialite's `userFromTokenAndSecret` method: use Laravel\Socialite\Facades\Socialite; @@ -181,7 +175,7 @@ If you already have a valid token and secret for a user, you can retrieve their #### Stateless Authentication -The `stateless` method may be used to disable session state verification. This is useful when adding social authentication to an API: +The `stateless` method may be used to disable session state verification. This is useful when adding social authentication to a stateless API that does not utilize cookie based sessions: use Laravel\Socialite\Facades\Socialite; From 32b086f9e5db346671aa925f4dadab61f0092d96 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 11 Mar 2022 15:25:56 -0600 Subject: [PATCH 0070/2609] formatting --- socialite.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/socialite.md b/socialite.md index 44eacc80128..d4004006bf5 100644 --- a/socialite.md +++ b/socialite.md @@ -35,7 +35,9 @@ When upgrading to a new major version of Socialite, it's important that you care ## Configuration -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` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin`, `google`, `github`, `gitlab`, or `bitbucket`, depending on the providers your application requires: +Before using Socialite, you will need to add credentials for the OAuth providers your application utilizes. Typically, these credentials may be retrieved by creating a "developer application" within the dashboard of the service you will be authenticating with. + +These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin`, `google`, `github`, `gitlab`, or `bitbucket`, depending on the providers your application requires: 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), From f5f4444083893861513dcdaaf12abe86a26716e0 Mon Sep 17 00:00:00 2001 From: Arnaud Lier Date: Sat, 12 Mar 2022 17:59:31 +0100 Subject: [PATCH 0071/2609] fix: add missing dropFullText (#7803) dropFullText was missing from the examples. --- migrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/migrations.md b/migrations.md index 680c664f36d..9d34697ef8d 100644 --- a/migrations.md +++ b/migrations.md @@ -1113,6 +1113,7 @@ Command | Description `$table->dropPrimary('users_id_primary');` | Drop a primary key from the "users" table. `$table->dropUnique('users_email_unique');` | Drop a unique index from the "users" table. `$table->dropIndex('geo_state_index');` | Drop a basic index from the "geo" table. +`$table->dropFullText('posts_body_fulltext');` | Drop a full text index from the "posts" 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 index type: From 0ab599e555ee9995675d1634b503390748b53721 Mon Sep 17 00:00:00 2001 From: u01jmg3 Date: Sat, 12 Mar 2022 22:18:44 +0000 Subject: [PATCH 0072/2609] Correct return type docblock --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 51f2e33b490..9f2545008bd 100644 --- a/notifications.md +++ b/notifications.md @@ -349,7 +349,7 @@ Some notifications inform users of errors, such as a failed invoice payment. You * Get the mail representation of the notification. * * @param mixed $notifiable - * @return \Illuminate\Notifications\Message + * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { From ac61a97dfc238e24773f5da8bf7326eab1a190f9 Mon Sep 17 00:00:00 2001 From: u01jmg3 Date: Sat, 12 Mar 2022 22:18:51 +0000 Subject: [PATCH 0073/2609] Document `withSymfonyMessage()` for notifications --- notifications.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/notifications.md b/notifications.md index 9f2545008bd..b5ae80b7736 100644 --- a/notifications.md +++ b/notifications.md @@ -17,6 +17,7 @@ - [Customizing The Templates](#customizing-the-templates) - [Attachments](#mail-attachments) - [Adding Tags & Metadata](#adding-tags-metadata) + - [Customizing The Symfony Message](#customizing-the-symfony-message) - [Using Mailables](#using-mailables) - [Previewing Mail Notifications](#previewing-mail-notifications) - [Markdown Mail Notifications](#markdown-mail-notifications) @@ -546,6 +547,29 @@ Tags and metadata can be added to the `MailMessage` - these are used by your ema ->metadata('comment_id', $this->comment->id); } + +### Customizing The Symfony Message + +The `withSymfonyMessage` method of the `MailMessage` base class allows you to register a closure which will be invoked with the Symfony Message instance before sending the message. This gives you an opportunity to deeply customize the message before it is delivered: + + use Symfony\Component\Mime\Email; + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->withSymfonyMessage(function (Email $message) { + $message->getHeaders()->addTextHeader( + 'Custom-Header', 'Header Value' + ); + }); + } + ### Using Mailables From cc482bd7187c5547abc87cce3bcb9ac146609aa3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 14 Mar 2022 13:18:46 -0500 Subject: [PATCH 0074/2609] formatting --- notifications.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/notifications.md b/notifications.md index b5ae80b7736..3737602fbc7 100644 --- a/notifications.md +++ b/notifications.md @@ -531,7 +531,7 @@ The `attachData` method may be used to attach a raw string of bytes as an attach ### Adding Tags & Metadata -Tags and metadata can be added to the `MailMessage` - these are used by your email service for filtering/processing: +Some third-party email providers such as Mailgun and Postmark support message "tags" and "metadata", which may be used to group and track emails sent by your application. You may add tags and metadata to an email message via the `tag` and `metadata` methods: /** * Get the mail representation of the notification. @@ -547,10 +547,15 @@ Tags and metadata can be added to the `MailMessage` - these are used by your ema ->metadata('comment_id', $this->comment->id); } +If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) and [metadata](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). + +If your application is using Amazon SES to send emails, you should use the `metadata` method to attach [SES "tags"](https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html) to the message. +Tags and metadata can be added to the `MailMessage` - these are used by your email service for filtering/processing: + ### Customizing The Symfony Message -The `withSymfonyMessage` method of the `MailMessage` base class allows you to register a closure which will be invoked with the Symfony Message instance before sending the message. This gives you an opportunity to deeply customize the message before it is delivered: +The `withSymfonyMessage` method of the `MailMessage` class allows you to register a closure which will be invoked with the Symfony Message instance before sending the message. This gives you an opportunity to deeply customize the message before it is delivered: use Symfony\Component\Mime\Email; From dea99d425fbcee6cc250ea088e3a70102ae0e9c1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 14 Mar 2022 14:43:57 -0500 Subject: [PATCH 0075/2609] augment documentation --- queues.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/queues.md b/queues.md index 6c01f1a98f8..5dc0e8f3eb5 100644 --- a/queues.md +++ b/queues.md @@ -302,6 +302,8 @@ In certain cases, you may want to define a specific "key" that makes the job uni In the example above, the `UpdateSearchIndex` job is unique by a product ID. So, any new dispatches of the job with the same product ID will be ignored until the existing job has completed processing. In addition, if the existing job is not processed within one hour, the unique lock will be released and another job with the same unique key can be dispatched to the queue. +> {note} If your application dispatches jobs from multiple web servers or containers, you should ensure that all of your servers are communicating with the same central cache server so that Laravel can accurately determine if a job is unique. + #### Keeping Jobs Unique Until Processing Begins From 0ba519feff3aa33123113b7ee30440a5dfc7c857 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Mar 2022 09:57:32 -0500 Subject: [PATCH 0076/2609] document freeze time --- mocking.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mocking.md b/mocking.md index e5d609a5abd..42336d1175d 100644 --- a/mocking.md +++ b/mocking.md @@ -610,6 +610,8 @@ For more information on testing file uploads, you may consult the [HTTP testing When testing, you may occasionally need to modify the time returned by helpers such as `now` or `Illuminate\Support\Carbon::now()`. Thankfully, Laravel's base feature test class includes helpers that allow you to manipulate the current time: + use Illuminate\Support\Carbon; + public function testTimeCanBeManipulated() { // Travel into the future... @@ -621,6 +623,11 @@ When testing, you may occasionally need to modify the time returned by helpers s $this->travel(5)->weeks(); $this->travel(5)->years(); + // Freeze time and resume normal time after executing closure... + $this->freezeTime(function (Carbon $time) { + // ... + }); + // Travel into the past... $this->travel(-5)->hours(); From e5b7c7e5bb2e0fcee0b02512a66946d24af918bb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Mar 2022 09:59:08 -0500 Subject: [PATCH 0077/2609] document passing closure to assertJsonPath --- http-tests.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/http-tests.md b/http-tests.md index 15f6728e0ee..682b39e0fae 100644 --- a/http-tests.md +++ b/http-tests.md @@ -338,6 +338,10 @@ If you would like to verify that the JSON response contains the given data at a } } +The `assertJsonPath` method also accepts a closure, which may be used to dynamically determine if the assertion should pass: + + $response->assertJsonPath('team.owner.name', fn ($name) => strlen($name) >= 3); + ### Fluent JSON Testing From 60ed79df60fbe1ce992df9216a6fe782ffd6051e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Mar 2022 10:00:29 -0500 Subject: [PATCH 0078/2609] assertDirectoryEmpty documentation --- mocking.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mocking.md b/mocking.md index 42336d1175d..4812e5b4592 100644 --- a/mocking.md +++ b/mocking.md @@ -598,6 +598,9 @@ The `Storage` facade's `fake` method allows you to easily generate a fake disk t // Assert one or more files were not stored... Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); + + // Assert that a given directory is empty... + Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); } } From 207377c67635e538ac1fbb8c834784139d18c127 Mon Sep 17 00:00:00 2001 From: Lito Date: Tue, 15 Mar 2022 19:54:16 +0100 Subject: [PATCH 0079/2609] Added `when` second callback argument. (#7807) * Added `when` second callback argument. * update example Co-authored-by: Taylor Otwell --- collections.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/collections.md b/collections.md index 1984740105d..7fddee2c82e 100644 --- a/collections.md +++ b/collections.md @@ -2769,15 +2769,15 @@ The `values` method returns a new collection with the keys reset to consecutive #### `when()` {.collection-method} -The `when` method will execute the given callback when the first argument given to the method evaluates to `true`: +The `when` method will execute the given callback when the first argument given to the method evaluates to `true`. The collection instance and the first argument given to the `when` method will be provided to the closure: $collection = collect([1, 2, 3]); - $collection->when(true, function ($collection) { + $collection->when(true, function ($collection, $value) { return $collection->push(4); }); - $collection->when(false, function ($collection) { + $collection->when(false, function ($collection, $value) { return $collection->push(5); }); @@ -2789,7 +2789,7 @@ A second callback may be passed to the `when` method. The second callback will b $collection = collect([1, 2, 3]); - $collection->when(false, function ($collection) { + $collection->when(false, function ($collection, $value) { return $collection->push(4); }, function ($collection) { return $collection->push(5); From ef53f85ef826c332508bd7f0ce5466d67e80d1d3 Mon Sep 17 00:00:00 2001 From: Reza Amini Date: Wed, 16 Mar 2022 01:52:54 +0330 Subject: [PATCH 0080/2609] [9.x] Add docs for ucsplit method on Str (#7808) * [9.x] Add docs for ucsplit method on Str * Update helpers.md Co-authored-by: Taylor Otwell --- helpers.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/helpers.md b/helpers.md index 17e74802a3a..c574a88db57 100644 --- a/helpers.md +++ b/helpers.md @@ -143,6 +143,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::title](#method-title-case) [Str::toHtmlString](#method-str-to-html-string) [Str::ucfirst](#method-str-ucfirst) +[Str::ucsplit](#method-str-ucsplit) [Str::upper](#method-str-upper) [Str::uuid](#method-str-uuid) [Str::wordCount](#method-str-word-count) @@ -1877,6 +1878,17 @@ The `Str::ucfirst` method returns the given string with the first character capi // Foo bar + +#### `Str::ucsplit()` {.collection-method} + +The `Str::ucsplit` method splits the given string into an array by uppercase characters: + + use Illuminate\Support\Str; + + $segments = Str::ucsplit('FooBar'); + + // [0 => 'Foo', 1 => 'Bar'] + #### `Str::upper()` {.collection-method} From 5b9ffbfecae5df91032ca067d9bbe9a8baa67292 Mon Sep 17 00:00:00 2001 From: Jacek Sawoszczuk Date: Wed, 16 Mar 2022 15:18:11 +0100 Subject: [PATCH 0081/2609] Fix lcfirst method link (#7810) Fixes the link as it was not working before and removes `()` so that the it's consistent with others. --- helpers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index c574a88db57..86dbda40db9 100644 --- a/helpers.md +++ b/helpers.md @@ -2309,8 +2309,8 @@ The `kebab` method converts the given string to `kebab-case`: // foo-bar - -#### `lcfirst()` {.collection-method} + +#### `lcfirst` {.collection-method} The `lcfirst` method returns the given string with the first character lowercased: From 9ddee96313f868d8a933c1357c77893cb2fb6e53 Mon Sep 17 00:00:00 2001 From: lagbox Date: Wed, 16 Mar 2022 10:50:49 -0400 Subject: [PATCH 0082/2609] Update eloquent-resource toArray examples to use $request (#7809) Adjust the code examples to use the `$request` variable that is passed to the `toArray` method when possible. I thought it might be nice if there was examples that used the Request that is passed. --- eloquent-resources.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index d5b2759f643..459820955dd 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -498,8 +498,6 @@ Paginated responses always contain `meta` and `links` keys with information abou Sometimes you may wish to only include an attribute in a resource response if a given condition is met. For example, you may wish to only include a value if the current user is an "administrator". Laravel provides a variety of helper methods to assist you in this situation. The `when` method may be used to conditionally add an attribute to a resource response: - use Illuminate\Support\Facades\Auth; - /** * Transform the resource into an array. * @@ -512,7 +510,7 @@ Sometimes you may wish to only include an attribute in a resource response if a 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, - 'secret' => $this->when(Auth::user()->isAdmin(), 'secret-value'), + 'secret' => $this->when($request->user()->isAdmin(), 'secret-value'), 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; @@ -522,7 +520,7 @@ In this example, the `secret` key will only be returned in the final resource re The `when` method also accepts a closure as its second argument, allowing you to calculate the resulting value only if the given condition is `true`: - 'secret' => $this->when(Auth::user()->isAdmin(), function () { + 'secret' => $this->when($request->user()->isAdmin(), function () { return 'secret-value'; }), @@ -543,7 +541,7 @@ Sometimes you may have several attributes that should only be included in the re 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, - $this->mergeWhen(Auth::user()->isAdmin(), [ + $this->mergeWhen($request->user()->isAdmin(), [ 'first-secret' => 'value', 'second-secret' => 'value', ]), From 122ad744a2c98aaa73b32c62e6e4a21754efaf9a Mon Sep 17 00:00:00 2001 From: Reza Amini Date: Thu, 17 Mar 2022 23:33:27 +0330 Subject: [PATCH 0083/2609] [9.x] Add ucsplit doc for Stringable section (#7814) * [9.x] Add ucsplit doc for Stringable section * Update helpers.md * Update helpers.md Co-authored-by: Taylor Otwell --- helpers.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/helpers.md b/helpers.md index 86dbda40db9..f19436b518a 100644 --- a/helpers.md +++ b/helpers.md @@ -221,6 +221,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [title](#method-fluent-str-title) [trim](#method-fluent-str-trim) [ucfirst](#method-fluent-str-ucfirst) +[ucsplit](#method-fluent-str-ucsplit) [upper](#method-fluent-str-upper) [when](#method-fluent-str-when) [whenContains](#method-fluent-str-when-contains) @@ -2846,6 +2847,17 @@ The `ucfirst` method returns the given string with the first character capitaliz $string = Str::of('foo bar')->ucfirst(); // Foo bar + + +#### `ucsplit` {.collection-method} + +The `ucsplit` method splits the given string into a collection by uppercase characters: + + use Illuminate\Support\Str; + + $string = Str::of('Foo Bar')->ucsplit(); + + // collect(['Foo', 'Bar']) #### `upper` {.collection-method} From eb4b78bc89d76fbe9eedb776f3d6ac71effbb71b Mon Sep 17 00:00:00 2001 From: madshadow Date: Fri, 18 Mar 2022 11:05:23 -0700 Subject: [PATCH 0084/2609] update The Redis Facade Alias section of Redis docs (#7815) * update The Redis Facade Alias section of Redis docs The current documentation re: using Predis for Redis suggests uncommenting the Redis alias in config/app.php. However, that commented out alias was removed when Facade::defaultAliases() was added. This commit instructs users who are using Predis and who want a Redis alias to insert the alias into config/app.php * Update redis.md Co-authored-by: Taylor Otwell --- redis.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/redis.md b/redis.md index c3cc894122d..ba670195757 100644 --- a/redis.md +++ b/redis.md @@ -148,7 +148,11 @@ In addition to the default `host`, `port`, `database`, and `password` server con #### 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. +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. By default, no `Redis` alias is included because it would conflict with the `Redis` class name provided by the phpredis extension. If you are using the Predis client and would like to add a `Redis` alias, you may add it to the `aliases` array in your application's `config/app.php` configuration file: + + 'aliases' => Facade::defaultAliases()->merge([ + 'Redis' => Illuminate\Support\Facades\Redis::class, + ])->toArray(), ### phpredis From 7ae18152dbdb8f9ae9b63fe7dbec2d06ee21cfe7 Mon Sep 17 00:00:00 2001 From: Mohamed Magdi <91500450+magdicom@users.noreply.github.com> Date: Fri, 18 Mar 2022 22:52:14 +0200 Subject: [PATCH 0085/2609] typo (#7816) --- socialite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/socialite.md b/socialite.md index d4004006bf5..35a352deb3e 100644 --- a/socialite.md +++ b/socialite.md @@ -100,7 +100,7 @@ Once the user has been retrieved from the OAuth provider, you may determine if t ### Access Scopes -Before redirecting the user, you may use the `scopes` method to specify the "scopes" that should be included in the authentication request. This method will merge all previously specified scopes scopes with the scopes that you specify: +Before redirecting the user, you may use the `scopes` method to specify the "scopes" that should be included in the authentication request. This method will merge all previously specified scopes with the scopes that you specify: use Laravel\Socialite\Facades\Socialite; From 0475b17011d91b3767e7a27dc835af245e80e39f Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Sun, 20 Mar 2022 05:51:12 +0900 Subject: [PATCH 0086/2609] Add withInput method to manual authentication example (#7818) * Add withInput method to manual authentication example * Update authentication.md * Update authentication.md Co-authored-by: Taylor Otwell --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index bc107f6f329..88111db7210 100644 --- a/authentication.md +++ b/authentication.md @@ -251,7 +251,7 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ return back()->withErrors([ 'email' => 'The provided credentials do not match our records.', - ]); + ])->onlyInput(['email']); } } From 620dddbc5d2ed5756c310edd8acd9b09305cd1d0 Mon Sep 17 00:00:00 2001 From: finus16 <36240553+finus16@users.noreply.github.com> Date: Mon, 21 Mar 2022 17:00:00 +0100 Subject: [PATCH 0087/2609] New blade directives (#7820) * New blade directives * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/upgrade.md b/upgrade.md index 42fb433a0bb..eecbdd5fbac 100644 --- a/upgrade.md +++ b/upgrade.md @@ -136,6 +136,12 @@ public function ignore(string $class); When iterating over a `LazyCollection` instance within a Blade template, the `$loop` variable is no longer available, as accessing this variable causes the entire `LazyCollection` to be loaded into memory, thus rendering the usage of lazy collections pointless in this scenario. +#### Checked / Disabled / Selected Blade Directives + +**Likelihood Of Impact: Low** + +The new `@checked`, `@disabled`, and `@selected` Blade directives may conflict with Vue events of the same name. You may use `@@` to escape the directives and avoid this conflict: `@@selected`. + ### Collections #### The `Enumerable` Contract From e3b562a30d378c72584726928ec1efaec3caf950 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 21 Mar 2022 17:22:40 +0100 Subject: [PATCH 0088/2609] [9.x] Document message ID and `MessageSent` changes (#7819) * Update upgrade.md * Update upgrade.md * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/upgrade.md b/upgrade.md index eecbdd5fbac..eee3dcbd283 100644 --- a/upgrade.md +++ b/upgrade.md @@ -556,6 +556,20 @@ Again, many applications may not be interacting with these methods, as they are SwiftMailer offered the ability to define a custom domain to include in generated Message IDs via the `mime.idgenerator.idright` configuration option. This is not supported by Symfony Mailer. Instead, Symfony Mailer will automatically generate a Message ID based on the sender. +##### SES Message IDs + +In previous releases of Laravel, the SES transport generated separate `X-Message-ID` and `X-SES-Message-ID` headers. In Laravel 9, these headers are removed. Instead, message ID headers are automatically generated by Symfony Mailer and may be accessed via the `getMessageId` method: + + $message = Mail::to($request->user())->send(new OrderShipped($order)); + + $messageId = $message->getMessageId(); + +#### `MessageSent` Event Changes + +The `message` property of the `Illuminate\Mail\Events\MessageSent` event now contains an instance of `Symfony\Component\Mime\Email` instead of an instance of `Swift_Message`. This message represents the email **before** it is sent. + +Additionally, a new `sent` property has been added to the `MessageSent` event. This property contains an instance of `Illuminate\Mail\SentMessage` and contains information about the sent email, such as the message ID. + #### Forced Reconnections It is no longer possible to force a transport reconnection (for example when the mailer is running via a daemon process). Instead, Symfony Mailer will attempt to reconnect to the transport automatically and throw an exception if the reconnection fails. From 5e343689080b30abea74ca11940a501c7caf88c6 Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Mon, 21 Mar 2022 17:30:09 -0500 Subject: [PATCH 0089/2609] [9.x] Proofread eloquent-serialization (#7824) * Remove extra word --- eloquent-serialization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-serialization.md b/eloquent-serialization.md index 3b7dac91dd3..e90918ae154 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -72,7 +72,7 @@ When an Eloquent model is converted to JSON, its loaded relationships will autom ## Hiding Attributes From JSON -Sometimes you may wish to limit the attributes, such as passwords, that are included in your model's array or JSON representation. To do so, add a `$hidden` property to your model. In attributes that are listed in the `$hidden` property's array will not be included in the serialized representation of your model: +Sometimes you may wish to limit the attributes, such as passwords, that are included in your model's array or JSON representation. To do so, add a `$hidden` property to your model. Attributes that are listed in the `$hidden` property's array will not be included in the serialized representation of your model: Date: Mon, 21 Mar 2022 17:30:32 -0500 Subject: [PATCH 0090/2609] [9.x] Proofread eloquent-mutators (#7823) * Remove extra word --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 1beefdc50b1..0a81667a8c1 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -91,7 +91,7 @@ public function address(): Attribute } ``` -When returning value objects from accessors, any changes made to the value object will automatically be synced back to the model before the model is saved. This is possible because Eloquent retains instances returned by accessors so it can be return the same instance each time the accessor is invoked: +When returning value objects from accessors, any changes made to the value object will automatically be synced back to the model before the model is saved. This is possible because Eloquent retains instances returned by accessors so it can return the same instance each time the accessor is invoked: use App\Models\User; From 8409c0a5379b92d9a4f87cabf057cb705b1d6bcd Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Mon, 21 Mar 2022 17:30:49 -0500 Subject: [PATCH 0091/2609] [9.x] Proofread eloquent-relationships (#7822) * Accidental capitalization * Add missing word --- eloquent-relationships.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 4cf9c689ada..74ab28b03b5 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -342,7 +342,7 @@ public function largestOrder() #### Advanced Has One Of Many Relationships -It is possible to construct more advanced "has one of many" relationships. For example, A `Product` model may have many associated `Price` models that are retained in the system even after new pricing is published. In addition, new pricing data for the product may be able to be published in advance to take effect at a future date via a `published_at` column. +It is possible to construct more advanced "has one of many" relationships. For example, a `Product` model may have many associated `Price` models that are retained in the system even after new pricing is published. In addition, new pricing data for the product may be able to be published in advance to take effect at a future date via a `published_at` column. So, in summary, we need to retrieve the latest published pricing where the published date is not in the future. In addition, if two prices have the same published date, we will prefer the price with the greatest ID. To accomplish this, we must pass an array to the `ofMany` method that contains the sortable columns which determine the latest price. In addition, a closure will be provided as the second argument to the `ofMany` method. This closure will be responsible for adding additional publish date constraints to the relationship query: @@ -1633,7 +1633,7 @@ If you are eager loading a `morphTo` relationship, Eloquent will run multiple qu ]); }])->get(); -In this example, Eloquent will only eager load posts that have not been hidden and videos have a `type` value of "educational". +In this example, Eloquent will only eager load posts that have not been hidden and videos that have a `type` value of "educational". ### Lazy Eager Loading From 39b8461b9968c66bfd870aedc37db1ce710e2405 Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Mon, 21 Mar 2022 17:31:01 -0500 Subject: [PATCH 0092/2609] [9.x] Proofread eloquent - Add missing word (#7821) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 7d16cf03309..de6196c6aa1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -618,7 +618,7 @@ The `update` method expects an array of column and value pairs representing the Eloquent provides the `isDirty`, `isClean`, and `wasChanged` methods to examine the internal state of your model and determine how its attributes have changed from when the model was originally retrieved. -The `isDirty` method determines if any of the model's attributes have been changed since the model was retrieved. You may pass a specific attribute name or an array of attributes to the `isDirty` method to determine if any of the attributes are "dirty". The `isClean` will determine if an attribute has remained unchanged since the model was retrieved. This method also accepts an optional attribute argument: +The `isDirty` method determines if any of the model's attributes have been changed since the model was retrieved. You may pass a specific attribute name or an array of attributes to the `isDirty` method to determine if any of the attributes are "dirty". The `isClean` method will determine if an attribute has remained unchanged since the model was retrieved. This method also accepts an optional attribute argument: use App\Models\User; From eca9ea876aac40f83f3ef8450e2fabeb19112a1c Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Mon, 21 Mar 2022 17:32:22 -0500 Subject: [PATCH 0093/2609] [9.x] Review mocking - Clarify requirements for `image` method (#7825) * [9.x] Review mocking * Clarify requirements for `image` method * Calling `UploadedFile::fake()->image()` calls functions like `imagecreatetruecolor`, which requires the GD Library * Update mocking.md Co-authored-by: Taylor Otwell --- mocking.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mocking.md b/mocking.md index 4812e5b4592..da6c3206b7f 100644 --- a/mocking.md +++ b/mocking.md @@ -604,9 +604,9 @@ The `Storage` facade's `fake` method allows you to easily generate a fake disk t } } -For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). +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. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). -> {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. +> {note} The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). ## Interacting With Time From 288326265b805fe628c6e5cfa35e78d930d4d582 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 23 Mar 2022 16:34:03 +0100 Subject: [PATCH 0094/2609] Update upgrade.md (#7827) --- upgrade.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/upgrade.md b/upgrade.md index eee3dcbd283..dd9cdc9d86b 100644 --- a/upgrade.md +++ b/upgrade.md @@ -556,14 +556,6 @@ Again, many applications may not be interacting with these methods, as they are SwiftMailer offered the ability to define a custom domain to include in generated Message IDs via the `mime.idgenerator.idright` configuration option. This is not supported by Symfony Mailer. Instead, Symfony Mailer will automatically generate a Message ID based on the sender. -##### SES Message IDs - -In previous releases of Laravel, the SES transport generated separate `X-Message-ID` and `X-SES-Message-ID` headers. In Laravel 9, these headers are removed. Instead, message ID headers are automatically generated by Symfony Mailer and may be accessed via the `getMessageId` method: - - $message = Mail::to($request->user())->send(new OrderShipped($order)); - - $messageId = $message->getMessageId(); - #### `MessageSent` Event Changes The `message` property of the `Illuminate\Mail\Events\MessageSent` event now contains an instance of `Symfony\Component\Mime\Email` instead of an instance of `Swift_Message`. This message represents the email **before** it is sent. From ffbb9c9ce94e8fef596df48e0538c8812d9dc1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20St=C3=BCckler?= Date: Fri, 25 Mar 2022 15:50:42 +0100 Subject: [PATCH 0095/2609] doc: add info about used markdown parser (#7828) * doc: add info about used markdown parser There are fine differences between different Markdown parsers. Without studying source code, it should be documentet, which Markdown parser is being used. * doc: update link to phpleague implementation * Update helpers.md Co-authored-by: Taylor Otwell --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index f19436b518a..d2f35696dc7 100644 --- a/helpers.md +++ b/helpers.md @@ -1492,7 +1492,7 @@ The `Str::lower` method converts the given string to lowercase: #### `Str::markdown()` {.collection-method} -The `Str::markdown` method converts GitHub flavored Markdown into HTML: +The `Str::markdown` method converts GitHub flavored Markdown into HTML using [CommonMark](https://commonmark.thephpleague.com/): use Illuminate\Support\Str; From 35feb426c6555fbb316f974e752a7a83524843a9 Mon Sep 17 00:00:00 2001 From: Adro Morelos Date: Mon, 28 Mar 2022 13:17:51 -0500 Subject: [PATCH 0096/2609] Fix syntax error on mail.md (#7839) Just add the missing `;` --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index be9c61ef492..6bfa50c767d 100644 --- a/mail.md +++ b/mail.md @@ -943,7 +943,7 @@ Laravel includes a variety of mail transports; however, you may wish to write yo */ public function __construct(ApiClient $client) { - $this->client = $client + $this->client = $client; } /** From 4bc1d05a4247bf62043b6614a5a2077a738815d2 Mon Sep 17 00:00:00 2001 From: Andrew Bashtannik Date: Tue, 29 Mar 2022 16:31:11 +0300 Subject: [PATCH 0097/2609] Update validation.md with new rule paragraph (#7842) The merged pull request https://github.com/laravel/framework/pull/41691 adds a new `exlcude_with` validation rule in v9.0 --- validation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation.md b/validation.md index 951146720ab..16454adcf61 100644 --- a/validation.md +++ b/validation.md @@ -795,6 +795,7 @@ Below is a list of all available validation rules and their function: [Exclude](#rule-exclude) [Exclude If](#rule-exclude-if) [Exclude Unless](#rule-exclude-unless) +[Exclude With](#rule-exclude-with) [Exclude Without](#rule-exclude-without) [Exists (Database)](#rule-exists) [File](#rule-file) @@ -1095,6 +1096,11 @@ The field under validation will be excluded from the request data returned by th The field under validation will be excluded from the request data returned by the `validate` and `validated` methods unless _anotherfield_'s field is equal to _value_. If _value_ is `null` (`exclude_unless:name,null`), the field under validation will be excluded unless the comparison field is `null` or the comparison field is missing from the request data. + +#### exclude_with:_anotherfield_ + +The field under validation will be excluded from the request data returned by the `validate` and `validated` methods if the _anotherfield_ field is present. + #### exclude_without:_anotherfield_ From 26b7091d77341e34e11ab78b222de6e243537b7a Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 29 Mar 2022 15:33:07 +0200 Subject: [PATCH 0098/2609] Bump PostgreSQL minimum support version (#7841) * Update database.md * Update database.md --- database.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.md b/database.md index 5b372cfff2e..ec7da3c7023 100644 --- a/database.md +++ b/database.md @@ -18,7 +18,7 @@ Almost every modern web application interacts with a database. Laravel makes int - MariaDB 10.2+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) - MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) -- PostgreSQL 9.6+ ([Version Policy](https://www.postgresql.org/support/versioning/)) +- PostgreSQL 10.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) - SQLite 3.8.8+ - SQL Server 2017+ ([Version Policy](https://docs.microsoft.com/en-us/lifecycle/products/?products=sql-server)) From 58e028a9afaab0d714acdf90966987fa5c92e72f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 09:53:18 -0500 Subject: [PATCH 0099/2609] update docs for auth session --- authentication.md | 12 ++++++------ middleware.md | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/authentication.md b/authentication.md index 88111db7210..6f295cb618c 100644 --- a/authentication.md +++ b/authentication.md @@ -424,13 +424,13 @@ In addition to calling the `logout` method, it is recommended that you invalidat 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` class' `web` middleware group: +Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` route middleware key as defined in your application's HTTP kernel: - 'web' => [ - // ... - \Illuminate\Session\Middleware\AuthenticateSession::class, - // ... - ], + Route::middleware(['auth', 'auth.session'])->group(function () { + Route::get('/', function () { + // ... + }); + }); 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: diff --git a/middleware.md b/middleware.md index d6cd5fcc1b8..e911ce65e90 100644 --- a/middleware.md +++ b/middleware.md @@ -194,7 +194,6 @@ Out of the box, Laravel comes with `web` and `api` middleware groups that contai \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, - // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, From aab5a6cfafc588e473ac55985b715c1abcd2dd31 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 09:55:12 -0500 Subject: [PATCH 0100/2609] document breeze ssr --- starter-kits.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/starter-kits.md b/starter-kits.md index 15cf8bc1aa3..0be65dc629e 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -71,6 +71,13 @@ npm run dev php artisan migrate ``` +If you would like Breeze to scaffold support for [Inertia SSR](https://inertiajs.com/server-side-rendering), you may provide the `ssr` option when invoking the `breeze:install` command: + +```shell +php artisan breeze:install vue --ssr +php artisan breeze:install react --ssr +``` + ### Breeze & Next.js / API From dfffe250fb997819a7d1d681d76ffd15e659325f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 09:59:17 -0500 Subject: [PATCH 0101/2609] document newLine --- helpers.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/helpers.md b/helpers.md index d2f35696dc7..bbb1cd0e8d0 100644 --- a/helpers.md +++ b/helpers.md @@ -192,6 +192,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [mask](#method-fluent-str-mask) [match](#method-fluent-str-match) [matchAll](#method-fluent-str-match-all) +[newLine](#method-fluent-str-new-line) [padBoth](#method-fluent-str-padboth) [padLeft](#method-fluent-str-padleft) [padRight](#method-fluent-str-padright) @@ -2448,6 +2449,18 @@ If you specify a matching group within the expression, Laravel will return a col If no matches are found, an empty collection will be returned. + +#### `newLine` {.collection-method} + +The `newLine` method appends an "end of line" character to a string: + + use Illuminate\Support\Str; + + $padded = Str::of('Laravel')->newLine()->append('Framework'); + + // 'Laravel + // Framework' + #### `padBoth` {.collection-method} From 6b1e2ade074f4951ff1d17ed1ce9e530f0781f8d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 10:05:45 -0500 Subject: [PATCH 0102/2609] document eloquent shouldCache --- eloquent-mutators.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 0a81667a8c1..04c86b23911 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -91,6 +91,9 @@ public function address(): Attribute } ``` + +#### Accessor Caching + When returning value objects from accessors, any changes made to the value object will automatically be synced back to the model before the model is saved. This is possible because Eloquent retains instances returned by accessors so it can return the same instance each time the accessor is invoked: use App\Models\User; @@ -102,6 +105,17 @@ When returning value objects from accessors, any changes made to the value objec $user->save(); +However, you may sometimes wish to enable caching for primitive values like strings and booleans, particularly if they are computationally intensive. To accomplish this, you may invoke the `shouldCache` method when defining your accessor: + +```php +public function hash(): Attribute +{ + return Attribute::make( + get: fn ($value) => bcrypt(gzuncompress($value)), + )->shouldCache(); +} +``` + If you would like to disable the object caching behavior of attributes, you may invoke the `withoutObjectCaching` method when defining the attribute: ```php From ff6ac2470b4de3b548ffd5365be2598389822dab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 10:17:05 -0500 Subject: [PATCH 0103/2609] document token expiration --- sanctum.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sanctum.md b/sanctum.md index c2c2197e883..885995aa8aa 100644 --- a/sanctum.md +++ b/sanctum.md @@ -10,6 +10,7 @@ - [Token Abilities](#token-abilities) - [Protecting Routes](#protecting-routes) - [Revoking Tokens](#revoking-tokens) + - [Token Expiration](#token-expiration) - [SPA Authentication](#spa-authentication) - [Configuration](#spa-configuration) - [Authenticating](#spa-authenticating) @@ -226,6 +227,15 @@ You may "revoke" tokens by deleting them from your database using the `tokens` r // Revoke a specific token... $user->tokens()->where('id', $tokenId)->delete(); + +### Token Expiration + +By default, Sanctum tokens never expire and may only be invalidated by [revoking the token](#revoking-tokens). However, if you would like to configure an expiration time for your application's API tokens, you may do so via the `expiration` configuration option defined in your application's `sanctum` configuration file. This configuration option defines the number of minutes until an issued token will be considered expired: + +```php +'expiration' => 525600, +``` + ## SPA Authentication From cf86b38c352d093e660fe9326f3cd833b009a2fd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 10:19:13 -0500 Subject: [PATCH 0104/2609] wip --- sanctum.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sanctum.md b/sanctum.md index 885995aa8aa..1218328acbd 100644 --- a/sanctum.md +++ b/sanctum.md @@ -236,6 +236,12 @@ By default, Sanctum tokens never expire and may only be invalidated by [revoking 'expiration' => 525600, ``` +If you have configured a token expiration time for your application, you may also wish to [schedule a task](/docs/{{version}}/scheduling) to prune your application's expired tokens. Thankfully, Sanctum includes a `sanctum:prune-expired` Artisan command that you may use to accomplish this. For example, you may configure a scheduled tasks to delete all expired token database records that have been expired for at least 24 hours: + +```php +$schedule->command('sanctum:prune-expired --hours=24')->daily(); +``` + ## SPA Authentication From 9a4eba7d441439bc8e4a3e6cac285d9174e1dd6b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 13:51:41 -0500 Subject: [PATCH 0105/2609] work on confirmation docs --- fortify.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/fortify.md b/fortify.md index 82e6ca28a40..b4d268ea988 100644 --- a/fortify.md +++ b/fortify.md @@ -271,12 +271,14 @@ Next, you should build a screen within your application where users can manage t ### Enabling Two Factor Authentication -To enable two factor authentication, your application should make a POST request to the `/user/two-factor-authentication` endpoint defined by Fortify. If the request is successful, the user will be redirected back to the previous URL and the `status` session variable will be set to `two-factor-authentication-enabled`. You may detect this `status` session variable within your templates to display the appropriate success message. If the request was an XHR request, `200` HTTP response will be returned: +To begin enabling two factor authentication, your application should make a POST request to the `/user/two-factor-authentication` endpoint defined by Fortify. If the request is successful, the user will be redirected back to the previous URL and the `status` session variable will be set to `two-factor-authentication-enabled`. You may detect this `status` session variable within your templates to display the appropriate success message. If the request was an XHR request, `200` HTTP response will be returned. + +After choosing to enable two factor authentication, the user must still "confirm" their two factor authentication configuration by providing a valid two factor authentication code. So, your "success" message should instruct the user that two factor authentication confirmation is still required: ```html @if (session('status') == 'two-factor-authentication-enabled') -
- Two factor authentication has been enabled. +
+ Please finish configuring two factor authentication below.
@endif ``` @@ -289,6 +291,23 @@ $request->user()->twoFactorQrCodeSvg(); If you are building a JavaScript powered frontend, you may make an XHR GET request to the `/user/two-factor-qr-code` endpoint to retrieve the user's two factor authentication QR code. This endpoint will return a JSON object containing an `svg` key. + +#### Confirming Two Factor Authentication + +In addition to displaying the user's two factor authentication QR code, you should provide a text input where the user can supply a valid authentication code to "confirm" their two factor authentication configuration. This code should be provided via a POST request to the `/user/confirmed-two-factor-authentication` endpoint defined by Fortify. + +If the request is successful, the user will be redirected back to the previous URL and the `status` session variable will be set to `two-factor-authentication-confirmed`: + +```html +@if (session('status') == 'two-factor-authentication-confirmed') +
+ Two factor authentication confirmed and enabled successfully. +
+@endif +``` + +If the request to the two factor authentication confirmation endpoint was made via an XHR request, a `200` HTTP response will be returned. + #### Displaying The Recovery Codes From ee644faa16c76bb1e88bff4a6c77678b16738464 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 13:53:19 -0500 Subject: [PATCH 0106/2609] adjust wording --- fortify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortify.md b/fortify.md index b4d268ea988..8c17a250d5e 100644 --- a/fortify.md +++ b/fortify.md @@ -294,7 +294,7 @@ If you are building a JavaScript powered frontend, you may make an XHR GET reque #### Confirming Two Factor Authentication -In addition to displaying the user's two factor authentication QR code, you should provide a text input where the user can supply a valid authentication code to "confirm" their two factor authentication configuration. This code should be provided via a POST request to the `/user/confirmed-two-factor-authentication` endpoint defined by Fortify. +In addition to displaying the user's two factor authentication QR code, you should provide a text input where the user can supply a valid authentication code to "confirm" their two factor authentication configuration. This code should be provided to the Laravel application via a POST request to the `/user/confirmed-two-factor-authentication` endpoint defined by Fortify. If the request is successful, the user will be redirected back to the previous URL and the `status` session variable will be set to `two-factor-authentication-confirmed`: From 41d81d4600ef8ea3a425242652251ec93c443ea5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Mar 2022 14:03:03 -0500 Subject: [PATCH 0107/2609] wip --- http-tests.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/http-tests.md b/http-tests.md index 682b39e0fae..884b84496e4 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1048,6 +1048,8 @@ Or, you may assert that a given field has a particular validation error message: 'name' => 'The given name was invalid.' ]); +> {tip} The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. + #### assertSessionHasErrorsIn @@ -1069,6 +1071,8 @@ Assert that the session has no validation errors for the given keys: $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); +> {tip} The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. + #### assertSessionMissing From d6761132ef102f2780d5979f5776c4a79ba914ca Mon Sep 17 00:00:00 2001 From: Craig Anderson Date: Tue, 29 Mar 2022 15:11:42 -0400 Subject: [PATCH 0108/2609] Document new excludeIf and prohibitedIf Rules (#7840) * Document new excludeIf and prohibitedIf Rules * formatting Co-authored-by: Taylor Otwell --- validation.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/validation.md b/validation.md index 16454adcf61..599aa5ccbce 100644 --- a/validation.md +++ b/validation.md @@ -1091,6 +1091,19 @@ The field under validation will be excluded from the request data returned by th The field under validation will be excluded from the request data returned by the `validate` and `validated` methods if the _anotherfield_ field is equal to _value_. +If complex conditional exclusion logic is required, you may utilize the `Rule::excludeIf` method. This method accepts a boolean or a closure. When given a closure, the closure should return `true` or `false` to indicate if the field under validation should be excluded: + + use Illuminate\Support\Facades\Validator; + use Illuminate\Validation\Rule; + + Validator::make($request->all(), [ + 'role_id' => Rule::excludeIf($request->user()->is_admin), + ]); + + Validator::make($request->all(), [ + 'role_id' => Rule::excludeIf(fn () => $request->user()->is_admin), + ]); + #### exclude_unless:_anotherfield_,_value_ @@ -1346,6 +1359,19 @@ The field under validation must be empty or not present. The field under validation must be empty or not present if the _anotherfield_ field is equal to any _value_. +If complex conditional prohibition logic is required, you may utilize the `Rule::prohibitedIf` method. This method accepts a boolean or a closure. When given a closure, the closure should return `true` or `false` to indicate if the field under validation should be prohibited: + + use Illuminate\Support\Facades\Validator; + use Illuminate\Validation\Rule; + + Validator::make($request->all(), [ + 'role_id' => Rule::prohibitedIf($request->user()->is_admin), + ]); + + Validator::make($request->all(), [ + 'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin), + ]); + #### prohibited_unless:_anotherfield_,_value_,... From dc69225eb40fb6f501ca32fc51873535957b749a Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Wed, 30 Mar 2022 23:34:05 +0900 Subject: [PATCH 0109/2609] Add a link to Testing Mailables (#7846) * Add a link to Testing Mailables * Update mocking.md Co-authored-by: Taylor Otwell --- mocking.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mocking.md b/mocking.md index da6c3206b7f..0586429b659 100644 --- a/mocking.md +++ b/mocking.md @@ -422,6 +422,11 @@ You may have noticed that there are two methods for asserting that mail was not return $mail->order->id === $order->id; }); + +#### Testing Mailable Content + +We suggest testing the content of your mailables separately from your tests that assert that a given mailable was "sent" to a specific user. To learn how to test the content of your mailables, check out our documentation on the [testing mailables](/docs/{{version}}/mail#testing-mailables). + ## Notification Fake From 9c3955e39d66423b105526f56881440ba2a12970 Mon Sep 17 00:00:00 2001 From: "M.KH" Date: Wed, 30 Mar 2022 19:09:43 +0430 Subject: [PATCH 0110/2609] Correct order model namespace (#7845) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 155e7cc3547..57a61d1c616 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -268,7 +268,7 @@ When a user is viewing one of their orders, we don't want them to have to refres namespace App\Events; - use App\Order; + use App\Models\Order; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; From c4c1fe77850d485ebc90a8814719b38d42f46054 Mon Sep 17 00:00:00 2001 From: Frank Steinberg <7845126+franksteinberg@users.noreply.github.com> Date: Fri, 1 Apr 2022 17:24:36 -0400 Subject: [PATCH 0111/2609] Add firstOrFail method to Collections documentation. (#7847) * Add firstOrFail method to Collections documentation. * Update collections.md Co-authored-by: Taylor Otwell --- collections.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/collections.md b/collections.md index 7fddee2c82e..4481bbdc7d0 100644 --- a/collections.md +++ b/collections.md @@ -119,6 +119,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [except](#method-except) [filter](#method-filter) [first](#method-first) +[firstOrFail](#method-first-or-fail) [firstWhere](#method-first-where) [flatMap](#method-flatmap) [flatten](#method-flatten) @@ -794,6 +795,23 @@ You may also call the `first` method with no arguments to get the first element // 1 + +#### `firstOrFail()` {.collection-method} + +The `firstOrFail` method is identical to the `first` method; however, if no result is found, an `Illuminate\Support\ItemNotFoundException` exception will be thrown: + + collect([1, 2, 3, 4])->firstOrFail(function ($value, $key) { + return $value > 5; + }); + + // Throws ItemNotFoundException... + +You may also call the `firstOrFail` method with no arguments to get the first element in the collection. If the collection is empty, an `Illuminate\Support\ItemNotFoundException` exception will be thrown: + + collect([])->firstOrFail(); + + // Throws ItemNotFoundException... + #### `firstWhere()` {.collection-method} @@ -3260,6 +3278,7 @@ Almost all methods available on the `Collection` class are also available on the [except](#method-except) [filter](#method-filter) [first](#method-first) +[firstOrFail](#method-first-or-fail) [firstWhere](#method-first-where) [flatMap](#method-flatmap) [flatten](#method-flatten) From 4655ebebc9853347a9e7b864c27af472a0cf5835 Mon Sep 17 00:00:00 2001 From: Zsolt Janes Date: Sun, 3 Apr 2022 17:06:52 +0200 Subject: [PATCH 0112/2609] Since it has only 1 argument I think we don't need the text first here. (#7848) --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index 7990b33cbc5..e3944afa126 100644 --- a/dusk.md +++ b/dusk.md @@ -568,7 +568,7 @@ The `attach` method may be used to attach a file to a `file` input element. Like ### Pressing Buttons -The `press` method may be used to click a button element on the page. The first argument given to the `press` method may be either the display text of the button or a CSS / Dusk selector: +The `press` method may be used to click a button element on the page. The argument given to the `press` method may be either the display text of the button or a CSS / Dusk selector: $browser->press('Login'); From dcdfff5b1983cbe4edd328ebd966e57c33ababff Mon Sep 17 00:00:00 2001 From: Vito Famiglietti Date: Mon, 4 Apr 2022 17:19:16 +0200 Subject: [PATCH 0113/2609] [9.x] Documentation for eloquent resources macros and whenNotNull method (#7853) * feat: add documentation for macros and whenNotNull * formatting * formatting Co-authored-by: Vito Famiglietti Co-authored-by: Taylor Otwell --- eloquent-resources.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eloquent-resources.md b/eloquent-resources.md index 459820955dd..a9c5a73d9e2 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -524,6 +524,10 @@ The `when` method also accepts a closure as its second argument, allowing you to return 'secret-value'; }), +Additionally, the `whenNotNull` method may be used to include an attribute in the resource response if the attribute is not null: + + 'name' => $this->whenNotNull($this->name), + #### Merging Conditional Attributes From 3da4c06183e52f0be8ad32839282bb944bdbc790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Debrauwer?= Date: Mon, 4 Apr 2022 17:27:01 +0200 Subject: [PATCH 0114/2609] Document retry always throws connection exception (#7851) --- http-client.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http-client.md b/http-client.md index 564e21ab1d7..e2ea0f1388e 100644 --- a/http-client.md +++ b/http-client.md @@ -189,6 +189,8 @@ If all of the requests fail, an instance of `Illuminate\Http\Client\RequestExcep $response = Http::retry(3, 100, throw: false)->post(...); +> {note} If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. + ### Error Handling From 91582cd4922a5c37a6a855bd3636573199d86581 Mon Sep 17 00:00:00 2001 From: Joel Butcher Date: Mon, 4 Apr 2022 16:52:59 +0100 Subject: [PATCH 0115/2609] [9.x] Document new isolate command (#7850) * Document new isolate command * Update valet.md * Reword and include isolated and unisolate command. * wording * Document new isolate command * formatting * wip * fix conflicts * formatting Co-authored-by: Taylor Otwell --- valet.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/valet.md b/valet.md index fea25e5f557..2f4fb4f730f 100644 --- a/valet.md +++ b/valet.md @@ -8,6 +8,7 @@ - [The "Link" Command](#the-link-command) - [Securing Sites With TLS](#securing-sites) - [Serving a Default Site](#serving-a-default-site) + - [Per-Site PHP Versions](#per-site-php-versions) - [Sharing Sites](#sharing-sites) - [Sharing Sites Via Ngrok](#sharing-sites-via-ngrok) - [Sharing Sites Via Expose](#sharing-sites-via-expose) @@ -205,7 +206,44 @@ valet unsecure laravel Sometimes, you may wish to configure Valet to serve a "default" site instead of a `404` when visiting an unknown `test` domain. To accomplish this, you may add a `default` option to your `~/.config/valet/config.json` configuration file containing the path to the site that should serve as your default site: - "default": "/Users/Sally/Sites/foo", + "default": "/Users/Sally/Sites/example-site", + + +### Per-Site PHP Versions + +By default, Valet uses your global PHP installation to serve your sites. However, if you need to support multiple PHP versions across various sites, you may use the `isolate` command to specify which PHP version a particular site should use. The `isolate` command configures Valet to use the specified PHP version for the site located in your current working directory: + +```shell +cd ~/Sites/example-site + +valet isolate php@8.0 +``` + +If your site name does not match the name of the directory that contains it, you may specify the site name using the `--site` option: + +```shell +valet isolate php@8.0 --site="site-name" +``` + +For convenience, you may use the `valet php`, `composer`, and `which-php` commands to proxy calls to the appropriate PHP CLI or tool based on the site's configured PHP version: + +```shell +valet php +valet composer +valet which-php +``` + +You may execute the `isolated` command to display a list of all of your isolated sites and their PHP versions: + +```shell +valet isolated +``` + +To revert a site back to Valet's globally installed PHP version, you may invoke the `unisolate` command from the site's root directory: + +```shell +valet unisolate +``` ## Sharing Sites From 8190221211de717202968475d256433bb2336bd4 Mon Sep 17 00:00:00 2001 From: Erfan <37825504+erfantkerfan@users.noreply.github.com> Date: Mon, 4 Apr 2022 22:40:10 +0430 Subject: [PATCH 0116/2609] remove php 8.0 from Laravel 10 (#7855) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 4656e8d8a16..86f0b2c0c48 100644 --- a/releases.md +++ b/releases.md @@ -27,7 +27,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 7 | 7.2 - 8.0 | March 3rd, 2020 | October 6th, 2020 | March 3rd, 2021 | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 8th, 2024 | -| 10 | 8.0 - 8.1 | February 7th, 2023 | August 7th, 2024 | February 7th, 2025 | +| 10 | 8.1 | February 7th, 2023 | August 7th, 2024 | February 7th, 2025 |
From bb7cd7ca91408d19bba91e98ddc1c9655b166d06 Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Mon, 4 Apr 2022 22:18:01 +0100 Subject: [PATCH 0117/2609] Added type to closure. (#7856) --- http-client.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index e2ea0f1388e..8ec666c3433 100644 --- a/http-client.md +++ b/http-client.md @@ -382,7 +382,9 @@ If you would like to fake a sequence of responses but do not need to specify a s If you require more complicated logic to determine what responses to return for certain endpoints, you may pass a closure to the `fake` method. This closure will receive an instance of `Illuminate\Http\Client\Request` and should return a response instance. Within your closure, you may perform whatever logic is necessary to determine what type of response to return: - Http::fake(function ($request) { + use Illuminate\Http\Client\Request; + + Http::fake(function (Request $request) { return Http::response('Hello World', 200); }); From 59d9f4391780be95b9975bbb53a647cf576b98cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Debrauwer?= Date: Tue, 5 Apr 2022 19:23:17 +0200 Subject: [PATCH 0118/2609] [9.x] Http client: document retry method $request parameter (#7849) * Document retry method $request parameter * Small fix in example * Update http-client.md Co-authored-by: Taylor Otwell --- http-client.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index 8ec666c3433..2fdd3acc965 100644 --- a/http-client.md +++ b/http-client.md @@ -181,10 +181,22 @@ If you would like HTTP client to automatically retry the request if a client or If needed, you may pass a third argument to the `retry` method. The third argument should be a callable that determines if the retries should actually be attempted. For example, you may wish to only retry the request if the initial request encounters an `ConnectionException`: - $response = Http::retry(3, 100, function ($exception) { + $response = Http::retry(3, 100, function ($exception, $request) { return $exception instanceof ConnectionException; })->post(...); +If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: + + $response = Http::withToken($this->getToken())->retry(2, 0, function ($exception, $request) { + if (! $exception instanceof RequestException || $request->response->status() !== 401) { + return false; + } + + $request->withToken($this->getNewToken()); + + return true; + })->post(...); + If all of the requests fail, an instance of `Illuminate\Http\Client\RequestException` will be thrown. If you would like to disable this behavior, you may provide a `throw` argument with a value of `false`. When disabled, the last response received by the client will be returned after all retries have been attempted: $response = Http::retry(3, 100, throw: false)->post(...); From b617b820c694a7aab20a69b42a3b3f33603a3938 Mon Sep 17 00:00:00 2001 From: Joel Butcher Date: Tue, 5 Apr 2022 18:26:21 +0100 Subject: [PATCH 0119/2609] [9.x] Document Valet Subdomains (#7859) * document subdomain routing * wip * formatting Co-authored-by: Taylor Otwell --- valet.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/valet.md b/valet.md index 2f4fb4f730f..3fff62f8927 100644 --- a/valet.md +++ b/valet.md @@ -172,6 +172,12 @@ cd ~/Sites/laravel valet link application ``` +Of course, you may also serve applications on subdomains using the `link` command: + +```shell +valet link api.application +``` + You may execute the `links` command to display a list of all of your linked directories: ```shell From 7e7b5b30eac117b6436c96bc863b35dc0635d46c Mon Sep 17 00:00:00 2001 From: Marinario Agalliu <53918165+marinarioagalliu@users.noreply.github.com> Date: Tue, 5 Apr 2022 19:28:34 +0200 Subject: [PATCH 0120/2609] Update eloquent.md (#7858) Add missing 'restored' function in docs --- eloquent.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/eloquent.md b/eloquent.md index de6196c6aa1..c401ff3fbb1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1383,6 +1383,17 @@ This command will place the new observer in your `App/Observers` directory. If t { // } + + /** + * Handle the User "restored" event. + * + * @param \App\Models\User $user + * @return void + */ + public function restored(User $user) + { + // + } /** * Handle the User "forceDeleted" event. From 5cae1636d29df8dc734617f1108129403b96d92e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Apr 2022 13:04:51 -0500 Subject: [PATCH 0121/2609] wip --- helpers.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/helpers.md b/helpers.md index bbb1cd0e8d0..bbb2d968b21 100644 --- a/helpers.md +++ b/helpers.md @@ -133,6 +133,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::singular](#method-str-singular) [Str::slug](#method-str-slug) [Str::snake](#method-snake-case) +[Str::squish](#method-str-squish) [Str::start](#method-str-start) [Str::startsWith](#method-starts-with) [Str::studly](#method-studly-case) @@ -211,6 +212,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [slug](#method-fluent-str-slug) [snake](#method-fluent-str-snake) [split](#method-fluent-str-split) +[squish](#method-fluent-str-squish) [start](#method-fluent-str-start) [startsWith](#method-fluent-str-starts-with) [studly](#method-fluent-str-studly) @@ -1757,6 +1759,17 @@ The `Str::snake` method converts the given string to `snake_case`: // foo-bar + +#### `Str::squish()` {.collection-method} + +The `Str::squish` method remove all extraneous white space from a string, including extraneous white space between words: + + use Illuminate\Support\Str; + + $string = Str::squish(' laravel framework '); + + // laravel framework + #### `Str::start()` {.collection-method} @@ -2715,6 +2728,17 @@ The `split` method splits a string into a collection using a regular expression: // collect(["one", "two", "three"]) + +#### `squish` {.collection-method} + +The `squish` method remove all extraneous white space from a string, including extraneous white space between words: + + use Illuminate\Support\Str; + + $string = Str::of(' laravel framework ')->squish(); + + // laravel framework + #### `start` {.collection-method} From 9569c5c4cc62faefccadcb748c09a1fc0441c671 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Apr 2022 13:08:22 -0500 Subject: [PATCH 0122/2609] wip --- eloquent-relationships.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 74ab28b03b5..a31aa6af7a3 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -292,6 +292,12 @@ However, you may find it more convenient to use the `whereBelongsTo` method, whi $posts = Post::whereBelongsTo($user)->get(); +You may also provide a [collection](/docs/{{version}}/eloquent-collections) instance to the `whereBelongsTo` method. When doing so, Laravel will retrieve models that belong to any of the parent models within the collection: + + $users = User::where('vip', true)->get(); + + $posts = Post::whereBelongsTo($users)->get(); + By default, Laravel will determine the relationship associated with the given model based on the class name of the model; however, you may specify the relationship name manually by providing it as the second argument to the `whereBelongsTo` method: $posts = Post::whereBelongsTo($user, 'author')->get(); From a5f2c6f8dbdd0846974f300a8150beba99205aab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Apr 2022 13:11:34 -0500 Subject: [PATCH 0123/2609] wip --- routing.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing.md b/routing.md index c6000632707..896ae8799c7 100644 --- a/routing.md +++ b/routing.md @@ -221,6 +221,10 @@ For convenience, some commonly used regular expression patterns have helper meth // })->whereUuid('id'); + Route::get('/category/{category}', function ($category) { + // + })->whereIn(['movie', 'song', 'painting']); + If the incoming request does not match the route pattern constraints, a 404 HTTP response will be returned. From b0bd2b49b3c25271744dbc648a74d1f3a3a9cb8c Mon Sep 17 00:00:00 2001 From: spebon <97607715+spebon@users.noreply.github.com> Date: Wed, 6 Apr 2022 14:33:58 +0000 Subject: [PATCH 0124/2609] Clarify exists rule usage (#7860) * Clarify exists rule usage Add missing docs on how to use `Rule::exists` with a column name. I assumed (based on the section title `exists:table,column`) that the syntax was `Rule::exists('table,column')` instead of two separate arguments. Others having the same issue: https://stackoverflow.com/questions/43115683/undefined-offset-1-error-for-laravel-validation. * Update validation.md Co-authored-by: Taylor Otwell --- validation.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/validation.md b/validation.md index 599aa5ccbce..eeff3cd3ae0 100644 --- a/validation.md +++ b/validation.md @@ -1160,6 +1160,10 @@ If you would like to customize the query executed by the validation rule, you ma ], ]); +You may explicitly specify the database column name that should be used by the `exists` rule generated by the `Rule::exists` method by providing the column name as the second argument to the `exists` method: + + 'state' => Rule::exists('states', 'abbreviation'), + #### file From c874a5c71d2fe5d2555ce344b06db1842e5933fb Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 6 Apr 2022 17:35:44 +0200 Subject: [PATCH 0125/2609] Update contributions.md (#7861) --- contributions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributions.md b/contributions.md index 2f918eb0dfd..223a08c0317 100644 --- a/contributions.md +++ b/contributions.md @@ -14,7 +14,7 @@ ## Bug Reports -To encourage active collaboration, Laravel strongly encourages pull requests, not just bug reports. "Bug reports" may also be sent in the form of a pull request containing a failing test. Pull requests will only be reviewed when marked as "ready for review" (not in the "draft" state) and all tests for new features are passing. Lingering, non-active pull requests left in the "draft" state will be closed after a few days. +To encourage active collaboration, Laravel strongly encourages pull requests, not just bug reports. Pull requests will only be reviewed when marked as "ready for review" (not in the "draft" state) and all tests for new features are passing. Lingering, non-active pull requests left in the "draft" state will be closed after a few days. However, if you file a bug report, your issue should contain a title and a clear description of the issue. You should also include as much relevant information as possible and a code sample that demonstrates the issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix. From b99e084235b077bb01435193d0c889ec08fb4045 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Apr 2022 13:27:04 -0500 Subject: [PATCH 0126/2609] wip --- views.md | 1 - 1 file changed, 1 deletion(-) diff --git a/views.md b/views.md index 68ff7b776fe..0df245f8164 100644 --- a/views.md +++ b/views.md @@ -205,7 +205,6 @@ Now that we have registered the composer, the `compose` method of the `App\View\ */ public function __construct(UserRepository $users) { - // Dependencies are automatically resolved by the service container... $this->users = $users; } From 33f9e674fdef33b481a6e42e2e3e7d9c819a4ee6 Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Thu, 7 Apr 2022 15:03:39 +0100 Subject: [PATCH 0127/2609] Update helpers.md (#7862) --- helpers.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/helpers.md b/helpers.md index bbb2d968b21..9a12bb0083e 100644 --- a/helpers.md +++ b/helpers.md @@ -1251,7 +1251,7 @@ The `Str::between` method returns the portion of a string between two values: $slice = Str::between('This is my name', 'This', 'name'); // ' is my ' - + #### `Str::betweenFirst()` {.collection-method} @@ -1439,7 +1439,7 @@ The `Str::kebab` method converts the given string to `kebab-case`: $converted = Str::kebab('fooBar'); // foo-bar - + #### `Str::lcfirst()` {.collection-method} @@ -1762,7 +1762,7 @@ The `Str::snake` method converts the given string to `snake_case`: #### `Str::squish()` {.collection-method} -The `Str::squish` method remove all extraneous white space from a string, including extraneous white space between words: +The `Str::squish` method removes all extraneous white space from a string, including extraneous white space between words: use Illuminate\Support\Str; @@ -1842,10 +1842,10 @@ The `Str::substrReplace` method replaces text within a portion of a string, star use Illuminate\Support\Str; - $result = Str::substrReplace('1300', ':', 2); + $result = Str::substrReplace('1300', ':', 2); // 13: - - $result = Str::substrReplace('1300', ':', 2, 0); + + $result = Str::substrReplace('1300', ':', 2, 0); // 13:00 @@ -2079,7 +2079,7 @@ The `between` method returns the portion of a string between two values: $converted = Str::of('This is my name')->between('This', 'name'); // ' is my ' - + #### `betweenFirst` {.collection-method} @@ -2323,7 +2323,7 @@ The `kebab` method converts the given string to `kebab-case`: $converted = Str::of('fooBar')->kebab(); // foo-bar - + #### `lcfirst` {.collection-method} @@ -2731,7 +2731,7 @@ The `split` method splits a string into a collection using a regular expression: #### `squish` {.collection-method} -The `squish` method remove all extraneous white space from a string, including extraneous white space between words: +The `squish` method removes all extraneous white space from a string, including extraneous white space between words: use Illuminate\Support\Str; @@ -2884,7 +2884,7 @@ The `ucfirst` method returns the given string with the first character capitaliz $string = Str::of('foo bar')->ucfirst(); // Foo bar - + #### `ucsplit` {.collection-method} From 113a5d1e9087fb853587116ac6d19303ab183b3c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Apr 2022 17:04:43 -0500 Subject: [PATCH 0128/2609] refine package docs --- packages.md | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/packages.md b/packages.md index 83248a5dac9..6e667aec555 100644 --- a/packages.md +++ b/packages.md @@ -252,36 +252,57 @@ 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 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: +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 Courier\Components\Alert; - use Courier\Components\Button; + use Illuminate\Support\Facades\Blade; + use VendorPackage\View\Components\AlertComponent; /** - * Bootstrap any package services. + * Bootstrap your package's services. * * @return void */ public function boot() { - $this->loadViewComponentsAs('courier', [ - Alert::class, - Button::class, - ]); + Blade::component('package-alert', AlertComponent::class); } -Once your view components are registered in a service provider, you may reference them in your view like so: +Once your component has been registered, it may be rendered using its tag alias: ```blade - + +``` + + +#### Autoloading Package Components + +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 `Nightshade\Views\Components` namespace: - + use Illuminate\Support\Facades\Blade; + + /** + * Bootstrap your package's services. + * + * @return void + */ + 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 + + ``` +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. + #### Anonymous Components -If your package contains anonymous components, they must be placed within a `components` directory of your package's "views" directory (as specified by `loadViewsFrom`). Then, you may render them by prefixing the component name with the package's view namespace: +If your package contains anonymous components, they must be placed within a `components` directory of your package's "views" directory (as specified by the [`loadViewsFrom` method](#views)). Then, you may render them by prefixing the component name with the package's view namespace: ```blade From 61a030645996c8bcc14557c1960bcc97e360b4a2 Mon Sep 17 00:00:00 2001 From: Deividas Date: Fri, 8 Apr 2022 16:59:17 +0300 Subject: [PATCH 0129/2609] onlyInput() method pass values as single array (#7863) onlyInput() uses PHP func_get_args() which receives value as ARRAY and converts to ARRAY. onlyInput(['email']) - converts to multi-dimensional array. (latter in framework Arr::set() breaks code to TypeError) onlyInput('email') - converts to simple array. --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index 6f295cb618c..596a508732b 100644 --- a/authentication.md +++ b/authentication.md @@ -251,7 +251,7 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ return back()->withErrors([ 'email' => 'The provided credentials do not match our records.', - ])->onlyInput(['email']); + ])->onlyInput('email'); } } From 5b80d16231e24023d3004397de016330ada57e65 Mon Sep 17 00:00:00 2001 From: Joseph Silber Date: Fri, 8 Apr 2022 14:20:51 -0400 Subject: [PATCH 0130/2609] Document the eager `Collection`'s `lazy()` method (#7866) * Document the eager `Collecction`'s `lazy()` method * formatting Co-authored-by: Taylor Otwell --- collections.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/collections.md b/collections.md index 4481bbdc7d0..24a332b5af1 100644 --- a/collections.md +++ b/collections.md @@ -138,6 +138,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [keyBy](#method-keyby) [keys](#method-keys) [last](#method-last) +[lazy](#method-lazy) [macro](#method-macro) [make](#method-make) [map](#method-map) @@ -1229,6 +1230,31 @@ You may also call the `last` method with no arguments to get the last element in // 4 + +#### `lazy()` {.collection-method} + +The `lazy` method returns a new [`LazyCollection`](#lazy-collections) instance from the underlying array of items: + + $lazyCollection = collect([1, 2, 3, 4])->lazy(); + + get_class($lazyCollection); + + // Illuminate\Support\LazyCollection + + $lazyCollection->all(); + + // [1, 2, 3, 4] + +This is especially useful when you need to perform transformations on a huge `Collection` that contains many items: + + $count = $hugeCollection() + ->lazy() + ->where('country', 'FR') + ->where('balance', '>', '100') + ->count(); + +By converting the collection to a `LazyCollection`, though the original values are all held in memory, subsequent filters do not need to store their values in memory. Therefore, virtually no additional memory will be allocated when filtering the collection's results. + #### `macro()` {.collection-method} From 251e5e57fbb9df3872c29a3e858430859c53a07c Mon Sep 17 00:00:00 2001 From: Khalil Bouzidi <49455868+Safemood@users.noreply.github.com> Date: Fri, 8 Apr 2022 20:18:58 +0100 Subject: [PATCH 0131/2609] add tabPrice for multiple items (#7865) * add tabPrice for multiple items add the `tabPrice` method to the documentation for multiple items per invoice. * Update billing.md * Update billing.md * Update billing.md Co-authored-by: Taylor Otwell --- billing.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index b4bc82f7fcc..66bf568cdfe 100644 --- a/billing.md +++ b/billing.md @@ -1509,13 +1509,19 @@ The invoice will be immediately charged against the user's default payment metho 'default_tax_rates' => ['txr_id'], ]); +Similarly to `invoicePrice`, you may use the `tabPrice` method to create a one-time charge for multiple items (up to 250 items per invoice) by adding them to the customer's "tab" and then invoicing the customer. For example, we may invoice a customer for five shirts and two mugs: + + $user->tabPrice('price_tshirt', 5); + $user->tabPrice('price_mug', 2); + $user->invoice(); + Alternatively, you may use the `invoiceFor` method to make a "one-off" charge against the customer's default payment method: $user->invoiceFor('One Time Fee', 500); -Although the `invoiceFor` method is available for you to use, it is recommendeded that you use the `invoicePrice` method with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. +Although the `invoiceFor` method is available for you to use, it is recommendeded that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. -> {note} The `invoicePrice` and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. +> {note} The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. ### Refunding Charges From cf5df4add1cbc1fd1131a91240ea689c8fe8ee76 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Apr 2022 16:22:13 -0500 Subject: [PATCH 0132/2609] wip --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 9f490b12f08..3927a2ca805 100644 --- a/installation.md +++ b/installation.md @@ -22,7 +22,7 @@ Laravel is a web application framework with expressive, elegant syntax. A web fr Laravel strives to provide an amazing developer experience while providing powerful features such as thorough dependency injection, an expressive database abstraction layer, queues and scheduled jobs, unit and integration testing, and more. -Whether you are new to PHP or web frameworks or have years of experience, Laravel is a framework that can grow with you. We'll help you take your first steps as a web developer or give you a boost as you take your expertise to the next level. We can't wait to see what you build. +Whether you are new to PHP web frameworks or have years of experience, Laravel is a framework that can grow with you. We'll help you take your first steps as a web developer or give you a boost as you take your expertise to the next level. We can't wait to see what you build. ### Why Laravel? From 11f69966adeee399f4f9aab55f3cd579606c43e2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Apr 2022 16:23:49 -0500 Subject: [PATCH 0133/2609] wip --- starter-kits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starter-kits.md b/starter-kits.md index 0be65dc629e..fc4704b34be 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Laravel Breeze](#laravel-breeze) - [Installation](#laravel-breeze-installation) - - [Breeze & Inertia](#breeze-and-inertia) + - [Breeze & React / Vue](#breeze-and-inertia) - [Breeze & Next.js / API](#breeze-and-next) - [Laravel Jetstream](#laravel-jetstream) From f77fea730716c039df50d0fede4ca5a826d8398c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Apr 2022 16:28:18 -0500 Subject: [PATCH 0134/2609] working on installation docs --- installation.md | 2 +- starter-kits.md | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/installation.md b/installation.md index 3927a2ca805..9e0c3bfcc2a 100644 --- a/installation.md +++ b/installation.md @@ -267,7 +267,7 @@ How you want to use Laravel will also dictate the next steps on your journey. Th ### Laravel The Full Stack Framework -Laravel may serve as a full stack framework. By "full stack" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or using a single-page application hybrid technology like [Inertia.js](https://inertiajs.com). This is the most common way to use the Laravel framework. +Laravel may serve as a full stack framework. By "full stack" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or a single-page application hybrid technology like [Inertia.js](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel. If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://laravel-livewire.com) and [Inertia.js](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications. diff --git a/starter-kits.md b/starter-kits.md index fc4704b34be..9fd1b8efc9e 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -55,14 +55,16 @@ Next, you may navigate to your application's `/login` or `/register` URLs in you > {tip} To learn more about compiling your application's CSS and JavaScript, check out the [Laravel Mix documentation](/docs/{{version}}/mix#running-mix). -### Breeze & Inertia +### Breeze & React / Vue -Laravel Breeze also offers an [Inertia.js](https://inertiajs.com) frontend implementation powered by Vue or React. To use an Inertia stack, specify `vue` or `react` as your desired stack when executing the `breeze:install` Artisan command: +Laravel Breeze also offers React and Vue scaffolding via an [Inertia.js](https://inertiajs.com) frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers. + +Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel. To use an Inertia stack, specify `vue` or `react` as your desired stack when executing the `breeze:install` Artisan command: ```shell php artisan breeze:install vue -// Or... +# Or... php artisan breeze:install react From adc50cbe2d4fa9e29c25bf38f0123e0d700ed899 Mon Sep 17 00:00:00 2001 From: Bilal Al-Massry <33354606+bilalalmassry@users.noreply.github.com> Date: Mon, 11 Apr 2022 10:35:07 +0300 Subject: [PATCH 0135/2609] fix alphabetical order (#7869) --- validation.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/validation.md b/validation.md index eeff3cd3ae0..732d8929b19 100644 --- a/validation.md +++ b/validation.md @@ -807,10 +807,10 @@ Below is a list of all available validation rules and their function: [In Array](#rule-in-array) [Integer](#rule-integer) [IP Address](#rule-ip) -[MAC Address](#rule-mac) [JSON](#rule-json) [Less Than](#rule-lt) [Less Than Or Equal](#rule-lte) +[MAC Address](#rule-mac) [Max](#rule-max) [MIME Types](#rule-mimetypes) [MIME Type By File Extension](#rule-mimes) @@ -1248,11 +1248,6 @@ The field under validation must be an IPv4 address. The field under validation must be an IPv6 address. - -#### mac_address - -The field under validation must be a MAC address. - #### json @@ -1268,6 +1263,11 @@ The field under validation must be less than the given _field_. The two fields m The field under validation must be less than or equal to the given _field_. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the [`size`](#rule-size) rule. + +#### mac_address + +The field under validation must be a MAC address. + #### max:_value_ From 4414ecba4468b15df196509e51e05278eafd5ddf Mon Sep 17 00:00:00 2001 From: adzlocal <98457067+adzlocal@users.noreply.github.com> Date: Mon, 11 Apr 2022 16:06:28 +0200 Subject: [PATCH 0136/2609] [9.x] Added undocumented retention policy for daily log channel (#7871) * added documentation for previously undocumented `days` setting for `daily` log channel * Update logging.md Co-authored-by: Taylor Otwell --- logging.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/logging.md b/logging.md index 02d886d1352..e966dcfe5a3 100644 --- a/logging.md +++ b/logging.md @@ -75,6 +75,12 @@ Name | Description | Default `locking` | Attempt to lock the log file before writing to it | `false` `permission` | The log file's permissions | `0644` +Additionally, the retention policy for the `daily` channel can be configured via the `days` option: + +Name | Description | Default +------------- |-------------------------------------------------------------------| ------------- +`days` | The number of days that daily log files should be retained | `7` + #### Configuring The Papertrail Channel From 349078e866b45fe1a324669b4db629b9998d22f2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Apr 2022 09:14:06 -0500 Subject: [PATCH 0137/2609] wip --- migrations.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/migrations.md b/migrations.md index 9d34697ef8d..2820576c9ae 100644 --- a/migrations.md +++ b/migrations.md @@ -104,20 +104,6 @@ Within both of these methods, you may use the Laravel schema builder to expressi } }; - -#### Anonymous Migrations - -As you may have noticed in the example above, Laravel will automatically assign a class name to all of the migrations that you generate using the `make:migration` command. However, if you wish, you may return an anonymous class from your migration file. This is primarily useful if your application accumulates many migrations and two of them have a class name collision: - - #### Setting The Migration Connection From e564112f8c03b71cabf8a11aab507d287b6124f6 Mon Sep 17 00:00:00 2001 From: Yoeri Boven Date: Mon, 11 Apr 2022 16:27:34 +0200 Subject: [PATCH 0138/2609] Update routing.md (#7872) --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 896ae8799c7..f3f65062200 100644 --- a/routing.md +++ b/routing.md @@ -223,7 +223,7 @@ For convenience, some commonly used regular expression patterns have helper meth Route::get('/category/{category}', function ($category) { // - })->whereIn(['movie', 'song', 'painting']); + })->whereIn('category', ['movie', 'song', 'painting']); If the incoming request does not match the route pattern constraints, a 404 HTTP response will be returned. From 81d679cde7c4e3a9811e7378336a0e535fbc35d1 Mon Sep 17 00:00:00 2001 From: Riaan du Plessis Date: Mon, 11 Apr 2022 17:40:26 +0200 Subject: [PATCH 0139/2609] Updated container 'extend' method description (#7867) The method was described as only having one argument, the closure. This is not true as its first argument is the service class that is being extended. I believe the complex sentence might be why it has been overlooked for so long, so I simplified it and corrected the description to provide details on both arguments required by the method. --- container.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.md b/container.md index f1ae9043b64..1a20681863a 100644 --- a/container.md +++ b/container.md @@ -339,7 +339,7 @@ Once the services have been tagged, you may easily resolve them all via the cont ### Extending Bindings -The `extend` method allows the modification of resolved services. For example, when a service is resolved, you may run additional code to decorate or configure the service. The `extend` method accepts a closure, which should return the modified service, as its only argument. The closure receives the service being resolved and the container instance: +The `extend` method allows the modification of resolved services. For example, when a service is resolved, you may run additional code to decorate or configure the service. The `extend` method accepts two arguments, the service class you're extending and a closure that should return the modified service. The closure receives the service being resolved and the container instance: $this->app->extend(Service::class, function ($service, $app) { return new DecoratedService($service); From 57d5012de73229f33a11da6889cc316968e4b090 Mon Sep 17 00:00:00 2001 From: Joseph Silber Date: Mon, 11 Apr 2022 13:53:24 -0400 Subject: [PATCH 0140/2609] Improve `lazy()` docs (#7873) --- collections.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collections.md b/collections.md index 24a332b5af1..e01898baf96 100644 --- a/collections.md +++ b/collections.md @@ -1247,13 +1247,13 @@ The `lazy` method returns a new [`LazyCollection`](#lazy-collections) instance f This is especially useful when you need to perform transformations on a huge `Collection` that contains many items: - $count = $hugeCollection() + $count = $hugeCollection ->lazy() ->where('country', 'FR') ->where('balance', '>', '100') ->count(); -By converting the collection to a `LazyCollection`, though the original values are all held in memory, subsequent filters do not need to store their values in memory. Therefore, virtually no additional memory will be allocated when filtering the collection's results. +By converting the collection to a `LazyCollection`, we avoid having to allocate a ton of additional memory. Though the original collection still keeps _its_ values in memory, the subsequent filters will not. Therefore, virtually no additional memory will be allocated when filtering the collection's results. #### `macro()` {.collection-method} From e280b9da65191fe4cab02f2db74733c9462cebb0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Apr 2022 09:18:10 -0500 Subject: [PATCH 0141/2609] document old helper update --- helpers.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/helpers.md b/helpers.md index 9a12bb0083e..42dcb519b39 100644 --- a/helpers.md +++ b/helpers.md @@ -3487,6 +3487,14 @@ The `old` function [retrieves](/docs/{{version}}/requests#retrieving-input) an [ $value = old('value', 'default'); +Since the "default value" provided as the second argument to the `old` function is often an attribute of an Eloquent model, Laravel allows you to simply pass the entire Eloquent model as the second argument to the `old` function. When doing so, Laravel will assume the first argument provided to the `old` function is the name of the Eloquent attribute that should be considered the "default value": + + {{ old('name', $user->name) }} + + // Is equivalent to... + + {{ old('name', $user) }} + #### `optional()` {.collection-method} From 276ff5495f2fa989a48b181d2eef3605baa0a4a8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Apr 2022 09:22:55 -0500 Subject: [PATCH 0142/2609] document scalar method --- database.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/database.md b/database.md index ec7da3c7023..f55dfbce2d7 100644 --- a/database.md +++ b/database.md @@ -153,6 +153,15 @@ The `select` method will always return an `array` of results. Each result within echo $user->name; } + +#### Selecting Scalar Values + +Sometimes your database query may result in a single, scalar value. Instead of being required to retrieve the query's scalar result from a record object, Laravel allows you to retrieve this value directly using the `scalar` method: + + $burgers = DB::scalar( + "select count(case when food = 'burger' then 1 end) as burgers from menu" + ); + #### Using Named Bindings From 0d2d20957b3ba330d8911f7143a9ae89aa3fe6a1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Apr 2022 09:35:49 -0500 Subject: [PATCH 0143/2609] document log levels --- errors.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/errors.md b/errors.md index 94295e68cb4..e8d4fe8d1fd 100644 --- a/errors.md +++ b/errors.md @@ -4,6 +4,7 @@ - [Configuration](#configuration) - [The Exception Handler](#the-exception-handler) - [Reporting Exceptions](#reporting-exceptions) + - [Exception Log Levels](#exception-log-levels) - [Ignoring Exceptions By Type](#ignoring-exceptions-by-type) - [Rendering Exceptions](#rendering-exceptions) - [Reportable & Renderable Exceptions](#renderable-exceptions) @@ -117,6 +118,27 @@ Sometimes you may need to report an exception but continue handling the current } } + +### Exception Log Levels + +When messages are written to your application's [logs](/docs/{{version}}/logging), the messages are written at a specified [log level](/docs/{{version}}/logging#log-levels), which indicates the severity or importance of the message being logged. + +As noted above, even when you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application; however, since the log level can sometimes influence the channels on which a message is logged, you may wish to configure the log level that certain exceptions are logged at. + +To accomplish this, you may specify an array of exception types and their associated log levels within your application's exception handler's `$levels` property: + + use PDOException; + use Psr\Log\LogLevel; + + /** + * A list of exception types with their corresponding custom log levels. + * + * @var array, \Psr\Log\LogLevel::*> + */ + protected $levels = [ + PDOException::class => LogLevel::CRITICAL, + ]; + ### Ignoring Exceptions By Type @@ -125,9 +147,9 @@ When building your application, there will be some types of exceptions you simpl use App\Exceptions\InvalidOrderException; /** - * A list of the exception types that should not be reported. + * A list of the exception types that are not reported. * - * @var array + * @var array> */ protected $dontReport = [ InvalidOrderException::class, From eb57436ca82e353ac603215888dde012e3b521f2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Apr 2022 09:36:53 -0500 Subject: [PATCH 0144/2609] wip --- errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors.md b/errors.md index e8d4fe8d1fd..513c6965dfb 100644 --- a/errors.md +++ b/errors.md @@ -125,7 +125,7 @@ When messages are written to your application's [logs](/docs/{{version}}/logging As noted above, even when you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application; however, since the log level can sometimes influence the channels on which a message is logged, you may wish to configure the log level that certain exceptions are logged at. -To accomplish this, you may specify an array of exception types and their associated log levels within your application's exception handler's `$levels` property: +To accomplish this, you may define an array of exception types and their associated log levels within the `$levels` property of your application's exception handler: use PDOException; use Psr\Log\LogLevel; From a259dc1af8acb799aa9b49dbf5c6da81b18ed3cf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Apr 2022 09:52:09 -0500 Subject: [PATCH 0145/2609] anon component namespaces --- blade.md | 131 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 80 insertions(+), 51 deletions(-) diff --git a/blade.md b/blade.md index ff13a181d3c..ad614f3378c 100644 --- a/blade.md +++ b/blade.md @@ -22,9 +22,13 @@ - [Reserved Keywords](#reserved-keywords) - [Slots](#slots) - [Inline Component Views](#inline-component-views) - - [Anonymous Components](#anonymous-components) - [Dynamic Components](#dynamic-components) - [Manually Registering Components](#manually-registering-components) +- [Anonymous Components](#anonymous-components) + - [Anonymous Index Components](#anonymous-index-components) + - [Data Properties / Attributes](#data-properties-attributes) + - [Accessing Parent Data](#accessing-parent-data) + - [Anonymous Components Namespaces](#anonymous-component-namespaces) - [Building Layouts](#building-layouts) - [Layouts Using Components](#layouts-using-components) - [Layouts Using Template Inheritance](#layouts-using-template-inheritance) @@ -1157,8 +1161,70 @@ To create a component that renders an inline view, you may use the `inline` opti php artisan make:component Alert --inline ``` + +### Dynamic Components + +Sometimes you may need to render a component but not know which component should be rendered until runtime. In this situation, you may use Laravel's built-in `dynamic-component` component to render the component based on a runtime value or variable: + +```blade + +``` + + +### Manually Registering Components + +> {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. + +When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. + +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; + + /** + * Bootstrap your package's services. + * + * @return void + */ + public function boot() + { + Blade::component('package-alert', AlertComponent::class); + } + +Once your component has been registered, it may be rendered using its tag alias: + +```blade + +``` + +#### Autoloading Package Components + +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. + * + * @return void + */ + 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 + + +``` + +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. + -### Anonymous Components +## 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`, you may simply render it like so: @@ -1173,7 +1239,7 @@ You may use the `.` character to indicate if a component is nested deeper inside ``` -#### Anonymous Index Components +### Anonymous Index Components Sometimes, when a component is made up of many Blade templates, you may wish to group the given component's templates within a single directory. For example, imagine an "accordion" component with the following directory structure: @@ -1202,7 +1268,7 @@ Thankfully, Blade allows you to place an `index.blade.php` file within a compone ``` -#### Data Properties / Attributes +### Data Properties / Attributes Since anonymous components do not have any associated class, you may wonder how you may differentiate which data should be passed to the component as variables and which attributes should be placed in the component's [attribute bag](#component-attributes). @@ -1225,7 +1291,7 @@ Given the component definition above, we may render the component like so: ``` -#### Accessing Parent Data +### Accessing Parent Data Sometimes you may want to access data from a parent component inside a child component. In these cases, you may use the `@aware` directive. For example, imagine we are building a complex menu component consisting of a parent `` and child ``: @@ -1262,68 +1328,31 @@ Because the `color` prop was only passed into the parent (``), it won't > {note} The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. - -### Dynamic Components - -Sometimes you may need to render a component but not know which component should be rendered until runtime. In this situation, you may use Laravel's built-in `dynamic-component` component to render the component based on a runtime value or variable: - -```blade - -``` - - -### Manually Registering Components - -> {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. - -When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. - -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; - - /** - * Bootstrap your package's services. - * - * @return void - */ - public function boot() - { - Blade::component('package-alert', AlertComponent::class); - } + +### Anonymous Component Namespaces -Once your component has been registered, it may be rendered using its tag alias: +As previously discussed, anonymous components are typically defined by placing a Blade template within your `resources/views/components` directory. However, you may occasionally want to register other anonymous component paths with Laravel in addition to the default path. -```blade - -``` +For example, when building a vacation booking application, you may wish to place flight booking related anonymous components within a `resources/views/flights/bookings/components` directory. To inform Laravel of this anonymous component location, you may use the `anonymousComponentNamespace` method provided by the `Blade` facade. -#### Autoloading Package Components - -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; +The `anonymousComponentNamespace` method accepts the "path" to the anonymous component location as its first argument and the "namespace" that components should be placed under as its second argument. As you will see in the example below, the "namespace" will be prefixed to the component's name when the component is rendered. Typically, this method should be called from the `boot` method of one of your application's [service providers](/docs/{{version}}/providers): /** - * Bootstrap your package's services. + * Bootstrap any application services. * * @return void */ public function boot() { - Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); + Blade::anonymousComponentNamespace('flights.bookings', 'flights'); } -This will allow the usage of package components by their vendor namespace using the `package-name::` syntax: +Given the example above, you may render a `panel` component that exists within the newly registered component directory like so: ```blade - - + ``` -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. - ## Building Layouts From 4420e12557e50c103b527591d9d65bdbc11e0bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20H=C3=BCneburg?= Date: Tue, 12 Apr 2022 18:54:16 +0200 Subject: [PATCH 0146/2609] Add docs for `waitForEvent` method (#7874) * Add docs for `waitForEvent` method Adds a section to the Dusk documentation describing the new `waitForEvent` method added in laravel/dusk#972. * formatting Co-authored-by: Taylor Otwell --- dusk.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index e3944afa126..0e96f3c8d58 100644 --- a/dusk.md +++ b/dusk.md @@ -825,7 +825,7 @@ You may also wait for a [named route's](/docs/{{version}}/routing#named-routes) $browser->waitForRoute($routeName, $parameters); -#### Waiting for Page Reloads +#### Waiting For Page Reloads If you need to wait for a page to reload after performing an action, use the `waitForReload` method: @@ -863,6 +863,32 @@ The `waitUntilVue` and `waitUntilVueIsNot` methods may be used to wait until a [ // Wait until the component attribute doesn't contain the given value... $browser->waitUntilVueIsNot('user.name', null, '@user'); + +#### Waiting For JavaScript Events + +The `waitForEvent` method can be used to pause the execution of a test until a JavaScript event occurs: + + $browser->waitForEvent('load'); + +The event listener is attached to the current scope, which is the `body` element by default. When using a scoped selector, the event listener will be attached to the matching element: + + $browser->with('iframe', function ($iframe) { + // Wait for the iframe's load event... + $iframe->waitForEvent('load'); + }); + +You may also provide a selector as the second argument to the `waitForEvent` method to attach the event listener to a specific element: + + $browser->waitForEvent('load', '.selector'); + +You may also wait for events on the `document` and `window` objects: + + // Wait until the document is scrolled... + $browser->waitForEvent('scroll', 'document'); + + // Wait a maximum of five seconds until the window is resized... + $browser->waitForEvent('resize', 'window', 5); + #### Waiting With A Callback From 936783c90bccd5969c1bd6dba0ba7a9cd46beaa7 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Wed, 13 Apr 2022 15:49:59 +0200 Subject: [PATCH 0147/2609] Update blade.md (#7875) --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index ad614f3378c..16f58a2e44a 100644 --- a/blade.md +++ b/blade.md @@ -1344,7 +1344,7 @@ The `anonymousComponentNamespace` method accepts the "path" to the anonymous com */ public function boot() { - Blade::anonymousComponentNamespace('flights.bookings', 'flights'); + Blade::anonymousComponentNamespace('flights.bookings.components', 'flights'); } Given the example above, you may render a `panel` component that exists within the newly registered component directory like so: From 5620fba4c45da601b0e36df1cb714ce02952f1f4 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 14 Apr 2022 15:44:56 +0200 Subject: [PATCH 0148/2609] [9.x] Add note about custom tld's (#7878) * Update valet.md * Update valet.md Co-authored-by: Taylor Otwell --- valet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valet.md b/valet.md index 3fff62f8927..578a77ab750 100644 --- a/valet.md +++ b/valet.md @@ -485,7 +485,7 @@ 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. +This directory contains all of Valet's Nginx site configurations. These files are rebuilt when running the `install` and `secure` commands. #### `~/.config/valet/Sites/` From 0f45487aed76bee9876ae730746ab5f8f3465629 Mon Sep 17 00:00:00 2001 From: Jason Lewis Date: Thu, 14 Apr 2022 23:16:56 +0930 Subject: [PATCH 0149/2609] [9.x] Fix code sample showing incorrect usage (#7877) Sample was showing `response` as a property on the recently added `$request` parameters when it is actually a parameter on `$exception` (when exception is an instance of `RequestException`. --- http-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index 2fdd3acc965..61dfe906ab1 100644 --- a/http-client.md +++ b/http-client.md @@ -188,7 +188,7 @@ If needed, you may pass a third argument to the `retry` method. The third argume If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: $response = Http::withToken($this->getToken())->retry(2, 0, function ($exception, $request) { - if (! $exception instanceof RequestException || $request->response->status() !== 401) { + if (! $exception instanceof RequestException || $exception->response->status() !== 401) { return false; } From e00f7eff72afc919e89e3f49f53ce40746dc76fd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 15 Apr 2022 14:06:47 -0500 Subject: [PATCH 0150/2609] update wording --- installation.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/installation.md b/installation.md index 9e0c3bfcc2a..fb4081922fd 100644 --- a/installation.md +++ b/installation.md @@ -48,9 +48,9 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ## Your First Laravel Project -We want it to be as easy as possible to get started with Laravel. There are a variety of options for developing and running a Laravel project on your own computer. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel project using [Docker](https://www.docker.com). +We want it to be as easy as possible to get started with Laravel. There are a variety of options for developing and running a Laravel project on your local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel project using [Docker](https://www.docker.com). -Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local computer's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your personal computer. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). +Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local machine's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your local machine. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. @@ -75,7 +75,7 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** +The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. @@ -104,7 +104,7 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** +The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. @@ -135,7 +135,7 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** +The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. @@ -161,7 +161,7 @@ curl -s "https://laravel.build/example-app?with=mysql,redis&devcontainer" | bash ### Installation Via Composer -If your computer already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: +If your local machine already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: ```shell composer create-project laravel/laravel example-app @@ -236,7 +236,7 @@ Laravel needs almost no additional configuration out of the box. You are free to ### Environment Based Configuration -Since many of Laravel's configuration option values may vary depending on whether your application is running on your local computer or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. +Since many of Laravel's configuration option values may vary depending on whether your application is running on your local machine or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. From 6231005b66d5d628d8fe6bcdcc1f0420db6c49ff Mon Sep 17 00:00:00 2001 From: Arnaud Lier Date: Sat, 16 Apr 2022 00:11:01 +0200 Subject: [PATCH 0151/2609] rearrange phrases in respective order (#7879) --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index 16f58a2e44a..dda532b55b2 100644 --- a/blade.md +++ b/blade.md @@ -342,7 +342,7 @@ In addition to conditional statements, Blade provides simple directives for work > {tip} While iterating through a `foreach` loop, 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 using the `@continue` and `@break` directives: +When using loops you may also skip the current iteration or end the loop using the `@continue` and `@break` directives: ```blade @foreach ($users as $user) From eff30c2e0f34c20ca7e08b07165e8a1babe46916 Mon Sep 17 00:00:00 2001 From: Tadhg Boyle Date: Mon, 18 Apr 2022 06:50:00 -0700 Subject: [PATCH 0152/2609] Improve `groupBy` preserveKeys argument syntax (#7880) I *believe* this was the intended syntax here, using PHP 8.0 named arguments. Apologies if I am incorrect :p Couldn't find any other places where this occurred. --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index e01898baf96..238644dc92f 100644 --- a/collections.md +++ b/collections.md @@ -1034,7 +1034,7 @@ Multiple grouping criteria may be passed as an array. Each array element will be $result = $data->groupBy(['skill', function ($item) { return $item['roles']; - }], $preserveKeys = true); + }], preserveKeys: true); /* [ From 72a1deea069e22db4777aee7dad08b0a78354211 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 19 Apr 2022 15:13:46 +0200 Subject: [PATCH 0153/2609] Fix description of Mailer return types (#7883) --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index dd9cdc9d86b..b94b96741ce 100644 --- a/upgrade.md +++ b/upgrade.md @@ -486,7 +486,7 @@ composer require symfony/postmark-mailer symfony/http-client #### Updated Return Types -The `send`, `html`, `text`, and `plain` methods no longer return the number of recipients that received the message. Instead, an instance of `Illuminate\Mail\SentMessage` is returned. This object contains an instance of `Symfony\Component\Mailer\SentMessage` that is accessible via the `getSymfonySentMessage` method or by dynamically invoking methods on the object. +The `send`, `html`, `raw`, and `plain` methods on `Illuminate\Mail\Mailer` no longer return `void`. Instead, an instance of `Illuminate\Mail\SentMessage` is returned. This object contains an instance of `Symfony\Component\Mailer\SentMessage` that is accessible via the `getSymfonySentMessage` method or by dynamically invoking methods on the object. #### Renamed "Swift" Methods From 90d0f954bd596f06afbc0d0507b14ce69d23c0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Farid=20Nogales=20L=C3=B3pez?= <34426099+FaruNL@users.noreply.github.com> Date: Tue, 19 Apr 2022 13:58:40 -0500 Subject: [PATCH 0154/2609] Fix description of Linux Host IP Configuration (#7885) * Fix description of Linux Host IP Configuration Since sail v1.10.1 the extra_hosts option makes the definition of the XDEBUG_CONFIG environment variable unnecessary for Linux. * Update sail.md * Update sail.md Co-authored-by: Taylor Otwell --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 85774bc63c8..0139a97a88d 100644 --- a/sail.md +++ b/sail.md @@ -416,7 +416,7 @@ SAIL_XDEBUG_MODE=develop,debug #### Linux Host IP Configuration -Internally, the `XDEBUG_CONFIG` environment variable is defined as `client_host=host.docker.internal` so that Xdebug will be properly configured for Mac and Windows (WSL2). If your local machine is running Linux, you will need to manually define this environment variable. +Internally, the `XDEBUG_CONFIG` environment variable is defined as `client_host=host.docker.internal` so that Xdebug will be properly configured for Mac and Windows (WSL2). If your local machine is running Linux, you should ensure that you are running Docker Engine 17.06.0+ and Compose 1.16.0+. Otherwise, you will need to manually define this environment variable as shown below. First, you should determine the correct host IP address to add to the environment variable by running the following command. Typically, the `` should be the name of the container that serves your application and often ends with `_laravel.test_1`: From 13adb4ca77b04b023133ba8abc675843c44f28c9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 19 Apr 2022 14:37:06 -0500 Subject: [PATCH 0155/2609] wip --- installation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/installation.md b/installation.md index fb4081922fd..dea895d1500 100644 --- a/installation.md +++ b/installation.md @@ -171,6 +171,8 @@ cd example-app php artisan serve ``` +Once you have started the Artisan development server, you may access your application at `http://localhost:8000`. + #### The Laravel Installer From ac08b4a896ca05e8951040f4fe78bc94eac15bba Mon Sep 17 00:00:00 2001 From: Marcial Paul Gargoles Date: Wed, 20 Apr 2022 23:42:31 +0800 Subject: [PATCH 0156/2609] Dashboard is always accessible (#7888) Dashboard is always accessible in local environment even if the gate "viewHorizon" is returning false. PS: I was not able to test my "viewHorizon" gate in my local environment, as the gate is ignored. xD --- horizon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/horizon.md b/horizon.md index 359fabb6902..8904ab86504 100644 --- a/horizon.md +++ b/horizon.md @@ -119,7 +119,7 @@ When the `balance` option is set to `false`, the default Laravel behavior will b ### Dashboard Authorization -Horizon exposes a dashboard at the `/horizon` URI. By default, you will only be able to access this dashboard in the `local` environment. However, within your `app/Providers/HorizonServiceProvider.php` file, there is an [authorization gate](/docs/{{version}}/authorization#gates) definition. This authorization gate controls access to Horizon in **non-local** environments. You are free to modify this gate as needed to restrict access to your Horizon installation: +Horizon exposes a dashboard at the `/horizon` URI. By default, you will always be able to access this dashboard in the `local` environment. However, within your `app/Providers/HorizonServiceProvider.php` file, there is an [authorization gate](/docs/{{version}}/authorization#gates) definition. This authorization gate controls access to Horizon in **non-local** environments. You are free to modify this gate as needed to restrict access to your Horizon installation: /** * Register the Horizon gate. From 47f92ca81e7bcfd5c90d97e835cee9f219d971c8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Apr 2022 10:42:51 -0500 Subject: [PATCH 0157/2609] Revert "Dashboard is always accessible (#7888)" (#7889) This reverts commit ac08b4a896ca05e8951040f4fe78bc94eac15bba. --- horizon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/horizon.md b/horizon.md index 8904ab86504..359fabb6902 100644 --- a/horizon.md +++ b/horizon.md @@ -119,7 +119,7 @@ When the `balance` option is set to `false`, the default Laravel behavior will b ### Dashboard Authorization -Horizon exposes a dashboard at the `/horizon` URI. By default, you will always be able to access this dashboard in the `local` environment. However, within your `app/Providers/HorizonServiceProvider.php` file, there is an [authorization gate](/docs/{{version}}/authorization#gates) definition. This authorization gate controls access to Horizon in **non-local** environments. You are free to modify this gate as needed to restrict access to your Horizon installation: +Horizon exposes a dashboard at the `/horizon` URI. By default, you will only be able to access this dashboard in the `local` environment. However, within your `app/Providers/HorizonServiceProvider.php` file, there is an [authorization gate](/docs/{{version}}/authorization#gates) definition. This authorization gate controls access to Horizon in **non-local** environments. You are free to modify this gate as needed to restrict access to your Horizon installation: /** * Register the Horizon gate. From 766a5765656bc7ac848ce71427e6e705cd6e3c08 Mon Sep 17 00:00:00 2001 From: Marcial Paul Gargoles Date: Thu, 21 Apr 2022 01:00:23 +0800 Subject: [PATCH 0158/2609] Update queries.md --- queries.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/queries.md b/queries.md index 79a24ea7457..6f6c37c3f5f 100644 --- a/queries.md +++ b/queries.md @@ -846,6 +846,12 @@ The `insertOrIgnore` method will ignore errors while inserting records into the > {note} `insertOrIgnore` will ignore duplicate records and also may ignore other types of errors depending on the database engine. For example, `insertOrIgnore` will [bypass MySQL's strict mode](https://dev.mysql.com/doc/refman/en/sql-mode.html#ignore-effect-on-execution). +The `insertUsing` method will insert new records into the table using a subquery. In the example below, we want to copy the users to a table before pruning. + + DB::table('pruned_users')->insertUsing([ + 'id', 'name', 'email', 'email_verified_at' + ], DB::table('users')->select('id', 'name', 'email', 'email_verified_at')->where('updated_at', '<=', now()->subMonth())); + #### Auto-Incrementing IDs From 773abc7cd5b4dd0cfabc539236ba89c8fd75de1d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Apr 2022 14:55:16 -0500 Subject: [PATCH 0159/2609] formatting --- queries.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/queries.md b/queries.md index 6f6c37c3f5f..7f3970e13db 100644 --- a/queries.md +++ b/queries.md @@ -837,20 +837,20 @@ You may insert several records at once by passing an array of arrays. Each array ['email' => 'janeway@example.com', 'votes' => 0], ]); -The `insertOrIgnore` method will ignore errors while inserting records into the database: +The `insertOrIgnore` method will ignore errors while inserting records into the database. When using this method, you should be aware that duplicate record errors will be ignored and other types of errors may also be ignored depending on the database engine. For example, `insertOrIgnore` will [bypass MySQL's strict mode](https://dev.mysql.com/doc/refman/en/sql-mode.html#ignore-effect-on-execution): DB::table('users')->insertOrIgnore([ ['id' => 1, 'email' => 'sisko@example.com'], ['id' => 2, 'email' => 'archer@example.com'], ]); -> {note} `insertOrIgnore` will ignore duplicate records and also may ignore other types of errors depending on the database engine. For example, `insertOrIgnore` will [bypass MySQL's strict mode](https://dev.mysql.com/doc/refman/en/sql-mode.html#ignore-effect-on-execution). - -The `insertUsing` method will insert new records into the table using a subquery. In the example below, we want to copy the users to a table before pruning. +The `insertUsing` method will insert new records into the table while using a subquery to determine the data that should be inserted: DB::table('pruned_users')->insertUsing([ 'id', 'name', 'email', 'email_verified_at' - ], DB::table('users')->select('id', 'name', 'email', 'email_verified_at')->where('updated_at', '<=', now()->subMonth())); + ], DB::table('users')->select( + 'id', 'name', 'email', 'email_verified_at' + )->where('updated_at', '<=', now()->subMonth())); #### Auto-Incrementing IDs From d7c6d11114fef2c8dfb6f754b34c0169cf7bde23 Mon Sep 17 00:00:00 2001 From: Colin Sheaff Date: Thu, 21 Apr 2022 10:30:22 -0500 Subject: [PATCH 0160/2609] A response to laravel/valet discussion #1207 This changeset is an attempt to more explicitly warn users away from serving sites from within folders protected by macOS by default or give them instructions on elevating Nginx's permissions to do so. I've slightly reorganized the table of contents to create a 'Troubleshooting' section and placed this information and the `Valet Directories & Files` section is now an H3 and moved under the new Troubleshooting H2 section. --- valet.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/valet.md b/valet.md index 578a77ab750..292a042e2eb 100644 --- a/valet.md +++ b/valet.md @@ -18,7 +18,10 @@ - [Custom Valet Drivers](#custom-valet-drivers) - [Local Drivers](#local-drivers) - [Other Valet Commands](#other-valet-commands) -- [Valet Directories & Files](#valet-directories-and-files) +- [Troubleshooting](#troubleshooting) + - [Recommendations](#recommendations) + - [Valet Directories & Files](#valet-directories-and-files) + - [Support](#support) ## Introduction @@ -462,8 +465,16 @@ Command | Description `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. + +## Troubleshooting + + +### Recommendations + +By default since version 10.14 [macOS restricts access to files and folders](https://manuals.info.apple.com/MANUALS/1000/MA1902/en_US/apple-platform-security-guide.pdf) including Desktop, Documents, Downloads, network volumes, and removable volumes. Therefore, Valet recommends your site folders are located outside of those protected folders. If you wish to serve sites from within one of those folders you will need to give Nginx 'Full Disk Acess', otherwise you may see 500 errors or other unpredictable behavior from Nginx, especially for static assets. To do so, go to `System Preferences` > `Security & Privacy` > `Privacy` and select `Full Disk Access` from the sidebar. Enable any `nginx` entries in the main window pane. + -## Valet Directories & Files +### Valet Directories & Files You may find the following directory and file information helpful while troubleshooting issues with your Valet environment: @@ -526,3 +537,8 @@ 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. + +> +### Support + +More support can be found at the project's [GitHub Discussions](https://github.com/laravel/valet/discussions) page. From 0683e2bcdbad1b11786f4c89ce2d8ba17e3083e5 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 21 Apr 2022 17:18:21 -0500 Subject: [PATCH 0161/2609] update description of custom pivot model capabilities (#7895) custom pivot models can do more than just define methods, such as cast attributes. we'll also make the wording a little more open ended so the reader doesn't think the capabilities are limited. --- eloquent-relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index a31aa6af7a3..a1053bd74e1 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -663,7 +663,7 @@ You can also filter the results returned by `belongsToMany` relationship queries ### Defining Custom Intermediate Table Models -If you would like to define a custom model to represent the intermediate table of your many-to-many relationship, you may call the `using` method when defining the relationship. Custom pivot models give you the opportunity to define additional methods on the pivot model. +If you would like to define a custom model to represent the intermediate table of your many-to-many relationship, you may call the `using` method when defining the relationship. Custom pivot models give you the opportunity to define additional behavior on the pivot model, such as methods and casts. Custom many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\Pivot` class while custom polymorphic many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\MorphPivot` class. For example, we may define a `Role` model which uses a custom `RoleUser` pivot model: From cd9f6cc47321a3827808949b152af97e7b1f23eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar?= Date: Fri, 22 Apr 2022 12:03:06 -0300 Subject: [PATCH 0162/2609] [9.x] Added Pluralizer::useLanguage documentation (#7894) * added Pluralizer::useLanguage documentation * formatting Co-authored-by: Taylor Otwell --- controllers.md | 8 ++++---- helpers.md | 10 +++++----- localization.md | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/controllers.md b/controllers.md index 15425185d9a..9cadef58600 100644 --- a/controllers.md +++ b/controllers.md @@ -326,7 +326,7 @@ When using a custom keyed implicit binding as a nested route parameter, Laravel ### Localizing Resource URIs -By default, `Route::resource` will create resource URIs using English verbs. If you need to localize the `create` and `edit` action verbs, you may use the `Route::resourceVerbs` method. This may be done at the beginning of the `boot` method within your application's `App\Providers\RouteServiceProvider`: +By default, `Route::resource` will create resource URIs using English verbs and plural rules. If you need to localize the `create` and `edit` action verbs, you may use the `Route::resourceVerbs` method. This may be done at the beginning of the `boot` method within your application's `App\Providers\RouteServiceProvider`: /** * Define your route model bindings, pattern filters, etc. @@ -343,11 +343,11 @@ By default, `Route::resource` will create resource URIs using English verbs. If // ... } -Once the verbs have been customized, a resource route registration such as `Route::resource('fotos', PhotoController::class)` will produce the following URIs: +Laravel's pluralizer supports [several different languages which you may configure based on your needs](/docs/{{version}}/localization#pluralization-language). Once the verbs and pluralization language have been customized, a resource route registration such as `Route::resource('publicacion', PublicacionController::class)` will produce the following URIs: - /fotos/crear + /publicacion/crear - /fotos/{foto}/editar + /publicacion/{publicaciones}/editar ### Supplementing Resource Controllers diff --git a/helpers.md b/helpers.md index 42dcb519b39..e2a03bb928c 100644 --- a/helpers.md +++ b/helpers.md @@ -1583,7 +1583,7 @@ The `Str::padRight` method wraps PHP's `str_pad` function, padding the right sid #### `Str::plural()` {.collection-method} -The `Str::plural` method converts a singular word string to its plural form. This function currently only supports the English language: +The `Str::plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): use Illuminate\Support\Str; @@ -1610,7 +1610,7 @@ You may provide an integer as a second argument to the function to retrieve the #### `Str::pluralStudly()` {.collection-method} -The `Str::pluralStudly` method converts a singular word string formatted in studly caps case to its plural form. This function currently only supports the English language: +The `Str::pluralStudly` method converts a singular word string formatted in studly caps case to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): use Illuminate\Support\Str; @@ -1721,7 +1721,7 @@ The `Str::reverse` method reverses the given string: #### `Str::singular()` {.collection-method} -The `Str::singular` method converts a string to its singular form. This function currently only supports the English language: +The `Str::singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): use Illuminate\Support\Str; @@ -2539,7 +2539,7 @@ The `pipe` method allows you to transform the string by passing its current valu #### `plural` {.collection-method} -The `plural` method converts a singular word string to its plural form. This function currently only supports the English language: +The `plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): use Illuminate\Support\Str; @@ -2683,7 +2683,7 @@ The `scan` method parses input from a string into a collection according to a fo #### `singular` {.collection-method} -The `singular` method converts a string to its singular form. This function currently only supports the English language: +The `singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): use Illuminate\Support\Str; diff --git a/localization.md b/localization.md index faf658f3681..a934480d061 100644 --- a/localization.md +++ b/localization.md @@ -2,6 +2,7 @@ - [Introduction](#introduction) - [Configuring The Locale](#configuring-the-locale) + - [Pluralization Language](#pluralization-language) - [Defining Translation Strings](#defining-translation-strings) - [Using Short Keys](#using-short-keys) - [Using Translation Strings As Keys](#using-translation-strings-as-keys) @@ -67,6 +68,27 @@ You may use the `currentLocale` and `isLocale` methods on the `App` facade to de // } + +### Pluralization Language + +You may instruct Laravel's "pluralizer", which is used by Eloquent and other portions of the framework to convert singular strings to plural strings, to use a language other than English. This may be accomplished by invoking the `useLanguage` method within the `boot` method of one of your application's service providers. The pluralizer's currently supported languages are: `french`, `norwegian-bokmal`, `portuguese`, `spanish`, and `turkish`: + + use Illuminate\Support\Pluralizer; + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Pluralizer::useLanguage('spanish'); + + // ... + } + +> {note} If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). + ## Defining Translation Strings From d7e8c2d95d3253314a42d50b33313e61d3bb28d1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 22 Apr 2022 10:18:13 -0500 Subject: [PATCH 0163/2609] formatting --- valet.md | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/valet.md b/valet.md index 292a042e2eb..b7c6207399b 100644 --- a/valet.md +++ b/valet.md @@ -18,10 +18,8 @@ - [Custom Valet Drivers](#custom-valet-drivers) - [Local Drivers](#local-drivers) - [Other Valet Commands](#other-valet-commands) -- [Troubleshooting](#troubleshooting) - - [Recommendations](#recommendations) - - [Valet Directories & Files](#valet-directories-and-files) - - [Support](#support) +- [Valet Directories & Files](#valet-directories-and-files) + - [Disk Access](#disk-access) ## Introduction @@ -465,16 +463,8 @@ Command | Description `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. - -## Troubleshooting - - -### Recommendations - -By default since version 10.14 [macOS restricts access to files and folders](https://manuals.info.apple.com/MANUALS/1000/MA1902/en_US/apple-platform-security-guide.pdf) including Desktop, Documents, Downloads, network volumes, and removable volumes. Therefore, Valet recommends your site folders are located outside of those protected folders. If you wish to serve sites from within one of those folders you will need to give Nginx 'Full Disk Acess', otherwise you may see 500 errors or other unpredictable behavior from Nginx, especially for static assets. To do so, go to `System Preferences` > `Security & Privacy` > `Privacy` and select `Full Disk Access` from the sidebar. Enable any `nginx` entries in the main window pane. - -### Valet Directories & Files +## Valet Directories & Files You may find the following directory and file information helpful while troubleshooting issues with your Valet environment: @@ -538,7 +528,9 @@ This file is the PHP-FPM pool configuration file. This file is the default Nginx configuration used for building SSL certificates for your sites. -> -### Support + +### Disk Access + +Since macOS 10.14, [access to some files and directories is restricted by default](https://manuals.info.apple.com/MANUALS/1000/MA1902/en_US/apple-platform-security-guide.pdf). These restrictions include the Desktop, Documents, and Downloads directories. In addition, network volume and removable volume access is restricted. Therefore, Valet recommends your site folders are located outside of these protected locations. -More support can be found at the project's [GitHub Discussions](https://github.com/laravel/valet/discussions) page. +However, if you wish to serve sites from within one of those locations, you will need to give Nginx "Full Disk Access". Otherwise, you may encounter server errors or other unpredictable behavior from Nginx, especially when serving static assets. Typically, macOS will automatically prompt you to grant Nginx full access to these locations. Or, you may do so manually via `System Preferences` > `Security & Privacy` > `Privacy` and selecting `Full Disk Access`. Next, enable any `nginx` entries in the main window pane. From 041590bd1d414a8c41286990645ee7dcf552f3c0 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Fri, 22 Apr 2022 17:35:47 +0200 Subject: [PATCH 0164/2609] [9.x] Document new pay and payWith methods (#7891) * Update billing.md * Update billing.md * formatting Co-authored-by: Taylor Otwell --- billing.md | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index 66bf568cdfe..e6b9fa5f3a6 100644 --- a/billing.md +++ b/billing.md @@ -47,6 +47,7 @@ - [Single Charges](#single-charges) - [Simple Charge](#simple-charge) - [Charge With Invoice](#charge-with-invoice) + - [Creating Payment Intents](#creating-payment-intents) - [Refunding Charges](#refunding-charges) - [Checkout](#checkout) - [Product Checkouts](#product-checkouts) @@ -1458,8 +1459,6 @@ 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. For example, when using United States Dollars, amounts should be specified in pennies. - 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: use Illuminate\Http\Request; @@ -1492,6 +1491,8 @@ The `charge` method will throw an exception if the charge fails. If the charge i // } +> {note} The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. + ### Charge With Invoice @@ -1523,6 +1524,37 @@ Although the `invoiceFor` method is available for you to use, it is recommendede > {note} The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. + +### Creating Payment Intents + +You can create a new Stripe payment intent by invoking the `pay` method on a billable model instance. Calling this method will create a payment intent that is wrapped in a `Laravel\Cashier\Payment` instance: + + use Illuminate\Http\Request; + + Route::post('/pay', function (Request $request) { + $payment = $request->user()->pay( + $request->get('amount') + ); + + return $payment->client_secret; + }); + +After creating the payment intent, you can return the client secret to your application's frontend so that the user can complete the payment in their browser. To read more about building entire payment flows using Stripe payment intents, please consult the [Stripe documentation](https://stripe.com/docs/payments/accept-a-payment?platform=web). + +When using the `pay` method, the default payment methods that are enabled within your Stripe dashboard will be available to the customer. Alternatively, if you only want to allow for some specific payment methods to be used, you may use the `payWith` method: + + use Illuminate\Http\Request; + + Route::post('/pay', function (Request $request) { + $payment = $request->user()->payWith( + $request->get('amount'), ['card', 'bancontact'] + ); + + return $payment->client_secret; + }); + +> {note} The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. + ### Refunding Charges From 4bd125899bb6dc6be13ef2dbaf3936929f8272e2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 22 Apr 2022 14:44:54 -0500 Subject: [PATCH 0165/2609] wip --- starter-kits.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/starter-kits.md b/starter-kits.md index 9fd1b8efc9e..18f7cce7549 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -21,6 +21,8 @@ While you are welcome to use these starter kits, they are not required. You are Breeze provides a wonderful starting point for beginning a fresh Laravel application and is also great choice for projects that plan to take their Blade templates to the next level with [Laravel Livewire](https://laravel-livewire.com). + + ### Installation From ec2d1c816221f342424c18fccb8fc631c0846a41 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 22 Apr 2022 14:59:01 -0500 Subject: [PATCH 0166/2609] wip --- deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index 132becb2454..2e3fdd46f38 100644 --- a/deployment.md +++ b/deployment.md @@ -137,7 +137,7 @@ This command precompiles all your Blade views so they are not compiled on demand ## Debug Mode -The debug option in your config/app.php configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the APP_DEBUG environment variable, which is stored in your .env file. +The debug option in your config/app.php configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your application's `.env` file. **In your production environment, this value should always be `false`. If the `APP_DEBUG` variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** From dff5ab2c6a45d63c502685e6906c8e0a3264d071 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sat, 23 Apr 2022 23:57:48 +1000 Subject: [PATCH 0167/2609] Continuity fix (#7896) The -W flag added in 2892fac027c04851f5193804bc5c58dc89ea9f63 was carried forward but no longer required --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index 6fd92b1d7e6..6a8f2ca9e5e 100644 --- a/filesystem.md +++ b/filesystem.md @@ -78,7 +78,7 @@ You may configure additional symbolic links in your `filesystems` configuration Before using the S3 driver, you will need to install the Flysystem S3 package via the Composer package manager: ```shell -composer require -W league/flysystem-aws-s3-v3 "^3.0" +composer require league/flysystem-aws-s3-v3 "^3.0" ``` The S3 driver configuration information is located in your `config/filesystems.php` configuration file. This file contains an example configuration array for an S3 driver. You are free to modify this array with your own S3 configuration and credentials. For convenience, these environment variables match the naming convention used by the AWS CLI. From 3515a72c401e4ca5c35ae85611b4d4d5c0ba7089 Mon Sep 17 00:00:00 2001 From: Zeyad Moslem <38225102+zeyadmoslem@users.noreply.github.com> Date: Mon, 25 Apr 2022 16:18:31 +0200 Subject: [PATCH 0168/2609] [9.x] add viaRemember function description (#7898) * [9.x] add viaRemember function description * Update authentication.md Co-authored-by: zeyad.moslem Co-authored-by: Taylor Otwell --- authentication.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/authentication.md b/authentication.md index 596a508732b..cc71a308214 100644 --- a/authentication.md +++ b/authentication.md @@ -298,6 +298,14 @@ When this value is `true`, Laravel will keep the user authenticated indefinitely // The user is being remembered... } +If your application offers "remember me" functionality, you may use the `viaRemember` method to determine if the currently authenticated user was authenticated using the "remember me" cookie: + + use Illuminate\Support\Facades\Auth; + + if (Auth::viaRemember()) { + // ... + } + ### Other Authentication Methods From 135ab0706b831e7f167a01a57642c7ca9855f584 Mon Sep 17 00:00:00 2001 From: darbaoui imad Date: Mon, 25 Apr 2022 14:33:09 +0000 Subject: [PATCH 0169/2609] For multiple arguments, it would be an array. (#7897) --- http-tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-tests.md b/http-tests.md index 884b84496e4..1b5d9bdf4ac 100644 --- a/http-tests.md +++ b/http-tests.md @@ -386,8 +386,8 @@ To assert that an attribute is present or absent, you may use the `has` and `mis In addition, the `hasAll` and `missingAll` methods allow asserting the presence or absence of multiple attributes simultaneously: $response->assertJson(fn (AssertableJson $json) => - $json->hasAll('status', 'data') - ->missingAll('message', 'code') + $json->hasAll(['status', 'data']) + ->missingAll(['message', 'code']) ); You may use the `hasAny` method to determine if at least one of a given list of attributes is present: From 68fe8f68c2f2411c43cf2ff0e824d9efe9bcd939 Mon Sep 17 00:00:00 2001 From: webdevnerdstuff Date: Wed, 27 Apr 2022 06:12:55 -0700 Subject: [PATCH 0170/2609] Correcting backup paths for database backups (#7903) --- homestead.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homestead.md b/homestead.md index 6e5746ea814..b9947fa4a38 100644 --- a/homestead.md +++ b/homestead.md @@ -597,7 +597,7 @@ Homestead can automatically backup your database when your Homestead virtual mac backup: true -Once configured, Homestead will export your databases to `mysql_backup` and `postgres_backup` directories when the `vagrant destroy` command is executed. These directories can be found in the folder where you installed Homestead or in the root of your project if you are using the [per project installation](#per-project-installation) method. +Once configured, Homestead will export your databases to `.backup/mysql_backup` and `.backup/postgres_backup` directories when the `vagrant destroy` command is executed. These directories can be found in the folder where you installed Homestead or in the root of your project if you are using the [per project installation](#per-project-installation) method. ### Configuring Cron Schedules From fc68fcc2250ea42afdbf0cf53ae23f33efa120a7 Mon Sep 17 00:00:00 2001 From: Nick K Date: Wed, 27 Apr 2022 19:06:41 +0100 Subject: [PATCH 0171/2609] Update csrf.md (#7905) --- csrf.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csrf.md b/csrf.md index 6c38fe8ca76..fab2546f7b7 100644 --- a/csrf.md +++ b/csrf.md @@ -65,7 +65,7 @@ The `App\Http\Middleware\VerifyCsrfToken` [middleware](/docs/{{version}}/middlew ### CSRF Tokens & SPAs -If you are building an SPA that is utilizing Laravel as an API backend, you should consult the [Laravel Sanctum documentation](/docs/{{version}}/sanctum) for information on authenticating with your API and protecting against CSRF vulnerabilities. +If you are building a SPA that is utilizing Laravel as an API backend, you should consult the [Laravel Sanctum documentation](/docs/{{version}}/sanctum) for information on authenticating with your API and protecting against CSRF vulnerabilities. ### Excluding URIs From CSRF Protection From 1b7d60fbd3aea960702a45eeba1139ebd8db426a Mon Sep 17 00:00:00 2001 From: manadinho Date: Mon, 2 May 2022 18:58:12 +0500 Subject: [PATCH 0172/2609] Added dispatchIf & dispatchUnless methods in docs (#7915) --- events.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/events.md b/events.md index 148803986f6..4daf335edfa 100644 --- a/events.md +++ b/events.md @@ -552,6 +552,12 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th OrderShipped::dispatch($order); } } + + If you would like to conditionally dispatch an event, you may use the `dispatchIf` and `dispatchUnless` methods: + + OrderShipped::dispatchIf($condition, $order); + + OrderShipped::dispatchUnless($condition, $order); > {tip} When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](/docs/{{version}}/mocking#event-fake) makes it a cinch. From 44561ad145376b78141663b8e0f2da0890a12f96 Mon Sep 17 00:00:00 2001 From: 22289d <79364059+22289d@users.noreply.github.com> Date: Mon, 2 May 2022 07:01:05 -0700 Subject: [PATCH 0173/2609] moved service provider definition to the top (#7914) * moved service provider definition to the top i was wondering what a service provider was the entire time i was reading this section. this sentence would have been much more helpful at the top like this. * Update lifecycle.md Co-authored-by: Taylor Otwell --- lifecycle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lifecycle.md b/lifecycle.md index 356e9d2f411..646e3a4941a 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -40,11 +40,11 @@ The method signature for the HTTP kernel's `handle` method is quite simple: it r ### Service Providers -One of the most important kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. All of the service providers for the application are configured in the `config/app.php` configuration file's `providers` array. +One of the most important kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. All of the service providers for the application are configured in the `config/app.php` configuration file's `providers` array. Laravel will iterate through this list of providers and instantiate each of them. After instantiating the providers, the `register` method will be called on all of the providers. Then, once all of the providers have been registered, the `boot` method will be called on each provider. This is so service providers may depend on every container binding being registered and available by the time their `boot` method is executed. -Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. Essentially every major feature offered by Laravel is bootstrapped and configured by a service provider. Since they bootstrap and configure so many features offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process. +Essentially every major feature offered by Laravel is bootstrapped and configured by a service provider. Since they bootstrap and configure so many features offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process. ### Routing From 65e75a76a7e613166dadc55446bcd55b2b966daf Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 3 May 2022 00:28:34 +1000 Subject: [PATCH 0174/2609] Document findOr method (#7912) --- eloquent.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/eloquent.md b/eloquent.md index c401ff3fbb1..67fca1ca55c 100644 --- a/eloquent.md +++ b/eloquent.md @@ -476,9 +476,13 @@ In addition to retrieving all of the records matching a given query, you may als // Alternative to retrieving the first model matching the query constraints... $flight = Flight::firstWhere('active', 1); -Sometimes you may wish to retrieve the first result of a query or perform some other action if no results are found. The `firstOr` method will return the first result matching the query or, if no results are found, execute the given closure. The value returned by the closure will be considered the result of the `firstOr` method: +Sometimes you may wish to perform some other action if no results are found. The `findOr` and `firstOr` methods will return a single model instance or, if no results are found, execute the given closure. The value returned by the closure will be considered the result of the method: - $model = Flight::where('legs', '>', 3)->firstOr(function () { + $flight = Flight::findOr(1, function () { + // ... + }); + + $flight = Flight::where('legs', '>', 3)->firstOr(function () { // ... }); From 2ff0873638989588b5d52a9e78af84beacf23324 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 3 May 2022 00:31:09 +1000 Subject: [PATCH 0175/2609] Document Sail testing database (#7911) * Document sail testing database * formatting Co-authored-by: Taylor Otwell --- sail.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 0139a97a88d..d37bc3eba9a 100644 --- a/sail.md +++ b/sail.md @@ -202,7 +202,9 @@ sail yarn ### 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. In addition, when the MySQL container is starting, it will ensure a database exists whose name matches the value of your `DB_DATABASE` environment variable. +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. + +In addition, the first time the MySQL container starts, it will create two databases for you. The first database is named using the value of your `DB_DATABASE` environment variable and is for your local development. The second is a dedicated testing database named `testing` and will ensure that your tests do not interfere with your development data. 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`. @@ -256,6 +258,12 @@ The Sail `test` command is equivalent to running the `test` Artisan command: sail artisan test ``` +By default, Sail will create a dedicated `testing` database that your tests do not interfere with the current state of your database. In a default Laravel installation, Sail will also configure your `phpunit.xml` file to use this database when executing your tests: + +```xml + +``` + ### Laravel Dusk From 390d96379fb9b034f307110c7b644269f0726c4f Mon Sep 17 00:00:00 2001 From: Elias Menkens Date: Mon, 2 May 2022 16:40:08 +0200 Subject: [PATCH 0176/2609] Make columns responsive (#7906) --- collections.md | 12 +++++++----- dusk.md | 6 ++++-- eloquent-collections.md | 12 +++++++----- helpers.md | 6 ++++-- http-tests.md | 6 ++++-- migrations.md | 12 +++++++----- validation.md | 6 ++++-- 7 files changed, 37 insertions(+), 23 deletions(-) diff --git a/collections.md b/collections.md index 238644dc92f..cc5cbbfe330 100644 --- a/collections.md +++ b/collections.md @@ -79,17 +79,19 @@ If necessary, you may define macros that accept additional arguments: 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: -
+
[all](#method-all) [average](#method-average) diff --git a/dusk.md b/dusk.md index 0e96f3c8d58..ec93a824062 100644 --- a/dusk.md +++ b/dusk.md @@ -913,12 +913,14 @@ Dusk provides a variety of assertions that you may make against your application diff --git a/eloquent-collections.md b/eloquent-collections.md index dd90ef55d36..d60839b3c48 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -40,13 +40,15 @@ All Eloquent collections extend the base [Laravel collection](/docs/{{version}}/ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a superset of methods to aid with managing your model collections. Most methods return `Illuminate\Database\Eloquent\Collection` instances; however, some methods, like `modelKeys`, return an `Illuminate\Support\Collection` instance. -
+
[contains](#method-contains) [diff](#method-diff) diff --git a/helpers.md b/helpers.md index e2a03bb928c..1ff62403275 100644 --- a/helpers.md +++ b/helpers.md @@ -13,12 +13,14 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct diff --git a/http-tests.md b/http-tests.md index 1b5d9bdf4ac..a1356d6ae3f 100644 --- a/http-tests.md +++ b/http-tests.md @@ -602,12 +602,14 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a diff --git a/migrations.md b/migrations.md index 2820576c9ae..69839e37bdc 100644 --- a/migrations.md +++ b/migrations.md @@ -322,13 +322,15 @@ The `table` method on the `Schema` facade may be used to update existing tables. The schema builder blueprint offers a variety of methods that correspond to the different types of columns you can add to your database tables. Each of the available methods are listed in the table below: -
+
[bigIncrements](#column-method-bigIncrements) [bigInteger](#column-method-bigInteger) diff --git a/validation.md b/validation.md index 732d8929b19..b2259c324d6 100644 --- a/validation.md +++ b/validation.md @@ -752,12 +752,14 @@ Below is a list of all available validation rules and their function: From 98e83e219ba3d1997720c1e68e40f4be1e7e6fb7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 2 May 2022 10:12:26 -0500 Subject: [PATCH 0177/2609] document request string --- requests.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/requests.md b/requests.md index ae084e7e911..7c8bb9ecdb6 100644 --- a/requests.md +++ b/requests.md @@ -282,6 +282,13 @@ When sending JSON requests to your application, you may access the JSON data via $name = $request->input('user.name'); + +#### Retrieving Stringable Input Values + +Instead of retrieving the request's input data as a primitive `string`, you may use the `string` method to retrieve the request data as an instance of [`Illuminate\Support\Stringable`](/docs/{{version}}/helpers#fluent-strings): + + $name = $request->string('name')->trim(); + #### Retrieving Boolean Input Values From e18d49f9b04b53224ad2f003888094f8aee91e98 Mon Sep 17 00:00:00 2001 From: "Dr. Adam Nielsen" <1765602+iwasherefirst2@users.noreply.github.com> Date: Tue, 3 May 2022 16:07:49 +0200 Subject: [PATCH 0178/2609] Be specific what to add to components constructor (#7916) * Be specific what to add to components constructor The old sentence "You should define required component's data in its class constructor." could be read as that optional parameters for the component do not need to be specified in the constructor. I think other may have read it in the same way: https://github.com/laravel/framework/issues/36256 * Update blade.md Co-authored-by: Taylor Otwell --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index dda532b55b2..5a8b8a2d9ee 100644 --- a/blade.md +++ b/blade.md @@ -677,7 +677,7 @@ You may pass data to Blade components using HTML attributes. Hard-coded, primiti ``` -You should define the component's required data in its class constructor. All public properties on a component will automatically be made available to the component's view. It is not necessary to pass the data to the view from the component's `render` method: +You should define all of the component's data attributes in its class constructor. All public properties on a component will automatically be made available to the component's view. It is not necessary to pass the data to the view from the component's `render` method: Date: Tue, 3 May 2022 17:43:56 +0200 Subject: [PATCH 0179/2609] [9.x] Document coupon and promotion code additions to Cashier Stripe (#7917) * Update billing.md * formatting Co-authored-by: Taylor Otwell --- billing.md | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index e6b9fa5f3a6..336c9c43f46 100644 --- a/billing.md +++ b/billing.md @@ -665,12 +665,54 @@ If you would like to apply a coupon when creating the subscription, you may use ->withCoupon('code') ->create($paymentMethod); -Or, if you would like to apply a [Stripe promotion code](https://stripe.com/docs/billing/subscriptions/discounts/codes), you may use the `withPromotionCode` method. The given promotion code ID should be the Stripe API ID assigned to the promotion code and not the customer facing promotion code: +Or, if you would like to apply a [Stripe promotion code](https://stripe.com/docs/billing/subscriptions/discounts/codes), you may use the `withPromotionCode` method: $user->newSubscription('default', 'price_monthly') - ->withPromotionCode('promo_code') + ->withPromotionCode('promo_code_id') ->create($paymentMethod); +The given promotion code ID should be the Stripe API ID assigned to the promotion code and not the customer facing promotion code. If you need to find a promotion code ID based on a given customer facing promotion code, you may use the `findPromotionCode` method: + + // Find a promotion code ID by its customer facing code... + $promotionCode = $user->findPromotionCode('SUMMERSALE'); + + // Find an active promotion code ID by its customer facing code... + $promotionCode = $user->findActivePromotionCode('SUMMERSALE'); + +In the example above, the returned `$promotionCode` object is an instance of `Laravel\Cashier\PromotionCode`. This class decorates an underlying `Stripe\PromotionCode` object. You can retrieve the coupon related to the promotion code by invoking the `coupon` method: + + $coupon = $user->findPromotionCode('SUMMERSALE')->coupon(); + +The coupon instance allows you to determine the discount amount and whether the coupon represents a fixed discount or percentage based discount: + + if ($coupon->isPercentage()) { + return $coupon->percentOff().'%'; // 21.5% + } else { + return $coupon->amountOff(); // $5.99 + } + +You can also retrieve the discounts that are currently applied to a customer or subscription: + + $discount = $billable->discount(); + + $discount = $subscription->discount(); + +The returned `Laravel\Cashier\Discount` instances decorate an underlying `Stripe\Discount` object instance. You may retrieve the coupon related to this discount by invoking the `coupon` method: + + $coupon = $subscription->discount()->coupon(); + +If you would like to apply a new coupon or promotion code to a customer or subscription, you may do so via the `applyCoupon` or `applyPromotionCode` methods: + + $billable->applyCoupon('coupon_id'); + $billable->applyPromotionCode('promotion_code_id'); + + $subscription->applyCoupon('coupon_id'); + $subscription->applyPromotionCode('promotion_code_id'); + +Remember, you should use the Stripe API ID assigned to the promotion code and not the customer facing promotion code. Only one coupon or promotion code can be applied to a customer or subscription at a given time. + +For more info on this subject, please consult the Stripe documentation regarding [coupons](https://stripe.com/docs/billing/subscriptions/coupons) and [promotion codes](https://stripe.com/docs/billing/subscriptions/coupons/codes). + #### Adding Subscriptions From e3ccc425140a36190ff81fd6a837b7f99599d1fb Mon Sep 17 00:00:00 2001 From: Yoeri Boven Date: Wed, 4 May 2022 21:17:23 +0200 Subject: [PATCH 0180/2609] Documented new feature (#7920) * Update mocking.md * formatting Co-authored-by: Taylor Otwell --- mocking.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/mocking.md b/mocking.md index 0586429b659..fa65530a1a3 100644 --- a/mocking.md +++ b/mocking.md @@ -479,18 +479,13 @@ You may pass a closure to the `assertSentTo` or `assertNotSentTo` methods in ord #### 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: +If the code you are testing sends [on-demand notifications](/docs/{{version}}/notifications#on-demand-notifications), you can test that the on-demand notification was sent via the `assertSentOnDemand` method: - use Illuminate\Notifications\AnonymousNotifiable; + Notification::assertSentOnDemand(OrderShipped::class); - Notification::assertSentTo( - new AnonymousNotifiable, OrderShipped::class - ); +By passing a closure as the second argument to the `assertSentOnDemand` method, you may determine if an on-demand notification was sent to the correct "route" address: -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, + Notification::assertSentOnDemand( OrderShipped::class, function ($notification, $channels, $notifiable) use ($user) { return $notifiable->routes['mail'] === $user->email; From cad9461716d76e12dc536db953894c359172465b Mon Sep 17 00:00:00 2001 From: Kelvin Macharia Ngunyi Date: Thu, 5 May 2022 15:59:56 +0200 Subject: [PATCH 0181/2609] Add `sole` method to Enumerable available methods (#7921) Illuminate\Support\Enumerable contract now defines a sole method in Laravel 9 --- collections.md | 1 + 1 file changed, 1 insertion(+) diff --git a/collections.md b/collections.md index cc5cbbfe330..1ea326907bd 100644 --- a/collections.md +++ b/collections.md @@ -3353,6 +3353,7 @@ Almost all methods available on the `Collection` class are also available on the [shuffle](#method-shuffle) [skip](#method-skip) [slice](#method-slice) +[sole](#method-sole) [some](#method-some) [sort](#method-sort) [sortBy](#method-sortby) From 618137b3b3e3ad24c50c85d0c65a96223dd6698c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 5 May 2022 15:57:53 -0500 Subject: [PATCH 0182/2609] wip --- billing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/billing.md b/billing.md index 336c9c43f46..d3662815dfa 100644 --- a/billing.md +++ b/billing.md @@ -160,8 +160,11 @@ Next, you should configure your Stripe API keys in your application's `.env` fil ```ini STRIPE_KEY=your-stripe-key STRIPE_SECRET=your-stripe-secret +STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret ``` +> {note} You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. + ### Currency Configuration From 6df2f5037e223124350e5c7ab16966c0b891cb31 Mon Sep 17 00:00:00 2001 From: Joel Clermont Date: Fri, 6 May 2022 08:41:26 -0500 Subject: [PATCH 0183/2609] Add documentation for TrimStrings::skipWhen (#7924) * Add documentation for TrimStrings::skipWhen * formatting Co-authored-by: Taylor Otwell --- requests.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/requests.md b/requests.md index 7c8bb9ecdb6..4e2daa8675d 100644 --- a/requests.md +++ b/requests.md @@ -462,7 +462,32 @@ All cookies created by the Laravel framework are encrypted and signed with an au 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. -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. +#### Disabling Input Normalization + +If you would like to disable this behavior for all requests, 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. + +If you would like to disable string trimming and empty string conversion for a subset of requests to your application, you may use the `skipWhen` method offered by both middleware. This method accepts a closure which should return `true` or `false` to indicate if input normalization should be skipped. Typically, the `skipWhen` method should be invoked in the `boot` method of your application's `AppServiceProvider`. + +```php +use App\Http\Middleware\ConvertEmptyStringsToNull; +use App\Http\Middleware\TrimStrings; + +/** + * Bootstrap any application services. + * + * @return void + */ +public function boot() +{ + TrimStrings::skipWhen(function ($request) { + return $request->is('admin/*'); + }); + + ConvertEmptyStringsToNull::skipWhen(function ($request) { + // ... + }); +} +``` ## Files From 999f0bb1438ef7617b69203d51c28581223c79c2 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 11 May 2022 00:07:21 +1000 Subject: [PATCH 0184/2609] Document Http::preventStrayRequests() (#7928) * Document Http::preventStrayRequests() * formatting Co-authored-by: Taylor Otwell --- http-client.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index 61dfe906ab1..41bfd85bd8b 100644 --- a/http-client.md +++ b/http-client.md @@ -14,6 +14,7 @@ - [Testing](#testing) - [Faking Responses](#faking-responses) - [Inspecting Requests](#inspecting-requests) + - [Preventing Stray Requests](#preventing-stray-requests) - [Events](#events) @@ -324,7 +325,7 @@ $response = Http::github()->get('/'); ## Testing -Many Laravel services provide functionality to help you easily and expressively write tests, and Laravel's HTTP wrapper is no exception. The `Http` facade's `fake` method allows you to instruct the HTTP client to return stubbed / dummy responses when requests are made. +Many Laravel services provide functionality to help you easily and expressively write tests, and Laravel's HTTP client is no exception. The `Http` facade's `fake` method allows you to instruct the HTTP client to return stubbed / dummy responses when requests are made. ### Faking Responses @@ -400,6 +401,25 @@ If you require more complicated logic to determine what responses to return for return Http::response('Hello World', 200); }); + +### Preventing Stray Requests + +If you would like to ensure that all requests sent via the HTTP client have been faked throughout your individual test or complete test suite, you can call the `preventStrayRequests` method. After calling this method, any requests that do not have a corresponding fake response will throw an exception rather than making the actual HTTP request: + + use Illuminate\Support\Facades\Http; + + Http::preventStrayRequests(); + + Http::fake([ + 'github.com/*' => Http::response('ok'), + ]); + + // An "ok" response is returned... + Http::get('https://github.com/laravel/framework'); + + // An exception is thrown... + Http::get('https://laravel.com/'); + ### Inspecting Requests From 78ac6fbbaffa3e7e8567b0550f2617229ba56eb3 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 11 May 2022 00:13:17 +1000 Subject: [PATCH 0185/2609] document Log::shareContext() (#7929) * document Log::shareContext() * formatting Co-authored-by: Taylor Otwell --- logging.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/logging.md b/logging.md index e966dcfe5a3..7b9783b5ccd 100644 --- a/logging.md +++ b/logging.md @@ -206,7 +206,7 @@ An array of contextual data may be passed to the log methods. This contextual da Log::info('User failed to login.', ['id' => $user->id]); -Occasionally, you may wish to specify some contextual information that should be included with all subsequent log entries. For example, you may wish to log a request ID that is associated with each incoming request to your application. To accomplish this, you may call the `Log` facade's `withContext` method: +Occasionally, you may wish to specify some contextual information that should be included with all subsequent log entries in a particular channel. For example, you may wish to log a request ID that is associated with each incoming request to your application. To accomplish this, you may call the `Log` facade's `withContext` method: (string) Str::uuid(), + ]); + } + } + ### Writing To Specific Channels From d2a325c5ecda4501feb6582a950ce7f04931029c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 May 2022 09:18:54 -0500 Subject: [PATCH 0186/2609] document withDelay --- notifications.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 3737602fbc7..7ccbcf5ae8b 100644 --- a/notifications.md +++ b/notifications.md @@ -154,12 +154,20 @@ Once the `ShouldQueue` interface has been added to your notification, you may se $user->notify(new InvoicePaid($invoice)); +When queueing notifications, a queued job will be created for each recipient and channel combination. For example, six jobs will be dispatched to the queue if your notification has three recipients and two channels. + + +#### Delaying Notifications + If you would like to delay the delivery of the notification, you may chain the `delay` method onto your notification instantiation: $delay = now()->addMinutes(10); $user->notify((new InvoicePaid($invoice))->delay($delay)); + +#### Delaying Notifications Per Channel + You may pass an array to the `delay` method to specify the delay amount for specific channels: $user->notify((new InvoicePaid($invoice))->delay([ @@ -167,7 +175,21 @@ You may pass an array to the `delay` method to specify the delay amount for spec 'sms' => now()->addMinutes(10), ])); -When queueing notifications, a queued job will be created for each recipient and channel combination. For example, six jobs will be dispatched to the queue if your notification has three recipients and two channels. +Alternatively, you may define a `withDelay` method on the notification class itself. The `withDelay` method should return an array of channel names and delay values: + + /** + * Determine the notification's delivery delay. + * + * @param mixed $notifiable + * @return array + */ + public function withDelay($notifiable) + { + return [ + 'mail' => now()->addMinutes(5), + 'sms' => now()->addMinutes(10), + ]; + } #### Customizing The Notification Queue Connection From 5a4cb949bee04f7167fb7e29c0da05b14c3aed4e Mon Sep 17 00:00:00 2001 From: Bernard Wiesner Date: Thu, 12 May 2022 23:45:45 +0900 Subject: [PATCH 0187/2609] document schedule:clear-cache (#7933) * document schedule:clear-cache * formatting Co-authored-by: Bernard Wiesner Co-authored-by: Taylor Otwell --- scheduling.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scheduling.md b/scheduling.md index b065d79103f..e32ef5ccdc2 100644 --- a/scheduling.md +++ b/scheduling.md @@ -262,6 +262,8 @@ If needed, you may specify how many minutes must pass before the "without overla $schedule->command('emails:send')->withoutOverlapping(10); +Behind the scenes, the `withoutOverlapping` method utilizes your application's [cache](/docs/{{version}}/cache) to obtain locks. If necessary, you can clear these cache locks using the `schedule:clear-cache` Artisan command. This is typically only necessary if a task becomes stuck due to an unexpected server problem. + ### Running Tasks On One Server From 4cdf7aed640f44e03fe8dbc084f16b01c8ca3592 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 16 May 2022 09:50:50 -0500 Subject: [PATCH 0188/2609] wip --- artisan.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artisan.md b/artisan.md index 768c94ec296..7e389a8c468 100644 --- a/artisan.md +++ b/artisan.md @@ -285,10 +285,10 @@ If you would like to define arguments or options to expect multiple input values 'mail: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 an array with `1` and `2` as its values: ```shell -php artisan mail:send foo bar +php artisan mail:send 1 2 ``` This `*` character can be combined with an optional argument definition to allow zero or more instances of an argument: @@ -300,7 +300,7 @@ This `*` character can be combined with an optional argument definition to allow When defining an option that expects multiple input values, each option value passed to the command should be prefixed with the option name: - 'mail:send {user} {--id=*}' + 'mail:send {--id=*}' Such a command may be invoked by passing multiple `--id` arguments: From 5b6513a28fd2357f38109203763ebfc121d78418 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 16 May 2022 17:12:45 +0200 Subject: [PATCH 0189/2609] Update validation.md (#7940) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index b2259c324d6..1252bb8272b 100644 --- a/validation.md +++ b/validation.md @@ -1219,8 +1219,8 @@ When the `in` rule is combined with the `array` rule, each value in the input ar 'airports' => [ 'required', 'array', - Rule::in(['NYC', 'LIT']), ], + 'airports.*' => Rule::in(['NYC', 'LIT']), ]); From 8c1cb2c6b52b738a1670097e4ec3067aca7f0618 Mon Sep 17 00:00:00 2001 From: Danilo Polani Date: Mon, 16 May 2022 17:48:20 +0200 Subject: [PATCH 0190/2609] Add `assertJsonMissingPath()` to HTTP Tests (#7936) * Update http-tests.md * Update http-tests.md * Update http-tests.md Co-authored-by: Taylor Otwell --- http-tests.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index a1356d6ae3f..c0e09f5d191 100644 --- a/http-tests.md +++ b/http-tests.md @@ -634,6 +634,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertJsonMissingExact](#assert-json-missing-exact) [assertJsonMissingValidationErrors](#assert-json-missing-validation-errors) [assertJsonPath](#assert-json-path) +[assertJsonMissingPath](#assert-json-missing-path) [assertJsonStructure](#assert-json-structure) [assertJsonValidationErrors](#assert-json-validation-errors) [assertJsonValidationErrorFor](#assert-json-validation-error-for) @@ -821,7 +822,7 @@ Assert that the response contains the given data at the specified path: $response->assertJsonPath($path, $expectedValue); -For example, if the JSON response returned by your application contains the following data: +For example, if the following JSON response is returned by your application: ```js { @@ -835,6 +836,27 @@ You may assert that the `name` property of the `user` object matches a given val $response->assertJsonPath('user.name', 'Steve Schoger'); + +#### assertJsonMissingPath + +Assert that the response does not contain the given path: + + $response->assertJsonMissingPath($path); + +For example, if the following JSON response is returned by your application: + +```js +{ + "user": { + "name": "Steve Schoger" + } +} +``` + +You may assert that it does not contain the `email` property of the `user` object: + + $response->assertJsonMissingPath('user.email'); + #### assertJsonStructure From f170a2ce30abafe4618a60bb9877b17a5b02b371 Mon Sep 17 00:00:00 2001 From: Brandon Eichhorn <101891339+eduance@users.noreply.github.com> Date: Mon, 16 May 2022 17:57:06 +0200 Subject: [PATCH 0191/2609] [9.x] Document the usage of expressions and raw statements within the default() modifier (#7938) * Document the usage of DB::raw() within the default value as to not be used as this could cause issues with doctrine/dbal. * Update migrations.md Co-authored-by: Taylor Otwell --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index 69839e37bdc..50f08be9882 100644 --- a/migrations.md +++ b/migrations.md @@ -927,7 +927,7 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi } }; -> {note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. +> {note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. In addition, it is not possible to combine raw `default` expressions (using `DB::raw`) with column changes via the `change` method. #### Column Order From 38177b43cf1fba690e47f2825969a9e0265cb3f1 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 17 May 2022 16:28:02 +0200 Subject: [PATCH 0192/2609] [9.x] Document hasExpiredTrial (#7942) * Update billing.md * Update cashier-paddle.md * Update billing.md * Update billing.md * Update billing.md * Update cashier-paddle.md * Update billing.md * Update billing.md * Update billing.md Co-authored-by: Taylor Otwell --- billing.md | 10 ++++++++++ cashier-paddle.md | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/billing.md b/billing.md index d3662815dfa..adc0fd9e1b8 100644 --- a/billing.md +++ b/billing.md @@ -1325,6 +1325,16 @@ You may use the `endTrial` method to immediately end a subscription trial: $user->subscription('default')->endTrial(); +To determine if an existing trial has expired, you may use the `hasExpiredTrial` methods: + + if ($user->hasExpiredTrial('default')) { + // + } + + if ($user->subscription('default')->hasExpiredTrial()) { + // + } + #### Defining Trial Days In Stripe / Cashier diff --git a/cashier-paddle.md b/cashier-paddle.md index 939276c8da5..6f7fe88b4a8 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -880,6 +880,16 @@ You may determine if the user is within their trial period using either the `onT // } +To determine if an existing trial has expired, you may use the `hasExpiredTrial` methods: + + if ($user->hasExpiredTrial('default')) { + // + } + + if ($user->subscription('default')->hasExpiredTrial()) { + // + } + #### Defining Trial Days In Paddle / Cashier From 8cfab3738335071cced09b1cf5ec2d6ad449d4fc Mon Sep 17 00:00:00 2001 From: Mehdi Rajabi Date: Wed, 18 May 2022 22:35:58 +0430 Subject: [PATCH 0193/2609] [9.x] Add orWhereRelation and orWhereMorphRelation (#7945) * Add orWhereRelation and orWhereMorphRelation * Update eloquent-relationships.md Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index a1053bd74e1..dc4f322d36c 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1229,7 +1229,7 @@ If you need even more power, you may use the `whereHas` and `orWhereHas` methods #### Inline Relationship Existence Queries -If you would like to query for a relationship's existence with a single, simple where condition attached to the relationship query, you may find it more convenient to use the `whereRelation` and `whereMorphRelation` methods. For example, we may query for all posts that have unapproved comments: +If you would like to query for a relationship's existence with a single, simple where condition attached to the relationship query, you may find it more convenient to use the `whereRelation`, `orWhereRelation`, `whereMorphRelation`, and `orWhereMorphRelation` methods. For example, we may query for all posts that have unapproved comments: use App\Models\Post; From 8a8d3b0a1a5cf9af2a5f5ae101b00f0f30fc60f0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 18 May 2022 13:06:52 -0500 Subject: [PATCH 0194/2609] wip --- mocking.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mocking.md b/mocking.md index fa65530a1a3..3d0161cf4d2 100644 --- a/mocking.md +++ b/mocking.md @@ -464,6 +464,9 @@ After calling the `Notification` facade's `fake` method, you may then assert tha Notification::assertNotSentTo( [$user], AnotherNotification::class ); + + // Assert that a given number of notifications were sent... + Notification::assertCount(3); } } From 56f3abfc75a01cbf9eaf137c8aad4fbbf6c3f5f2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 18 May 2022 13:11:27 -0500 Subject: [PATCH 0195/2609] wip --- collections.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/collections.md b/collections.md index 1ea326907bd..799df3fb24a 100644 --- a/collections.md +++ b/collections.md @@ -212,6 +212,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [unlessEmpty](#method-unlessempty) [unlessNotEmpty](#method-unlessnotempty) [unwrap](#method-unwrap) +[value](#method-value) [values](#method-values) [when](#method-when) [whenEmpty](#method-whenempty) @@ -2791,6 +2792,20 @@ The static `unwrap` method returns the collection's underlying items from the gi // 'John Doe' + +#### `value()` {.collection-method} + +The `value` method retrieves a given value from the first element of the collection: + + $collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Speaker', 'price' => 400], + ]); + + $value = $collection->value('price'); + + // 200 + #### `values()` {.collection-method} From 2b3dacad3c24b2197f6c26851b100d4a065a1265 Mon Sep 17 00:00:00 2001 From: David Heremans Date: Thu, 19 May 2022 16:30:04 +0200 Subject: [PATCH 0196/2609] Adds documentation for Arr::prependKeysWith method --- helpers.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/helpers.md b/helpers.md index 1ff62403275..e7654af8f9e 100644 --- a/helpers.md +++ b/helpers.md @@ -46,6 +46,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::isAssoc](#method-array-isassoc) [Arr::isList](#method-array-islist) [Arr::keyBy](#method-array-keyby) +[Arr::prependKeysWith](#method-array-prependkeyswith) [Arr::last](#method-array-last) [Arr::only](#method-array-only) [Arr::pluck](#method-array-pluck) @@ -630,6 +631,28 @@ The `Arr::keyBy` method keys the array by the given key. If multiple items have ] */ + +#### `Arr::prependKeysWith()` {.collection-method} + +The `Arr::prependKeysWith` prepends all key names of an associative array with the given prefix: + + use Illuminate\Support\Arr; + + $array = [ + 'key' => 'value', + 'key2' => 'value2', + ]; + + $keyed = Arr::prependKeysWith($array, 'prefix.'); + + /* + [ + 'prefix.key' => 'value', + 'prefix.key2' => 'value2', + ] + */ + + #### `Arr::last()` {.collection-method} From 9a129dce7cec7cd594e8c623e505282d5b2b2b68 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Fri, 20 May 2022 15:34:55 +0200 Subject: [PATCH 0197/2609] Update releases.md (#7948) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 69a2d4d7186..76cbb64d37b 100644 --- a/releases.md +++ b/releases.md @@ -19,7 +19,7 @@ When referencing the Laravel framework or its components from your application o ## Support Policy -For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). +For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest major release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | | --- | --- | --- | --- | --- | From 7ef304f1cc6ef867e1c592a70a19b186177089cd Mon Sep 17 00:00:00 2001 From: Michael Flynn Date: Mon, 23 May 2022 11:43:21 -0400 Subject: [PATCH 0198/2609] Updating custom throttle response. (#7950) * Updating custom throttle reponse. Added the 2 parameters that the custom response callback is called with in the ThrottleRequests::buildException method. I tried to implement my own custom response and lost the throttle headers (X-RateLimit-Limit, etc). It was not obvious to me how to get the header info into my response. I tried to dig through the code, but missed the spot where the callback is called. Got a response to my problem on Laracasts, through I'd help put it in the docs for others. * Update routing.md Co-authored-by: Taylor Otwell --- routing.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routing.md b/routing.md index f3f65062200..a86a72f9d3d 100644 --- a/routing.md +++ b/routing.md @@ -644,6 +644,7 @@ Laravel includes powerful and customizable rate limiting services that you may u 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 to the rate limiter. Limit configuration are instances of the `Illuminate\Cache\RateLimiting\Limit` class. This class contains helpful "builder" methods so that you can quickly define your limit. The rate limiter name may be any string you wish: use Illuminate\Cache\RateLimiting\Limit; + use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; /** @@ -661,8 +662,8 @@ Rate limiters are defined using the `RateLimiter` facade's `for` method. The `fo If the incoming request exceeds the specified rate limit, a response with a 429 HTTP status code will automatically be returned by Laravel. If you would like to define your own response that should be returned by a rate limit, you may use the `response` method: RateLimiter::for('global', function (Request $request) { - return Limit::perMinute(1000)->response(function () { - return response('Custom response...', 429); + return Limit::perMinute(1000)->response(function (Request $request, array $headers) { + return response('Custom response...', 429, $headers); }); }); From 653985a0172a5926144aff58f8a89573ee015a60 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 24 May 2022 23:51:55 +1000 Subject: [PATCH 0199/2609] replace first class callables with comments (#7951) --- broadcasting.md | 12 ++++++------ cache.md | 2 +- database.md | 2 +- errors.md | 2 +- http-client.md | 26 +++++++++++++------------- logging.md | 2 +- mail.md | 2 +- queries.md | 4 ++-- queues.md | 12 ++++++------ upgrade.md | 2 +- validation.md | 2 +- 11 files changed, 34 insertions(+), 34 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 57a61d1c616..c4023c28b05 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -748,9 +748,9 @@ If you would like to listen for events on a private channel, use the `private` m ```js Echo.private(`orders.${this.order.id}`) - .listen(...) - .listen(...) - .listen(...); + .listen(/* ... */) + .listen(/* ... */) + .listen(/* ... */); ``` @@ -865,9 +865,9 @@ As typical of other types of events, you may listen for events sent to presence ```js Echo.join(`chat.${roomId}`) - .here(...) - .joining(...) - .leaving(...) + .here(/* ... */) + .joining(/* ... */) + .leaving(/* ... */) .listen('NewMessage', (e) => { // }); diff --git a/cache.md b/cache.md index 6848361b9f6..0c58d55bac0 100644 --- a/cache.md +++ b/cache.md @@ -141,7 +141,7 @@ The `Cache` facade's `get` method is used to retrieve items from the cache. If t 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(); + return DB::table(/* ... */)->get(); }); diff --git a/database.md b/database.md index f55dfbce2d7..9f43cf69922 100644 --- a/database.md +++ b/database.md @@ -231,7 +231,7 @@ If your application defines multiple connections in your `config/database.php` c use Illuminate\Support\Facades\DB; - $users = DB::connection('sqlite')->select(...); + $users = DB::connection('sqlite')->select(/* ... */); You may access the raw, underlying PDO instance of a connection using the `getPdo` method on a connection instance: diff --git a/errors.md b/errors.md index 513c6965dfb..e9a5ea44d57 100644 --- a/errors.md +++ b/errors.md @@ -229,7 +229,7 @@ Instead of type-checking exceptions in the exception handler's `register` method */ public function render($request) { - return response(...); + return response(/* ... */); } } diff --git a/http-client.md b/http-client.md index 41bfd85bd8b..3d48063f415 100644 --- a/http-client.md +++ b/http-client.md @@ -148,43 +148,43 @@ For convenience, you may use the `acceptJson` method to quickly specify that you You may specify basic and digest authentication credentials using the `withBasicAuth` and `withDigestAuth` methods, respectively: // Basic authentication... - $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(...); + $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); // Digest authentication... - $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(...); + $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */); #### Bearer Tokens If you would like to quickly add a bearer token to the request's `Authorization` header, you may use the `withToken` method: - $response = Http::withToken('token')->post(...); + $response = Http::withToken('token')->post(/* ... */); ### Timeout The `timeout` method may be used to specify the maximum number of seconds to wait for a response: - $response = Http::timeout(3)->get(...); + $response = Http::timeout(3)->get(/* ... */); If the given timeout is exceeded, an instance of `Illuminate\Http\Client\ConnectionException` will be thrown. You may specify the maximum number of seconds to wait while trying to connect to a server using the `connectTimeout` method: - $response = Http::connectTimeout(3)->get(...); + $response = Http::connectTimeout(3)->get(/* ... */); ### Retries If you would like HTTP client to automatically retry the request if a client or server error occurs, you may use the `retry` method. The `retry` method accepts the maximum number of times the request should be attempted and the number of milliseconds that Laravel should wait in between attempts: - $response = Http::retry(3, 100)->post(...); + $response = Http::retry(3, 100)->post(/* ... */); If needed, you may pass a third argument to the `retry` method. The third argument should be a callable that determines if the retries should actually be attempted. For example, you may wish to only retry the request if the initial request encounters an `ConnectionException`: $response = Http::retry(3, 100, function ($exception, $request) { return $exception instanceof ConnectionException; - })->post(...); + })->post(/* ... */); If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: @@ -196,11 +196,11 @@ If a request attempt fails, you may wish to make a change to the request before $request->withToken($this->getNewToken()); return true; - })->post(...); + })->post(/* ... */); If all of the requests fail, an instance of `Illuminate\Http\Client\RequestException` will be thrown. If you would like to disable this behavior, you may provide a `throw` argument with a value of `false`. When disabled, the last response received by the client will be returned after all retries have been attempted: - $response = Http::retry(3, 100, throw: false)->post(...); + $response = Http::retry(3, 100, throw: false)->post(/* ... */); > {note} If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. @@ -229,7 +229,7 @@ Unlike Guzzle's default behavior, Laravel's HTTP client wrapper does not throw e If you have a response instance and would like to throw an instance of `Illuminate\Http\Client\RequestException` if the response status code indicates a client or server error, you may use the `throw` or `throwIf` methods: - $response = Http::post(...); + $response = Http::post(/* ... */); // Throw an exception if a client or server error occurred... $response->throw(); @@ -243,11 +243,11 @@ The `Illuminate\Http\Client\RequestException` instance has a public `$response` The `throw` method returns the response instance if no error occurred, allowing you to chain other operations onto the `throw` method: - return Http::post(...)->throw()->json(); + return Http::post(/* ... */)->throw()->json(); If you would like to perform some additional logic before the exception is thrown, you may pass a closure to the `throw` method. The exception will be thrown automatically after the closure is invoked, so you do not need to re-throw the exception from within the closure: - return Http::post(...)->throw(function ($response, $e) { + return Http::post(/* ... */)->throw(function ($response, $e) { // })->json(); @@ -336,7 +336,7 @@ For example, to instruct the HTTP client to return empty, `200` status code resp Http::fake(); - $response = Http::post(...); + $response = Http::post(/* ... */); #### Faking Specific URLs diff --git a/logging.md b/logging.md index 7b9783b5ccd..e7b905a9d34 100644 --- a/logging.md +++ b/logging.md @@ -406,6 +406,6 @@ Once you have configured the `custom` driver channel, you're ready to define the */ public function __invoke(array $config) { - return new Logger(...); + return new Logger(/* ... */); } } diff --git a/mail.md b/mail.md index 6bfa50c767d..8628200c6be 100644 --- a/mail.md +++ b/mail.md @@ -987,7 +987,7 @@ Once you've defined your custom transport, you may register it via the `extend` public function boot() { Mail::extend('mailchimp', function (array $config = []) { - return new MailchimpTransport(...); + return new MailchimpTransport(/* ... */); }) } diff --git a/queries.md b/queries.md index 7f3970e13db..64ded76de61 100644 --- a/queries.md +++ b/queries.md @@ -258,7 +258,7 @@ Instead of using the `DB::raw` method, you may also use the following methods to #### `selectRaw` -The `selectRaw` method can be used in place of `addSelect(DB::raw(...))`. This method accepts an optional array of bindings as its second argument: +The `selectRaw` method can be used in place of `addSelect(DB::raw(/* ... */))`. This method accepts an optional array of bindings as its second argument: $orders = DB::table('orders') ->selectRaw('price * ? as price_with_tax', [1.0825]) @@ -348,7 +348,7 @@ You may also specify more advanced join clauses. To get started, pass a closure DB::table('users') ->join('contacts', function ($join) { - $join->on('users.id', '=', 'contacts.user_id')->orOn(...); + $join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */); }) ->get(); diff --git a/queues.md b/queues.md index 5dc0e8f3eb5..fdcec3966e7 100644 --- a/queues.md +++ b/queues.md @@ -622,7 +622,7 @@ Once you have written your job class, you may dispatch it using the `dispatch` m */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // ... @@ -660,7 +660,7 @@ If you would like to specify that a job should not be immediately available for */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // ... @@ -713,7 +713,7 @@ If you would like to dispatch a job immediately (synchronously), you may use the */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // Create podcast... @@ -775,7 +775,7 @@ In addition to chaining job class instances, you may also chain closures: new ProcessPodcast, new OptimizePodcast, function () { - Podcast::update(...); + Podcast::update(/* ... */); }, ])->dispatch(); @@ -835,7 +835,7 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // Create podcast... @@ -894,7 +894,7 @@ If your application interacts with multiple queue connections, you may specify w */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // Create podcast... diff --git a/upgrade.md b/upgrade.md index b94b96741ce..d8948234762 100644 --- a/upgrade.md +++ b/upgrade.md @@ -449,7 +449,7 @@ The [HTTP client](/docs/{{version}}/http-client) now has a default timeout of 30 If you wish to specify a longer timeout for a given request, you may do so using the `timeout` method: - $response = Http::timeout(120)->get(...); + $response = Http::timeout(120)->get(/* ... */); #### HTTP Fake & Middleware diff --git a/validation.md b/validation.md index 1252bb8272b..7f5baa4bc07 100644 --- a/validation.md +++ b/validation.md @@ -590,7 +590,7 @@ Many of Laravel's built-in error messages include an `:attribute` placeholder th 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 = Validator::make(/* ... */); $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { From 74c9577fdaf6f55537a91283580017dd4f629e7d Mon Sep 17 00:00:00 2001 From: Steve McDougall Date: Tue, 24 May 2022 15:55:35 +0100 Subject: [PATCH 0200/2609] [SCOUT] Adding Declaring Model Engine to Scout docs (#7952) * Adding Declaring Model Engine to Scout docs * Matching styling of docs by pulling in EngineManager namespace as import * formatting Co-authored-by: Taylor Otwell --- scout.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/scout.md b/scout.md index 63b1a6eea11..772863e854f 100644 --- a/scout.md +++ b/scout.md @@ -8,6 +8,7 @@ - [Configuring Model Indexes](#configuring-model-indexes) - [Configuring Searchable Data](#configuring-searchable-data) - [Configuring The Model ID](#configuring-the-model-id) + - [Configuring Search Engines Per Model](#configuring-search-engines-per-model) - [Identifying Users](#identifying-users) - [Database / Collection Engines](#database-and-collection-engines) - [Database Engine](#database-engine) @@ -209,6 +210,34 @@ By default, Scout will use the primary key of the model as model's unique ID / k } } + +### Configuring Search Engines Per Model + +When searching, Scout will typically use the default search engine specified in your application's `scout` configuration file. However, the search engine for a particular model can be changed by overriding the `searchableUsing` method on the model: + + engine('meilisearch'); + } + } + ### Identifying Users From a67705a0da7af2a77aaea944c870de9d214ac0c1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 May 2022 13:44:00 -0500 Subject: [PATCH 0201/2609] wip --- database-testing.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/database-testing.md b/database-testing.md index b149c7b1888..a1d905b88fd 100644 --- a/database-testing.md +++ b/database-testing.md @@ -165,6 +165,14 @@ State transformation methods typically call the `state` method provided by Larav }); } +#### "Trashed" State + +If your Eloquent model can be [soft deleted](/docs/{{version}}/eloquent#soft-deleting), you may invoke the built-in `trashed` state method to indicate that the created model should already be "soft deleted". You do not need to manually define the `trashed` state as it is automatically available to all factories: + + use App\Models\User; + + $user = User::factory()->trashed()->create(); + ### Factory Callbacks From 7ad23cc63157669548c4dea18058629ef0165a7a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 May 2022 13:46:51 -0500 Subject: [PATCH 0202/2609] wip --- migrations.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/migrations.md b/migrations.md index 50f08be9882..ffcbd2336ed 100644 --- a/migrations.md +++ b/migrations.md @@ -269,6 +269,14 @@ The `temporary` method may be used to indicate that the table should be "tempora // ... }); +If you would like to add a "comment" to a database table, you may invoke the `comment` method on the table instance. Table comments are currently only supported by MySQL and Postgres: + + Schema::create('calculations', function (Blueprint $table) { + $table->comment('Business calculations'); + + // ... + }); + ### Updating Tables From 3b9566cfd4d9fa8b3545cea40169f0474f8cd422 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 May 2022 13:49:53 -0500 Subject: [PATCH 0203/2609] formatting --- helpers.md | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/helpers.md b/helpers.md index e7654af8f9e..54f240d8a94 100644 --- a/helpers.md +++ b/helpers.md @@ -46,11 +46,11 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::isAssoc](#method-array-isassoc) [Arr::isList](#method-array-islist) [Arr::keyBy](#method-array-keyby) -[Arr::prependKeysWith](#method-array-prependkeyswith) [Arr::last](#method-array-last) [Arr::only](#method-array-only) [Arr::pluck](#method-array-pluck) [Arr::prepend](#method-array-prepend) +[Arr::prependKeysWith](#method-array-prependkeyswith) [Arr::pull](#method-array-pull) [Arr::query](#method-array-query) [Arr::random](#method-array-random) @@ -631,28 +631,6 @@ The `Arr::keyBy` method keys the array by the given key. If multiple items have ] */ - -#### `Arr::prependKeysWith()` {.collection-method} - -The `Arr::prependKeysWith` prepends all key names of an associative array with the given prefix: - - use Illuminate\Support\Arr; - - $array = [ - 'key' => 'value', - 'key2' => 'value2', - ]; - - $keyed = Arr::prependKeysWith($array, 'prefix.'); - - /* - [ - 'prefix.key' => 'value', - 'prefix.key2' => 'value2', - ] - */ - - #### `Arr::last()` {.collection-method} @@ -734,6 +712,27 @@ If needed, you may specify the key that should be used for the value: // ['name' => 'Desk', 'price' => 100] + +#### `Arr::prependKeysWith()` {.collection-method} + +The `Arr::prependKeysWith` prepends all key names of an associative array with the given prefix: + + use Illuminate\Support\Arr; + + $array = [ + 'name' => 'Desk', + 'price' => 100, + ]; + + $keyed = Arr::prependKeysWith($array, 'product.'); + + /* + [ + 'product.name' => 'Desk', + 'product.price' => 100, + ] + */ + #### `Arr::pull()` {.collection-method} From 7fbbf87d77c53ad11a1e97c5184bb4af3de404ce Mon Sep 17 00:00:00 2001 From: Rahul Dey Date: Wed, 25 May 2022 19:59:56 +0530 Subject: [PATCH 0204/2609] Update eloquent-mutators.md (#7953) --- eloquent-mutators.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 04c86b23911..e743370bf7e 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -80,7 +80,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ -public function address(): Attribute +protected function address(): Attribute { return Attribute::make( get: fn ($value, $attributes) => new Address( @@ -108,7 +108,7 @@ When returning value objects from accessors, any changes made to the value objec However, you may sometimes wish to enable caching for primitive values like strings and booleans, particularly if they are computationally intensive. To accomplish this, you may invoke the `shouldCache` method when defining your accessor: ```php -public function hash(): Attribute +protected function hash(): Attribute { return Attribute::make( get: fn ($value) => bcrypt(gzuncompress($value)), @@ -124,7 +124,7 @@ If you would like to disable the object caching behavior of attributes, you may * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ -public function address(): Attribute +protected function address(): Attribute { return Attribute::make( get: fn ($value, $attributes) => new Address( @@ -188,7 +188,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ -public function address(): Attribute +protected function address(): Attribute { return Attribute::make( get: fn ($value, $attributes) => new Address( From 00562e40c2b4deab3a13fd9660ecc9524f0b2c8e Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 28 May 2022 10:37:27 +1000 Subject: [PATCH 0205/2609] fix sail binary references (#7954) --- artisan.md | 2 +- mix.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artisan.md b/artisan.md index 7e389a8c468..86dba2b6693 100644 --- a/artisan.md +++ b/artisan.md @@ -43,7 +43,7 @@ php artisan help migrate 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: ```shell -./sail artisan list +./vendor/bin/sail artisan list ``` diff --git a/mix.md b/mix.md index 33bea22bf20..ef0fc1c9962 100644 --- a/mix.md +++ b/mix.md @@ -51,8 +51,8 @@ npm -v 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: ```shell -./sail node -v -./sail npm -v +./vendor/bin/sail node -v +./vendor/bin/sail npm -v ``` From 477ab41785109b5a751574be81ef906629bd60d2 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 30 May 2022 15:19:59 +0200 Subject: [PATCH 0206/2609] [9.x] Fix documentation for digits rule (#7957) * Update validation.md * Update validation.md Co-authored-by: Taylor Otwell --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 7f5baa4bc07..b3c1263107f 100644 --- a/validation.md +++ b/validation.md @@ -994,12 +994,12 @@ The field under validation must have a different value than _field_. #### digits:_value_ -The field under validation must be _numeric_ and must have an exact length of _value_. +The integer under validation must have an exact length of _value_. #### digits_between:_min_,_max_ -The field under validation must be _numeric_ and must have a length between the given _min_ and _max_. +The integer validation must have a length between the given _min_ and _max_. #### dimensions From 006f7b81a1d8efa1eb586804aef5da23b868e8e7 Mon Sep 17 00:00:00 2001 From: ecrmnn Date: Mon, 30 May 2022 20:45:33 +0200 Subject: [PATCH 0207/2609] Adds documentation for Arr::map() --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 54f240d8a94..e2897e27a42 100644 --- a/helpers.md +++ b/helpers.md @@ -47,6 +47,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::isList](#method-array-islist) [Arr::keyBy](#method-array-keyby) [Arr::last](#method-array-last) +[Arr::map](#method-array-map) [Arr::only](#method-array-only) [Arr::pluck](#method-array-pluck) [Arr::prepend](#method-array-prepend) @@ -652,6 +653,21 @@ A default value may be passed as the third argument to the method. This value wi $last = Arr::last($array, $callback, $default); + +#### `Arr::map()` {.collection-method} + +The `Arr::map` method iterates through the array and passes each value and key to the given callback. + + use Illuminate\Support\Arr; + + $array = ['firstname' => 'taylor', 'lastname' => 'otwell']; + + $mapped = Arr::map($array, function ($value, $key) { + return ucfirst($value); + }); + + // ['firstname' => 'Taylor', 'lastname' => 'Otwell'] + #### `Arr::only()` {.collection-method} From 4ec51e90f8c1f9af925e906cee1d3529913532b2 Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Tue, 31 May 2022 15:11:35 +0100 Subject: [PATCH 0208/2609] Added documentation for the "--only-vendor" option for the "route:list" command. (#7961) --- routing.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/routing.md b/routing.md index a86a72f9d3d..743b23b1465 100644 --- a/routing.md +++ b/routing.md @@ -146,6 +146,12 @@ In addition, you may instruct Laravel to hide any routes that are defined by thi php artisan route:list --except-vendor ``` +Likewise, you may also instruct Laravel to only show routes that are defined by third-party packages by providing the `--only-vendor` option when executing the `route:list` command: + +```shell +php artisan route:list --only-vendor +``` + ## Route Parameters From 32cd2f6eeae62862b49114c2524049b9b344caf5 Mon Sep 17 00:00:00 2001 From: Daniel Eckermann Date: Tue, 31 May 2022 16:15:41 +0200 Subject: [PATCH 0209/2609] [9.x] Adds documentation for `Str::isJson()` (#7960) * Adds documentation for Str::isJson() * Update helpers.md Co-authored-by: Taylor Otwell --- helpers.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/helpers.md b/helpers.md index 54f240d8a94..df10d396dff 100644 --- a/helpers.md +++ b/helpers.md @@ -185,6 +185,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [isAscii](#method-fluent-str-is-ascii) [isEmpty](#method-fluent-str-is-empty) [isNotEmpty](#method-fluent-str-is-not-empty) +[isJson](#method-fluent-str-is-json) [isUuid](#method-fluent-str-is-uuid) [kebab](#method-fluent-str-kebab) [lcfirst](#method-fluent-str-lcfirst) @@ -2322,6 +2323,25 @@ The `isNotEmpty` method determines if the given string is not empty: // true + +#### `isJson` {.collection-method} + +The `isJson` method determines if a given string is valid JSON: + + use Illuminate\Support\Str; + + $result = Str::isJson('[1,2,3]')); + + // true + + $result = Str::isJson('{"first": "John", "last": "Doe"}')); + + // true + + $result = Str::isJson('{first: "John", last: "Doe"}')); + + // false + #### `isUuid` {.collection-method} From 6194e3d4114d121dc251e17740d291fed660848d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 May 2022 09:22:38 -0500 Subject: [PATCH 0210/2609] formatting --- helpers.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/helpers.md b/helpers.md index e2897e27a42..1e452f79166 100644 --- a/helpers.md +++ b/helpers.md @@ -186,6 +186,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [isAscii](#method-fluent-str-is-ascii) [isEmpty](#method-fluent-str-is-empty) [isNotEmpty](#method-fluent-str-is-not-empty) +[isJson](#method-fluent-str-is-json) [isUuid](#method-fluent-str-is-uuid) [kebab](#method-fluent-str-kebab) [lcfirst](#method-fluent-str-lcfirst) @@ -656,17 +657,17 @@ A default value may be passed as the third argument to the method. This value wi #### `Arr::map()` {.collection-method} -The `Arr::map` method iterates through the array and passes each value and key to the given callback. +The `Arr::map` method iterates through the array and passes each value and key to the given callback. The array value is replaced by the value returned by the callback: use Illuminate\Support\Arr; - $array = ['firstname' => 'taylor', 'lastname' => 'otwell']; + $array = ['first' => 'james', 'last' => 'kirk']; $mapped = Arr::map($array, function ($value, $key) { return ucfirst($value); }); - // ['firstname' => 'Taylor', 'lastname' => 'Otwell'] + // ['first' => 'James', 'last' => 'Kirk'] #### `Arr::only()` {.collection-method} @@ -2338,6 +2339,25 @@ The `isNotEmpty` method determines if the given string is not empty: // true + +#### `isJson` {.collection-method} + +The `isJson` method determines if a given string is valid JSON: + + use Illuminate\Support\Str; + + $result = Str::of('[1,2,3]')->isJson(); + + // true + + $result = Str::of('{"first": "John", "last": "Doe"}')->isJson(); + + // true + + $result = Str::of('{first: "John", last: "Doe"}')->isJson(); + + // false + #### `isUuid` {.collection-method} From 4ce9519f16b86367838bb65526950e22252c9fc9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 May 2022 09:25:29 -0500 Subject: [PATCH 0211/2609] add normal method --- helpers.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/helpers.md b/helpers.md index 1e452f79166..f6ddbf47d81 100644 --- a/helpers.md +++ b/helpers.md @@ -113,6 +113,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::headline](#method-str-headline) [Str::is](#method-str-is) [Str::isAscii](#method-str-is-ascii) +[Str::isJson](#method-str-is-json) [Str::isUuid](#method-str-is-uuid) [Str::kebab](#method-kebab-case) [Str::lcfirst](#method-str-lcfirst) @@ -1455,6 +1456,25 @@ The `Str::isAscii` method determines if a given string is 7 bit ASCII: // false + +#### `Str::isJson()` {.collection-method} + +The `Str::isJson` method determines if the given string is valid JSON: + + use Illuminate\Support\Str; + + $result = Str::isJson('[1,2,3]'); + + // true + + $result = Str::isJson('{"first": "John", "last": "Doe"}'); + + // true + + $result = Str::isJson('{first: "John", last: "Doe"}'); + + // false + #### `Str::isUuid()` {.collection-method} From 80c5f1b679d1980ffa0aac5475eb3c5574b43c15 Mon Sep 17 00:00:00 2001 From: Daniel Eckermann Date: Tue, 31 May 2022 16:30:03 +0200 Subject: [PATCH 0212/2609] [9.x] Adds documentation for `Arr::join()` (#7959) * Adds documentation for Arr::join * Updates link * Formatting Co-authored-by: Taylor Otwell --- helpers.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/helpers.md b/helpers.md index f6ddbf47d81..fe8760687fb 100644 --- a/helpers.md +++ b/helpers.md @@ -45,6 +45,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::hasAny](#method-array-hasany) [Arr::isAssoc](#method-array-isassoc) [Arr::isList](#method-array-islist) +[Arr::join](#method-array-join) [Arr::keyBy](#method-array-keyby) [Arr::last](#method-array-last) [Arr::map](#method-array-map) @@ -613,6 +614,23 @@ The `Arr::isList` method returns `true` if the given array's keys are sequential // false + +#### `Arr::join()` {.collection-method} + +The `Arr::join` method joins array elements with a string. Using this method's second argument, you may also specify the joining string for the final element of the array: + + use Illuminate\Support\Arr; + + $array = ['Tailwind', 'Alpine', 'Laravel', 'Livewire']; + + $joined = Arr::join($array, ', '); + + // Tailwind, Alpine, Laravel, Livewire + + $joined = Arr::join($array, ', ', ' and '); + + // Tailwind, Alpine, Laravel and Livewire + #### `Arr::keyBy()` {.collection-method} From c658a56b4e0bea18990ab5ad15b6cf1f87657bb8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 May 2022 11:00:24 -0500 Subject: [PATCH 0213/2609] wip --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index fe8760687fb..540ac6467b7 100644 --- a/helpers.md +++ b/helpers.md @@ -3696,7 +3696,7 @@ If you would like to manually calculate the number of milliseconds to sleep betw return retry(5, function () { // ... - }, function ($attempt) { + }, function ($attempt, $exception) { return $attempt * 100; }); From 016d1223fb709b59acfe68c6ed0db6ebd25a4eb5 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 4 Jun 2022 05:20:36 +1000 Subject: [PATCH 0214/2609] [9.x] Add validation response example (#7966) * add validation response example * formatting Co-authored-by: Taylor Otwell --- validation.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/validation.md b/validation.md index b3c1263107f..63d157e9aa1 100644 --- a/validation.md +++ b/validation.md @@ -8,6 +8,7 @@ - [Displaying The Validation Errors](#quick-displaying-the-validation-errors) - [Repopulating Forms](#repopulating-forms) - [A Note On Optional Fields](#a-note-on-optional-fields) + - [Validation Error Response Format](#validation-error-response-format) - [Form Request Validation](#form-request-validation) - [Creating Form Requests](#creating-form-requests) - [Authorizing Form Requests](#authorizing-form-requests) @@ -99,7 +100,7 @@ Next, let's take a look at a simple controller that handles incoming requests to 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 `Illuminate\Validation\ValidationException` 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. +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](#validation-error-response-format) will be returned. To get a better understanding of the `validate` method, let's jump back into the `store` method: @@ -202,7 +203,7 @@ In addition, you may copy this file to another translation language directory to #### 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. +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](#validation-error-response-format). This JSON response will be sent with a 422 HTTP status code. #### The `@error` Directive @@ -258,6 +259,34 @@ 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. + +### Validation Error Response Format + +When your application throws a `Illuminate\Validation\ValidationException` exception and the incoming HTTP request is expecting a JSON response, Laravel will automatically format the error messages for you and return a `422 Unprocessable Entity` HTTP response. + +Below, you can review an example of the JSON response format for validation errors. Note that nested error keys are flattened into "dot" notation format: + +```json +{ + "message": "The team name must be a string. (and 4 more errors)", + "errors": { + "team_name": [ + "The team name must be a string.", + "The team name must be at least 1 characters." + ], + "authorization.role": [ + "The selected authorization.role is invalid." + ], + "users.0.email": [ + "The users.0.email field is required." + ], + "users.2.email": [ + "The users.2.email must be a valid email address." + ] + } +} +``` + ## Form Request Validation @@ -309,7 +338,7 @@ So, how are the validation rules evaluated? All you need to do is type-hint the $validated = $request->safe()->except(['name', 'email']); } -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. +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](#validation-error-response-format). #### Adding After Hooks To Form Requests @@ -522,7 +551,7 @@ The `stopOnFirstFailure` method will inform the validator that it should stop va ### Automatic Redirection -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: +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](#validation-error-response-format): Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', From 32f3bfe85a06fcba03e486da29cfa55ef750a7bd Mon Sep 17 00:00:00 2001 From: Aleksandr Medvedev Date: Mon, 6 Jun 2022 18:06:50 +0300 Subject: [PATCH 0215/2609] [9.x] add `classBasename` method for Fluent strings (#7970) --- helpers.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/helpers.md b/helpers.md index 540ac6467b7..e14e8499bd6 100644 --- a/helpers.md +++ b/helpers.md @@ -176,6 +176,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [between](#method-fluent-str-between) [betweenFirst](#method-fluent-str-between-first) [camel](#method-fluent-str-camel) +[classBasename](#method-fluent-str-class-basename) [contains](#method-fluent-str-contains) [containsAll](#method-fluent-str-contains-all) [dirname](#method-fluent-str-dirname) @@ -2181,6 +2182,17 @@ The `camel` method converts the given string to `camelCase`: // fooBar + +#### `classBasename` {.collection-method} + +The `classBasename` method returns the class name of the given class with the class's namespace removed: + + use Illuminate\Support\Str; + + $class = Str::of('Foo\Bar\Baz')->classBasename(); + + // Baz + #### `contains` {.collection-method} From b2eb70e147a9ef25dfc4a9b1f3a30f5f63f3e937 Mon Sep 17 00:00:00 2001 From: Matias Hernan Lauriti Date: Mon, 6 Jun 2022 16:00:31 -0300 Subject: [PATCH 0216/2609] Fixed typo on Authorization -> Inline Authorization (#7972) --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index 67c80938e67..9e432a28955 100644 --- a/authorization.md +++ b/authorization.md @@ -233,7 +233,7 @@ Gate::allowIf(fn ($user) => $user->isAdministrator()); Gate::denyIf(fn ($user) => $user->banned()); ``` -If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an `Illuminate\Auth\Access\AuthorizationException` exception. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler: +If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an `Illuminate\Auth\Access\AuthorizationException` exception. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler. ## Creating Policies From 21c981c355379241e7e2f659270ebae98b38a4f1 Mon Sep 17 00:00:00 2001 From: Matias Hernan Lauriti Date: Mon, 6 Jun 2022 16:00:43 -0300 Subject: [PATCH 0217/2609] Fixed typo on Authorization -> Inline Authorization (#7973) --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index 72c2641f278..12c11f132af 100644 --- a/authorization.md +++ b/authorization.md @@ -233,7 +233,7 @@ Gate::allowIf(fn ($user) => $user->isAdministrator()); Gate::denyIf(fn ($user) => $user->banned()); ``` -If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an `Illuminate\Auth\Access\AuthorizationException` exception. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler: +If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an `Illuminate\Auth\Access\AuthorizationException` exception. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler. ## Creating Policies From f3d1a20f2cb7a291e229487cf13583b2c153e678 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 7 Jun 2022 16:49:27 +0200 Subject: [PATCH 0218/2609] Update collections.md (#7974) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index 799df3fb24a..4c81aa81ea4 100644 --- a/collections.md +++ b/collections.md @@ -1687,7 +1687,7 @@ The `pluck` method also supports retrieving nested values using "dot" notation: $plucked->all(); - // ['Rosa', 'Judith'] + // [['Rosa', 'Judith']] If duplicate keys exist, the last matching element will be inserted into the plucked collection: From ec890baa77d00a6cc8828b83737497c3c56ccf03 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Jun 2022 09:54:56 -0500 Subject: [PATCH 0219/2609] update example --- collections.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/collections.md b/collections.md index 4c81aa81ea4..e006d7da3d0 100644 --- a/collections.md +++ b/collections.md @@ -1676,9 +1676,15 @@ The `pluck` method also supports retrieving nested values using "dot" notation: $collection = collect([ [ + 'name' => 'Laracon', 'speakers' => [ 'first_day' => ['Rosa', 'Judith'], - 'second_day' => ['Angela', 'Kathleen'], + ], + ], + [ + 'name' => 'VueConf', + 'speakers' => [ + 'first_day' => ['Abigail', 'Joey'], ], ], ]); @@ -1687,7 +1693,7 @@ The `pluck` method also supports retrieving nested values using "dot" notation: $plucked->all(); - // [['Rosa', 'Judith']] + // [['Rosa', 'Judith'], ['Abigail', 'Joey']] If duplicate keys exist, the last matching element will be inserted into the plucked collection: From c5fc984bd60771e63947a8080953ce5837d144ff Mon Sep 17 00:00:00 2001 From: Lucas Fiege Date: Fri, 10 Jun 2022 11:32:21 -0300 Subject: [PATCH 0220/2609] Added coverage to SAIL_XDEBUG_MODE (#7978) I added the missing `coverage` value to `SAIL_XDEBUG_MODE` in order to have `php artisan test --coverage` working, this was missing in the docs. --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index d37bc3eba9a..9e76e74b1ef 100644 --- a/sail.md +++ b/sail.md @@ -419,7 +419,7 @@ sail share --subdomain=my-sail-site Laravel Sail's Docker configuration includes support for [Xdebug](https://xdebug.org/), a popular and powerful debugger for PHP. In order to enable Xdebug, you will need to add a few variables to your application's `.env` file to [configure Xdebug](https://xdebug.org/docs/step_debug#mode). To enable Xdebug you must set the appropriate mode(s) before starting Sail: ```ini -SAIL_XDEBUG_MODE=develop,debug +SAIL_XDEBUG_MODE=develop,debug,coverage ``` #### Linux Host IP Configuration From 087dae3b8aa4b1c3d2b00b7f13184599e38e24e8 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Wed, 15 Jun 2022 15:52:49 +0900 Subject: [PATCH 0221/2609] Add other helpful test methods of the mailable (#7983) --- mocking.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mocking.md b/mocking.md index 3d0161cf4d2..cdc976beaed 100644 --- a/mocking.md +++ b/mocking.md @@ -406,12 +406,15 @@ You may pass a closure to the `assertSent`, `assertNotSent`, `assertQueued`, or 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: +When calling the `Mail` facade's assertion methods, the mailable instance accepted by the provided closure exposes helpful methods for examining the mailable: Mail::assertSent(OrderShipped::class, function ($mail) use ($user) { return $mail->hasTo($user->email) && $mail->hasCc('...') && - $mail->hasBcc('...'); + $mail->hasBcc('...') && + $mail->hasReplyTo('...') && + $mail->hasFrom('...') && + $mail->hasSubject('...'); }); You may have noticed that there are two methods for asserting that mail was not sent: `assertNotSent` and `assertNotQueued`. Sometimes you may wish to assert that no mail was sent **or** queued. To accomplish this, you may use the `assertNothingOutgoing` and `assertNotOutgoing` methods: From 378b3faeb70edb807ac44a118e97fa616ad57ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=9B=D0=B3i?= <99682351+bvtterfly@users.noreply.github.com> Date: Wed, 15 Jun 2022 11:26:34 +0430 Subject: [PATCH 0222/2609] [9.x] Fix typo (#7982) * Fix Typo in billing.md * Fix Typo in envoy.md * Fix Typo in sail.md * Revert "Fix Typo in sail.md" This reverts commit a2232c638e2aa7cfa75ea4a7f6254961b3922e71. --- billing.md | 4 ++-- envoy.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/billing.md b/billing.md index adc0fd9e1b8..986789b1ec7 100644 --- a/billing.md +++ b/billing.md @@ -1575,7 +1575,7 @@ Alternatively, you may use the `invoiceFor` method to make a "one-off" charge ag $user->invoiceFor('One Time Fee', 500); -Although the `invoiceFor` method is available for you to use, it is recommendeded that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. +Although the `invoiceFor` method is available for you to use, it is recommended that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. > {note} The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. @@ -1659,7 +1659,7 @@ To retrieve the upcoming invoice for a customer, you may use the `upcomingInvoic $invoice = $user->upcomingInvoice(); -Similary, if the customer has multiple subscriptions, you can also retrieve the upcoming invoice for a specific subscription: +Similarly, if the customer has multiple subscriptions, you can also retrieve the upcoming invoice for a specific subscription: $invoice = $user->subscription('default')->upcomingInvoice(); diff --git a/envoy.md b/envoy.md index 88483b61e31..89c0af51c6f 100644 --- a/envoy.md +++ b/envoy.md @@ -323,7 +323,7 @@ Envoy also supports sending notifications to [Telegram](https://telegram.org) af ### Microsoft Teams -Envoy also supports sending notifications to [Microsoft Teams](https://www.microsoft.com/en-us/microsoft-teams) after each task is executed. The `@microsoftTeams` directive accepts a Teams Webhook (required), a message, theme color (success, info, warning, error), and an array of options. You may retrieve your Teams Webook by creating a new [incoming webhook](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook). The Teams API has many other attributes to customize your message box like title, summary, and sections. You can find more information on the [Microsoft Teams documentation](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#example-of-connector-message). You should pass the entire Webhook URL into the `@microsoftTeams` directive: +Envoy also supports sending notifications to [Microsoft Teams](https://www.microsoft.com/en-us/microsoft-teams) after each task is executed. The `@microsoftTeams` directive accepts a Teams Webhook (required), a message, theme color (success, info, warning, error), and an array of options. You may retrieve your Teams Webhook by creating a new [incoming webhook](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook). The Teams API has many other attributes to customize your message box like title, summary, and sections. You can find more information on the [Microsoft Teams documentation](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#example-of-connector-message). You should pass the entire Webhook URL into the `@microsoftTeams` directive: ```blade @finished From 3585226f39c5e529efb5af0b31555730ca8a2913 Mon Sep 17 00:00:00 2001 From: Mitchell Smith <48261027+mitchierichie@users.noreply.github.com> Date: Wed, 15 Jun 2022 11:35:26 -0600 Subject: [PATCH 0223/2609] document append method on Eloquent Collection (#7987) * document append method on Eloquent Collection * formatting Co-authored-by: Taylor Otwell --- eloquent-collections.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/eloquent-collections.md b/eloquent-collections.md index d60839b3c48..00adcc34ab6 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -62,6 +62,7 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe
+[append](#method-append) [contains](#method-contains) [diff](#method-diff) [except](#method-except) @@ -79,8 +80,17 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe
+ +#### `append($attributes)` {.collection-method .first-collection-method} + +The `append` method may be used to indicate that an attribute should be [appended](/docs/{{version}}/eloquent-serialization#appending-values-to-json) for every model in the collection. This method accepts an array of attributes or a single attribute: + + $users->append('team'); + + $users->append(['team', 'is_admin']); + -#### `contains($key, $operator = null, $value = null)` {.collection-method .first-collection-method} +#### `contains($key, $operator = null, $value = null)` {.collection-method} The `contains` method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance: From 6582c3d4f375cd321b52d858fb0d7d1e5c03d51a Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Wed, 15 Jun 2022 14:53:23 -0400 Subject: [PATCH 0224/2609] Suggest public properties for Bus::assertDispatched testing (#7989) --- queues.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index fdcec3966e7..65d66a27a8d 100644 --- a/queues.md +++ b/queues.md @@ -183,7 +183,7 @@ Job classes are very simple, normally containing only a `handle` method that is * * @var \App\Models\Podcast */ - protected $podcast; + public $podcast; /** * Create a new job instance. @@ -1711,7 +1711,7 @@ When a particular job fails, you may want to send an alert to your users or reve * * @var \App\Podcast */ - protected $podcast; + public $podcast; /** * Create a new job instance. From db45785f9f74e5eae99eb5a39ef79cc820f464e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Nikolaou?= Date: Fri, 17 Jun 2022 05:13:01 +0300 Subject: [PATCH 0225/2609] Display `Collection::slide()` method alphabetically (#7992) --- collections.md | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/collections.md b/collections.md index e006d7da3d0..abd6197c261 100644 --- a/collections.md +++ b/collections.md @@ -178,11 +178,11 @@ For the majority of the remaining collection documentation, we'll discuss each m [search](#method-search) [shift](#method-shift) [shuffle](#method-shuffle) -[sliding](#method-sliding) [skip](#method-skip) [skipUntil](#method-skipuntil) [skipWhile](#method-skipwhile) [slice](#method-slice) +[sliding](#method-sliding) [sole](#method-sole) [some](#method-some) [sort](#method-sort) @@ -2031,35 +2031,6 @@ The `shuffle` method randomly shuffles the items in the collection: // [3, 2, 5, 1, 4] - (generated randomly) - -#### `sliding()` {.collection-method} - -The `sliding` method returns a new collection of chunks representing a "sliding window" view of the items in the collection: - - $collection = collect([1, 2, 3, 4, 5]); - - $chunks = $collection->sliding(2); - - $chunks->toArray(); - - // [[1, 2], [2, 3], [3, 4], [4, 5]] - -This is especially useful in conjunction with the [`eachSpread`](#method-eachspread) method: - - $transactions->sliding(2)->eachSpread(function ($previous, $current) { - $current->total = $previous->total + $current->amount; - }); - -You may optionally pass a second "step" value, which determines the distance between the first item of every chunk: - - $collection = collect([1, 2, 3, 4, 5]); - - $chunks = $collection->sliding(3, step: 2); - - $chunks->toArray(); - - // [[1, 2, 3], [3, 4, 5]] - #### `skip()` {.collection-method} @@ -2140,6 +2111,35 @@ If you would like to limit the size of the returned slice, pass the desired size The returned slice will preserve keys by default. If you do not wish to preserve the original keys, you can use the [`values`](#method-values) method to reindex them. + +#### `sliding()` {.collection-method} + +The `sliding` method returns a new collection of chunks representing a "sliding window" view of the items in the collection: + + $collection = collect([1, 2, 3, 4, 5]); + + $chunks = $collection->sliding(2); + + $chunks->toArray(); + + // [[1, 2], [2, 3], [3, 4], [4, 5]] + +This is especially useful in conjunction with the [`eachSpread`](#method-eachspread) method: + + $transactions->sliding(2)->eachSpread(function ($previous, $current) { + $current->total = $previous->total + $current->amount; + }); + +You may optionally pass a second "step" value, which determines the distance between the first item of every chunk: + + $collection = collect([1, 2, 3, 4, 5]); + + $chunks = $collection->sliding(3, step: 2); + + $chunks->toArray(); + + // [[1, 2, 3], [3, 4, 5]] + #### `sole()` {.collection-method} From d5989c4041223e7a56b26ee3494b61e2cbd02673 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 17 Jun 2022 19:51:33 -1000 Subject: [PATCH 0226/2609] document fakeExcept --- mocking.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mocking.md b/mocking.md index cdc976beaed..8e316d8c8fd 100644 --- a/mocking.md +++ b/mocking.md @@ -312,6 +312,12 @@ If you only want to fake event listeners for a specific set of events, you may p $order->update([...]); } +You may fake all events except for a set of specified events using the `fakeExcept` method: + + Event::fakeExcept([ + OrderCreated::class, + ]); + ### Scoped Event Fakes From 6038060e9eddf7b73d88eca61745679037a00d70 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 17 Jun 2022 20:00:37 -1000 Subject: [PATCH 0227/2609] add note on tags --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index 0c58d55bac0..6f9057e9c73 100644 --- a/cache.md +++ b/cache.md @@ -266,7 +266,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` a value into 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. Items stored via tags may not be accessed without also providing the tags that were used to store the value. For example, let's access a tagged cache and `put` a value into the cache: Cache::tags(['people', 'artists'])->put('John', $john, $seconds); From f9cac1f8b2919378194fbce6fcc838a93ec31eaa Mon Sep 17 00:00:00 2001 From: GoodM4ven <81492351+GoodM4ven@users.noreply.github.com> Date: Mon, 20 Jun 2022 20:26:45 +0300 Subject: [PATCH 0228/2609] [9.x] Noted about explicit base-url generation for MinIO in Sail (#7988) * Noted about explicit base-url generation for MinIO * Update sail.md Co-authored-by: GoodM4ven Co-authored-by: Taylor Otwell --- sail.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sail.md b/sail.md index 9e76e74b1ef..c2b31be6ede 100644 --- a/sail.md +++ b/sail.md @@ -241,6 +241,12 @@ AWS_ENDPOINT=http://minio:9000 AWS_USE_PATH_STYLE_ENDPOINT=true ``` +In order for Laravel's Flysystem integration to generate proper URLs when using MinIO, you should define the `AWS_URL` environment variable so that it matches your application's local URL and includes the bucket name in the URL path: + +```ini +AWS_URL=http://localhost:9000/local +``` + ## Running Tests From 882d3315b3f89b2d7ce73a855c2ad0435a351098 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jun 2022 09:52:52 -0500 Subject: [PATCH 0229/2609] document whenQueryingForLongerThan --- database.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/database.md b/database.md index 9f43cf69922..002939b2fed 100644 --- a/database.md +++ b/database.md @@ -6,6 +6,7 @@ - [Running SQL Queries](#running-queries) - [Using Multiple Database Connections](#using-multiple-database-connections) - [Listening For Query Events](#listening-for-query-events) + - [Monitoring Cumulative Query Time](#monitoring-cumulative-query-time) - [Database Transactions](#database-transactions) - [Connecting To The Database CLI](#connecting-to-the-database-cli) @@ -276,6 +277,43 @@ If you would like to specify a closure that is invoked for each SQL query execut } } + +### Monitoring Cumulative Query Time + +A common performance bottleneck of modern web applications is the amount of time they spend querying databases. Thankfully, Laravel can invoke a closure or callback of your choice when it spends too much time querying the database during a single request. To get started, provide a query time threshold (in milliseconds) and closure to the `whenQueryingForLongerThan` method. You may invoke this method in the `boot` method of a [service provider](/docs/{{version}}/providers): + + ## Database Transactions From f9cdd17837dd0e7377c6469feae59a6c753520a4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jun 2022 09:56:33 -0500 Subject: [PATCH 0230/2609] document nested eager loading --- eloquent-relationships.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index dc4f322d36c..a047a1d401f 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1512,6 +1512,15 @@ To eager load a relationship's relationships, you may use "dot" syntax. For exam $books = Book::with('author.contacts')->get(); +Alternatively, you may specify nested eager loaded relationships by providing a nested array to the `with` method, which can be convenient when eager loading multiple nested relationships: + + $books = Book::with([ + 'author' => [ + 'contacts', + 'publisher', + ], + ])->get(); + #### Nested Eager Loading `morphTo` Relationships From f1d820b2d17eb638adf107665f50b4ce1a7dc034 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jun 2022 09:59:33 -0500 Subject: [PATCH 0231/2609] document host --- requests.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/requests.md b/requests.md index 4e2daa8675d..1c2888089f6 100644 --- a/requests.md +++ b/requests.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Interacting With The Request](#interacting-with-the-request) - [Accessing The Request](#accessing-the-request) - - [Request Path & Method](#request-path-and-method) + - [Request Path, Host, & Method](#request-path-and-method) - [Request Headers](#request-headers) - [Request IP Address](#request-ip-address) - [Content Negotiation](#content-negotiation) @@ -97,7 +97,7 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` route } -### Request Path & Method +### Request Path, Host, & Method 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. @@ -136,6 +136,15 @@ If you would like to append query string data to the current URL, you may call t $request->fullUrlWithQuery(['type' => 'phone']); + +#### Retrieving The Request Host + +You may retrieve the "host" of the incoming request via the `host`, `httpHost`, and `schemeAndHttpHost` methods: + + $request->host(); + $request->httpHost(); + $request->schemeAndHttpHost(); + #### Retrieving The Request Method From 43a0cda873a95050c2d45ee020e2c43bf2adde0a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jun 2022 10:26:00 -0500 Subject: [PATCH 0232/2609] attachable objects --- mail.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/mail.md b/mail.md index 8628200c6be..00ebe332861 100644 --- a/mail.md +++ b/mail.md @@ -11,6 +11,7 @@ - [View Data](#view-data) - [Attachments](#attachments) - [Inline Attachments](#inline-attachments) + - [Attachable Objects](#attachable-objects) - [Tags & Metadata](#tags-and-metadata) - [Customizing The Symfony Message](#customizing-the-symfony-message) - [Markdown Mailables](#markdown-mailables) @@ -471,6 +472,65 @@ If you already have a raw image data string you wish to embed into an email temp ``` + +### Attachable Objects + +While attaching files to messages via simple string paths is often sufficient, in many cases the attachable entities within your application are represented by classes. For example, if your application is attaching a photo to a message, your application may also have a `Photo` model that represents that photo. When that is the case, wouldn't it be convenient to simply pass the `Photo` model to the `attach` method? Attachable objects allow you to do just that. + +To get started, implement the `Illuminate\Contracts\Mail\Attachable` interface on the object that will be attachable to messages. This interface dictates that your class defines a `toMailAttachment` method that returns an `Illuminate\Mail\Attachment` instance: + + view('photos.resized') + ->attach($this->photo); + } + +Of course, attachment data may be stored on a remote file storage service such as Amazon S3. So, Laravel also allows you to generate attachment instances from data that is stored on one of your application's [filesystem disks](/docs/{{version}}/filesystem): + + // Create an attachment from a file on your default disk... + return Attachment::fromStorage($this->path); + + // Create an attachment from a file on a specific disk... + return Attachment::fromStorageDisk('backblaze', $this->path); + +In addition, you may create attachment instances via data that you have in memory. To accomplish this, provide a closure to the `fromData` method. The closure should return the raw data that represents the attachment: + + return Attachment::fromData(fn () => $this->content); + +Laravel also provides additional methods that you may use to customize your attachments. For example, you may use the `as` and `withMime` methods to customize the file's name and MIME type: + + return Attachment::fromPath('/path/to/file') + ->as('Photo Name') + ->withMime('image/jpeg'); + ### Tags & Metadata From 986bee2b0fda1a82098f80592cd04b450dba70e8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jun 2022 10:29:10 -0500 Subject: [PATCH 0233/2609] add tip for attachable objects --- notifications.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/notifications.md b/notifications.md index 7ccbcf5ae8b..c84750063b8 100644 --- a/notifications.md +++ b/notifications.md @@ -495,6 +495,8 @@ To add attachments to an email notification, use the `attach` method while build ->attach('/path/to/file'); } +> {tip} The `attach` method offered by notification mail messages also accepts [attachable objects](/docs/{{version}}/mail#attachable-objects). Please consult the comprehensive [attachable object documentation](/docs/{{version}}/mail#attachable-objects) to learn more. + When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: /** From 577128efe2f246a2044133c19eb304ae258c9441 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jun 2022 10:41:20 -0500 Subject: [PATCH 0234/2609] document invokable rules --- validation.md | 65 ++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/validation.md b/validation.md index 63d157e9aa1..dad2902ba3b 100644 --- a/validation.md +++ b/validation.md @@ -1873,54 +1873,35 @@ Occasionally, you may want to attach additional validation rules to your default 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 -php artisan make:rule Uppercase +php artisan make:rule Uppercase --invokable ``` -Once the rule has been created, we are ready to define its behavior. A rule object contains two methods: `passes` and `message`. The `passes` method receives the attribute value and name, and should return `true` or `false` depending on whether the attribute value is valid or not. The `message` method should return the validation error message that should be used when validation fails: +Once the rule has been created, we are ready to define its behavior. A rule object contains a single method: `__invoke`. This method receives the attribute name, its value, and a callback that should be invoked on failure with the validation error message: ['required', 'string', new Uppercase], ]); +#### Translating Validation Messages + +Instead of providing a literal error message to the `$fail` closure, you may also provide a [translation string key](/docs/{{version}}/localization) and instruct Laravel to translate the error message: + + if (strtoupper($value) !== $value) { + $fail('validation.uppercase')->translate(); + } + +If necessary, you may provide placeholder replacements and the preferred language as the first and second arguments to the `translate` method: + + $fail('validation.location')->translate([ + 'value' => $this->value, + ], 'fr') + #### Accessing Additional Data If your custom validation rule class needs to access all of the other data undergoing validation, your rule class may implement the `Illuminate\Contracts\Validation\DataAwareRule` interface. This interface requires your class to define a `setData` method. This method will automatically be invoked by Laravel (before validation proceeds) with all of the data under validation: @@ -1937,10 +1932,10 @@ If your custom validation rule class needs to access all of the other data under namespace App\Rules; - use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\DataAwareRule; + use Illuminate\Contracts\Validation\InvokableRule; - class Uppercase implements Rule, DataAwareRule + class Uppercase implements DataAwareRule, InvokableRule { /** * All of the data under validation. @@ -1971,10 +1966,10 @@ Or, if your validation rule requires access to the validator instance performing namespace App\Rules; - use Illuminate\Contracts\Validation\Rule; + use Illuminate\Contracts\Validation\InvokableRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; - class Uppercase implements Rule, ValidatorAwareRule + class Uppercase implements InvokableRule, ValidatorAwareRule { /** * The validator instance. @@ -2036,7 +2031,7 @@ For a custom rule to run even when an attribute is empty, the rule must imply th To generate a new implicit rule object, you may use the `make:rule` Artisan command with the `--implicit` option : ```shell -php artisan make:rule Uppercase --implicit +php artisan make:rule Uppercase --invokable --implicit ``` > {note} An "implicit" rule only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you. From f621bcdd2f26a949149dfa8d3958857dc37a9b54 Mon Sep 17 00:00:00 2001 From: Tony Messias Date: Tue, 21 Jun 2022 13:56:37 -0300 Subject: [PATCH 0235/2609] Fix attachable object example missing contract implementation (#7999) --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 00ebe332861..4173a62cd36 100644 --- a/mail.md +++ b/mail.md @@ -487,7 +487,7 @@ To get started, implement the `Illuminate\Contracts\Mail\Attachable` interface o use Illuminate\Database\Eloquent\Model; use Illuminate\Mail\Attachment; - class Photo extends Model + class Photo extends Model implements Attachable { /** * Get the attachable representation of the model. From fd6690129cb92dc36a001ab900d17e16d899be73 Mon Sep 17 00:00:00 2001 From: Dennis <3907144+denniseilander@users.noreply.github.com> Date: Wed, 22 Jun 2022 16:41:35 +0200 Subject: [PATCH 0236/2609] [9.x] Mention the default timeout of a worker (#7998) * Mention the default timeout of a worker * Update queues.md * Update queues.md Co-authored-by: Taylor Otwell --- queues.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index 65d66a27a8d..d5fb40477a4 100644 --- a/queues.md +++ b/queues.md @@ -1034,7 +1034,7 @@ In this example, the job is released for ten seconds if the application is unabl > {note} The `pcntl` PHP extension must be installed in order to specify job timeouts. -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). +Often, you know roughly how long you expect your queued jobs to take. For this reason, Laravel allows you to specify a "timeout" value. By default, the timeout value is 60 seconds. 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: @@ -1560,7 +1560,7 @@ In your `config/queue.php` configuration file, each queue connection defines a ` #### Worker Timeouts -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): +The `queue:work` Artisan command exposes a `--timeout` option. By default, the `--timeout` value is 60 seconds. 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): ```shell php artisan queue:work --timeout=60 From cdb5660e54250d25eb19ef8c79e65a5dbc4a99b1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 24 Jun 2022 00:01:26 +1000 Subject: [PATCH 0237/2609] [9.x] Document global `fake()` helper (#8002) * migrate examples to use global helper * document fake() helper * Update helpers.md * Update database-testing.md Co-authored-by: Taylor Otwell --- database-testing.md | 14 +++++++------- helpers.md | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/database-testing.md b/database-testing.md index a1d905b88fd..5f309bbeb71 100644 --- a/database-testing.md +++ b/database-testing.md @@ -84,8 +84,8 @@ To see an example of how to write a factory, take a look at the `database/factor public function definition() { return [ - 'name' => $this->faker->name(), - 'email' => $this->faker->unique()->safeEmail(), + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), @@ -95,7 +95,7 @@ To see an example of how to write a factory, take a look at the `database/factor As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. -Via the `faker` property, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing. +Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing. > {tip} You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. @@ -515,8 +515,8 @@ To define a relationship within your model factory, you will typically assign a { return [ 'user_id' => User::factory(), - 'title' => $this->faker->title(), - 'content' => $this->faker->paragraph(), + 'title' => fake()->title(), + 'content' => fake()->paragraph(), ]; } @@ -534,8 +534,8 @@ If the relationship's columns depend on the factory that defines it you may assi 'user_type' => function (array $attributes) { return User::find($attributes['user_id'])->type; }, - 'title' => $this->faker->title(), - 'content' => $this->faker->paragraph(), + 'title' => fake()->title(), + 'content' => fake()->paragraph(), ]; } diff --git a/helpers.md b/helpers.md index e14e8499bd6..975f5125ef3 100644 --- a/helpers.md +++ b/helpers.md @@ -294,6 +294,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [encrypt](#method-encrypt) [env](#method-env) [event](#method-event) +[fake](#method-fake) [filled](#method-filled) [info](#method-info) [logger](#method-logger) @@ -3526,6 +3527,27 @@ The `event` function dispatches the given [event](/docs/{{version}}/events) to i event(new UserRegistered($user)); + +#### `fake()` {.collection-method} + +The `fake` function resolves a [Faker](https://github.com/FakerPHP/Faker) singleton from the container, which can be useful when creating fake data in model factories, database seeding, tests, and prototyping views: + +```blade +@for($i = 0; $i < 10; $i++) +
+
Name
+
{{ fake()->name() }}
+ +
Email
+
{{ fake()->unique()->safeEmail() }}
+
+@endfor +``` + +By default, the `fake` function will utilize the `app.faker_locale` configuration option in your `config/app.php` configuration file; however, you may also specify the locale by passing it to the `fake` function. Each locale will resolve an individual singleton: + + fake('nl_NL')->name() + #### `filled()` {.collection-method} From 0a09408b74de49c3bb13817e602ed1be21e8a1a3 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 23 Jun 2022 23:03:32 +0900 Subject: [PATCH 0238/2609] [9.x] Add parameters to the closure for whenQueryingForLongerThan (#8001) * Add parameters to the closure for whenQueryingForLongerThan * Update database.md Co-authored-by: Taylor Otwell --- database.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database.md b/database.md index 002939b2fed..9eaf476feb2 100644 --- a/database.md +++ b/database.md @@ -286,6 +286,7 @@ A common performance bottleneck of modern web applications is the amount of time namespace App\Providers; + use Illuminate\Database\Connection; use Illuminate\Support\Facades\DB; use Illuminate\Support\ServiceProvider; @@ -308,7 +309,7 @@ A common performance bottleneck of modern web applications is the amount of time */ public function boot() { - DB::whenQueryingForLongerThan(500, function () { + DB::whenQueryingForLongerThan(500, function (Connection $connection) { // Notify development team... }); } From f1c74bfcf58130f1a32a6b00035160620e30924e Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 23 Jun 2022 16:31:18 +0200 Subject: [PATCH 0239/2609] Document auth and verified middleware usage (#8005) * Update verification.md * Update verification.md * Update verification.md Co-authored-by: Taylor Otwell --- verification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/verification.md b/verification.md index 9caf5baf084..b3ed062aad3 100644 --- a/verification.md +++ b/verification.md @@ -110,11 +110,11 @@ Sometimes a user may misplace or accidentally delete the email address verificat ### Protecting Routes -[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 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. Typically, this middleware is paired with the `auth` middleware: Route::get('/profile', function () { // Only verified users may access this route... - })->middleware('verified'); + })->middleware(['auth', '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). From 7a1e8e7ce9be71715b1edcd317eee728611b8168 Mon Sep 17 00:00:00 2001 From: Lucas Kruger Date: Mon, 27 Jun 2022 15:24:49 +0200 Subject: [PATCH 0240/2609] Fix example in 'Using An Existing Client Instance' (#8007) Looking at the laravel-echo [https://github.com/laravel/echo/blob/cd608aac40f136b9a50da8b1785615cadf95b04c/src/connector/pusher-connector.ts#L29](source), it expects a Pusher instance, not the class. --- broadcasting.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index c4023c28b05..7f6a0fbad92 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -189,13 +189,16 @@ If you already have a pre-configured Pusher Channels client instance that you wo ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -const client = require('pusher-js'); +const options = { + broadcaster: 'pusher', + key: 'your-pusher-channels-key' +} window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key', - client: client + ...options, + client: new Pusher(options.key, options) }); ``` From 464b08ab70182204940d931de8731f0160da9443 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 29 Jun 2022 00:35:27 +1000 Subject: [PATCH 0241/2609] [9.x] Vite (#7984) * Relocate and streamline mix docs * Add Vite docs * Update references to Mix * Update entry points * Document resolvePageComponent helper function * remove Vite suffix * restore JS import * Update mix heading * Add tip to direct mix users to docs or upgrade. * update SSR docs * remove mix installation docs * Add vite link to mix page * improve title * Remove misleading wording * Improve clarity * Title casing * Improve clarity * Adjust entrypoints * Fix link * Fix versioning * Improving wording * Remove confusing wording * Improve wording * Improve wording * Update Vite stylesheet section * Fix build command * Update ToC * remove ziggy alias * formatting * add tips * add frontend page * move page * simplify installation docs * continuing formatting of installation story * augment starter kit docs * singluar aliases * fix plugin names * Add code fence for consistency * Add missing links * migration guide * show removal of css file to match addition below * Document working with a secure dev server * Update frontend.md Co-authored-by: Jess Archer * formatting and wording * adjust sail wording * wording * add link to frontend * frontend docs review * review vite docs * Fix typo Co-authored-by: Tim MacDonald Co-authored-by: Taylor Otwell --- authentication.md | 4 +- broadcasting.md | 18 +- documentation.md | 4 +- frontend.md | 196 ++++++++++++++++++++++ installation.md | 132 +++++++-------- mix.md | 419 +--------------------------------------------- sail.md | 2 +- sanctum.md | 4 +- starter-kits.md | 35 ++-- vite.md | 395 +++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 688 insertions(+), 521 deletions(-) create mode 100644 frontend.md create mode 100644 vite.md diff --git a/authentication.md b/authentication.md index cc71a308214..54e1ae6bb3c 100644 --- a/authentication.md +++ b/authentication.md @@ -79,7 +79,7 @@ _Laravel Breeze_ is a simple, minimal implementation of all of Laravel's authent _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 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 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](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 @@ -123,7 +123,7 @@ First, you should [install a Laravel application starter kit](/docs/{{version}}/ 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](https://tailwindcss.com). Breeze also offers an [Inertia](https://inertiajs.com) based scaffolding option using Vue or React. -[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. +[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 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 diff --git a/broadcasting.md b/broadcasting.md index 7f6a0fbad92..d190a292e7c 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -163,13 +163,14 @@ Once Echo is installed, you are ready to create a fresh Echo instance in your ap ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -window.Pusher = require('pusher-js'); +window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', - key: process.env.MIX_PUSHER_APP_KEY, - cluster: process.env.MIX_PUSHER_APP_CLUSTER, + key: import.meta.env.VITE_PUSHER_APP_KEY, + cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, forceTLS: true }); ``` @@ -180,7 +181,7 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run dev ``` -> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix). +> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). #### Using An Existing Client Instance @@ -219,12 +220,13 @@ Once Echo is installed, you are ready to create a fresh Echo instance in your ap ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -window.Pusher = require('pusher-js'); +window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', - key: process.env.MIX_ABLY_PUBLIC_KEY, + key: import.meta.env.VITE_ABLY_PUBLIC_KEY, wsHost: 'realtime-pusher.ably.io', wsPort: 443, disableStats: true, @@ -232,7 +234,7 @@ window.Echo = new Echo({ }); ``` -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. +Note that our Ably Echo configuration references a `VITE_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: @@ -240,7 +242,7 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run dev ``` -> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix). +> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). ## Concept Overview diff --git a/documentation.md b/documentation.md index 5da7f822cba..be6a940e2ad 100644 --- a/documentation.md +++ b/documentation.md @@ -6,6 +6,7 @@ - [Installation](/docs/{{version}}/installation) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) + - [Frontend](/docs/{{version}}/frontend) - [Starter Kits](/docs/{{version}}/starter-kits) - [Deployment](/docs/{{version}}/deployment) - ## Architecture Concepts @@ -22,6 +23,7 @@ - [Responses](/docs/{{version}}/responses) - [Views](/docs/{{version}}/views) - [Blade Templates](/docs/{{version}}/blade) + - [Bundling Assets](/docs/{{version}}/vite) - [URL Generation](/docs/{{version}}/urls) - [Session](/docs/{{version}}/session) - [Validation](/docs/{{version}}/validation) @@ -32,7 +34,6 @@ - [Broadcasting](/docs/{{version}}/broadcasting) - [Cache](/docs/{{version}}/cache) - [Collections](/docs/{{version}}/collections) - - [Compiling Assets](/docs/{{version}}/mix) - [Contracts](/docs/{{version}}/contracts) - [Events](/docs/{{version}}/events) - [File Storage](/docs/{{version}}/filesystem) @@ -83,6 +84,7 @@ - [Homestead](/docs/{{version}}/homestead) - [Horizon](/docs/{{version}}/horizon) - [Jetstream](https://jetstream.laravel.com) + - [Mix](/docs/{{version}}/mix) - [Octane](/docs/{{version}}/octane) - [Passport](/docs/{{version}}/passport) - [Sail](/docs/{{version}}/sail) diff --git a/frontend.md b/frontend.md new file mode 100644 index 00000000000..9a7a24008ac --- /dev/null +++ b/frontend.md @@ -0,0 +1,196 @@ +# Frontend + +- [Introduction](#introduction) +- [Using PHP](#using-php) + - [PHP & Blade](#php-and-blade) + - [Livewire](#livewire) + - [Starter Kits](#php-starter-kits) +- [Using Vue / React](#using-vue-react) + - [Inertia](#inertia) + - [Starter Kits](#inertia-starter-kits) +- [Bundling Assets](#bundling-assets) + + +## Introduction + +Laravel is a backend framework that provides all of the features you need to build modern web applications, such as [routing](/docs/{{version}}/routing), [validation](/docs/{{version}}/validation), [caching](/docs/{{version}}/cache), [queues](/docs/{{version}}/queues), [file storage](/docs/{{version}}/filesystem), and more. However, we believe it's important to offer developers a beautiful full-stack experience, including powerful approaches for building your application's frontend. + +There are two primary ways to tackle frontend development when building an application with Laravel, and which approach you choose is determined by whether you would like to build your frontend by leveraging PHP or by using JavaScript frameworks such as Vue and React. We'll discuss both of these options below so that you can make an informed decision regarding the best approach to frontend development for your application. + + +## Using PHP + + +### PHP & Blade + +In the past, most PHP applications rendered HTML to the browser using simple HTML templates interspersed with PHP `echo` statements which render data that was retrieved from a database during the request: + +```blade +
+ + Hello, name; ?>
+ +
+``` + +In Laravel, this approach to rendering HTML can still be achieved using [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Blade is an extremely light-weight templating language that provides convenient, short syntax for displaying data, iterating over data, and more: + +```blade +
+ @foreach ($users as $user) + Hello, {{ $user->name }}
+ @endforeach +
+``` + +When building applications in this fashion, form submissions and other page interactions typically receive an entirely new HTML document from the server and the entire page is re-rendered by the browser. Even today, many applications may be perfectly suited to having their frontends constructed in this way using simple Blade templates. + + +#### Growing Expectations + +However, as user expectations regarding web applications have matured, many developers have found the need to build more dynamic frontends with interactions that feel more polished. In light of this, some developers choose to begin building their application's frontend using JavaScript frameworks such as Vue and React. + +Others, preferring to stick with the backend language they are comfortable with, have developed solutions that allow the construction of modern web application UIs while still primarily utilizing their backend language of choice. For example, in the Rails ecosystem, this has spurred the creation of libraries such as [Turbo](https://turbo.hotwired.dev/) [Hotwire](https://hotwired.dev/), and [Stimulus](https://stimulus.hotwired.dev/). + +Within the Laravel ecosystem, the need to create modern, dynamic frontends by primarily using PHP has led to the creation of [Laravel Livewire](https://laravel-livewire.com) and [Alpine.js](https://alpinejs.dev/). + + +### Livewire + +[Laravel Livewire](https://laravel-livewire.com) is a framework for building Laravel powered frontends that feel dynamic, modern, and alive just like frontends built with modern JavaScript frameworks like Vue and React. + +When using Livewire, you will create Livewire "components" that render a discrete portion of your UI and expose methods and data that can be invoked and interacted with from your application's frontend. For example, a simple "Counter" component might look like the following: + +```php +count++; + } + + public function render() + { + return view('livewire.counter'); + } +} +``` + +And, the corresponding template for the counter would be written like so: + +```blade +
+ +

{{ $count }}

+
+``` + +As you can see, Livewire enables you to write new HTML attributes such as `wire:click` that connect your Laravel application's frontend and backend. In addition, you can render your component's current state using simple Blade expressions. + +For many, Livewire has revolutionized frontend development with Laravel, allowing them to stay within the comfort of Laravel while constructing modern, dynamic web applications. Typically, developers using Livewire will also utilize [Alpine.js](https://alpinejs.dev/) to "sprinkle" JavaScript onto their frontend only where it is needed, such as in order to render a dialog window. + +If you're new to Laravel, we recommend getting familiar with the basic usage of [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Then, consult the official [Laravel Livewire documentation](https://laravel-livewire.com/docs) to learn how to take your application to the next level with interactive Livewire components. + + +### Starter Kits + +If you would like to build your frontend using PHP and Livewire, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using [Blade](/docs/{{version}}/blade) and [Tailwind](https://tailwindcss.com) so that you can simply start building your next big idea. + + +## Using Vue / React + +Although it's possible to build modern frontends using Laravel and Livewire, many developers still prefer to leverage the power of a JavaScript framework like Vue or React. This allows developers to take advantage of the rich ecosystem of JavaScript packages and tools available via NPM. + +However, without additional tooling, pairing Laravel with Vue or React would leave us needing to solve a variety of complicated problems such as client-side routing, data hydration, and authentication. Client-side routing is often simplified by using opinionated Vue / React frameworks such as [Nuxt](https://nuxtjs.org/) and [Next](https://nextjs.org/); however, data hydration and authentication remain complicated and cumbersome problems to solve when pairing a backend framework like Laravel with these frontend frameworks. + +In addition, developers are left maintaining two separate code repositories, often needing to coordinate maintenance, releases, and deployments across both repositories. While these problems are not insurmountable, we don't believe it's a productive or enjoyable way to develop applications. + + +### Inertia + +Thankfully, Laravel offers the best of both worlds. [Inertia](https://inertiajs.com) bridges the gap between your Laravel application and your modern Vue or React frontend, allowing you to build full-fledged, modern frontends using Vue or React while leveraging Laravel routes and controllers for routing, data hydration, and authentication — all within a single code repository. With this approach, you can enjoy the full power of both Laravel and Vue / React without crippling the capabilities of either tool. + +After installing Inertia into your Laravel application, you will write routes and controllers like normal. However, instead of returning a Blade template from your controller, you will return an Inertia page: + +```php + User::findOrFail($id) + ]); + } +} +``` + +An Inertia page corresponds to a Vue or React component, typically stored within the `resources/js/Pages` directory of your application. The data given to the page via the `Inertia::render` method will be used to hydrate the "props" of the page component: + +```vue + + + +``` + +As you can see, Inertia allows you to leverage the full power of Vue or React when building your frontend, while providing a light-weight bridge between your Laravel powered backend and your JavaScript powered frontend. + +#### Server-Side Rendering + +If you're concerned about diving into Inertia because your application requires server-side rendering, don't worry. Inertia offers [server-side rendering support](https://inertiajs.com/server-side-rendering). And, when deploying your application via [Laravel Forge](https://forge.laravel.com), it's a breeze to ensure that Inertia's server-side rendering process is always running. + + +### Starter Kits + +If you would like to build your frontend using Inertia and Vue / React, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits#breeze-and-inertia) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using Inertia, Vue / React, [Tailwind](https://tailwindcss.com), and [Vite](https://vitejs.dev) so that you can start building your next big idea. + + +## Bundling Assets + +Regardless of whether you choose to develop your frontend using Blade and Livewire or Vue / React and Inertia, you will likely need to bundle your application's CSS into production ready assets. Of course, if you choose to build your application's frontend with Vue or React, you will also need to bundle your components into browser ready JavaScript assets. + +By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. Vite provides lightning-fast build times and near instantaneous Hot Module Replacement (HMR) during local development. In all new Laravel application's, including those using our [starter kits](/docs/{{version}}/starter-kits), you will find a `vite.config.js` file that loads our light-weight Laravel Vite plugin that makes Vite a joy to use with Laravel applications. + +The fastest way to get started with Laravel and Vite is by beginning your application's development using [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), our simplest starter kit that jump-starts your application by providing frontend and backend authentication scaffolding. + +> {tip} For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). diff --git a/installation.md b/installation.md index dea895d1500..aee003d0e5b 100644 --- a/installation.md +++ b/installation.md @@ -3,14 +3,15 @@ - [Meet Laravel](#meet-laravel) - [Why Laravel?](#why-laravel) - [Your First Laravel Project](#your-first-laravel-project) +- [Laravel & Docker](#laravel-and-docker) - [Getting Started On macOS](#getting-started-on-macos) - [Getting Started On Windows](#getting-started-on-windows) - [Getting Started On Linux](#getting-started-on-linux) - [Choosing Your Sail Services](#choosing-your-sail-services) - - [Installation Via Composer](#installation-via-composer) - [Initial Configuration](#initial-configuration) - [Environment Based Configuration](#environment-based-configuration) - [Directory Configuration](#directory-configuration) + - [Databases & Migrations](#databases-and-migrations) - [Next Steps](#next-steps) - [Laravel The Full Stack Framework](#laravel-the-fullstack-framework) - [Laravel The API Backend](#laravel-the-api-backend) @@ -48,7 +49,30 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ## Your First Laravel Project -We want it to be as easy as possible to get started with Laravel. There are a variety of options for developing and running a Laravel project on your local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel project using [Docker](https://www.docker.com). +Before creating your first Laravel project, you should ensure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS, PHP and Composer can be installed via [Homebrew](https://brew.sh/). In addition, we recommend [installing Node and NPM](https://nodejs.org). + +After you have installed PHP and Composer, you may create a new Laravel project via the Composer `create-project` command: + +```nothing +composer create-project laravel/laravel example-app +``` + +After the project has been created, start Laravel's local development server using the Laravel's Artisan CLI `serve` command: + +```nothing +cd example-app + +php artisan serve +``` + +Once you have started the Artisan development server, your application will be accessible in your web browser at `http://localhost:8000`. Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). + +> {tip} If you would like a head start when developing your Laravel application, 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. + + +## Laravel & Docker + +We want it to be as easy as possible to get started with Laravel regardless of your preferred operating system. So, there are a variety of options for developing and running a Laravel project on your local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel project using [Docker](https://www.docker.com). Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local machine's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your local machine. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). @@ -158,97 +182,56 @@ You may instruct Sail to install a default [Devcontainer](/docs/{{version}}/sail curl -s "https://laravel.build/example-app?with=mysql,redis&devcontainer" | bash ``` - -### Installation Via Composer - -If your local machine already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: - -```shell -composer create-project laravel/laravel example-app - -cd example-app - -php artisan serve -``` - -Once you have started the Artisan development server, you may access your application at `http://localhost:8000`. - - -#### The Laravel Installer - -Or, you may install the Laravel Installer as a global Composer dependency: - -```shell -composer global require laravel/installer - -laravel new example-app + +## Initial Configuration -cd example-app +All of the configuration files for the Laravel framework are stored in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you. -php artisan serve -``` +Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. -Make sure to place Composer's system-wide vendor bin directory in your `$PATH` so the `laravel` executable can be located by your system. This directory exists in different locations based on your operating system; however, some common locations include: + +### Environment Based Configuration -
+Since many of Laravel's configuration option values may vary depending on whether your application is running on your local machine or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. -- macOS: `$HOME/.composer/vendor/bin` -- Windows: `%USERPROFILE%\AppData\Roaming\Composer\vendor\bin` -- GNU / Linux Distributions: `$HOME/.config/composer/vendor/bin` or `$HOME/.composer/vendor/bin` +Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. -
+> {tip} For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). -For convenience, the Laravel installer can also create a Git repository for your new project. To indicate that you want a Git repository to be created, pass the `--git` flag when creating a new project: + +### Directory Configuration -```shell -laravel new example-app --git -``` +Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files that exist within your application. -This command will initialize a new Git repository for your project and automatically commit the base Laravel skeleton. The `git` flag assumes you have properly installed and configured Git. You can also use the `--branch` flag to set the initial branch name: + +### Databases & Migrations -```shell -laravel new example-app --git --branch="main" -``` +Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with a MySQL database and will access the database at `127.0.0.1`. If you are developing on macOS and need to install MySQL, Postgres, or Redis locally, you may find it convenient to utilize [DBngin](https://dbngin.com/). -Instead of using the `--git` flag, you may also use the `--github` flag to create a Git repository and also create a corresponding private repository on GitHub: +If you do not want to install MySQL or Postgres on your local machine, you can always use a [SQLite](https://www.sqlite.org/index.html) database. SQLite is a small, fast, self-contained database engine. To get started, create a SQLite database by creating an empty SQLite file. Typically, this file will exist within the `database` directory of your Laravel application: ```shell -laravel new example-app --github +touch database/database.sqlite ``` -The created repository will then be available at `https://github.com//example-app`. The `github` flag assumes you have properly installed the [GitHub CLI](https://cli.github.com) and are authenticated with GitHub. Additionally, you should have `git` installed and properly configured. If needed, you can pass additional flags that are supported by the GitHub CLI: +Next, update your `.env` configuration file to use Laravel's `sqlite` database driver. You may remove the other database configuration options: -```shell -laravel new example-app --github="--public" +```ini +DB_CONNECTION=sqlite # [tl! add] +DB_CONNECTION=mysql # [tl! remove] +DB_HOST=127.0.0.1 # [tl! remove] +DB_PORT=3306 # [tl! remove] +DB_DATABASE=laravel # [tl! remove] +DB_USERNAME=root # [tl! remove] +DB_PASSWORD= # [tl! remove] ``` -You may use the `--organization` flag to create the repository under a specific GitHub organization: +Once you have configured your SQLite database, you may run your application's [database migrations](/docs/{{version}}/migrations), which will create your application's database tables: ```shell -laravel new example-app --github="--public" --organization="laravel" +php artisan migrate ``` - -## Initial Configuration - -All of the configuration files for the Laravel framework are stored in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you. - -Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. - - -### Environment Based Configuration - -Since many of Laravel's configuration option values may vary depending on whether your application is running on your local machine or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. - -Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. - -> {tip} For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). - - -### Directory Configuration - -Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files that exist within your application. - ## Next Steps @@ -259,6 +242,7 @@ Now that you have created your Laravel project, you may be wondering what to lea - [Request Lifecycle](/docs/{{version}}/lifecycle) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) +- [Frontend](/docs/{{version}}/frontend) - [Service Container](/docs/{{version}}/container) - [Facades](/docs/{{version}}/facades) @@ -269,11 +253,11 @@ How you want to use Laravel will also dictate the next steps on your journey. Th ### Laravel The Full Stack Framework -Laravel may serve as a full stack framework. By "full stack" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or a single-page application hybrid technology like [Inertia.js](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel. +Laravel may serve as a full stack framework. By "full stack" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or a single-page application hybrid technology like [Inertia](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel. -If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://laravel-livewire.com) and [Inertia.js](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications. +If this is how you plan to use Laravel, you may want to check out our documentation on [frontend development](/docs/{{version}}/frontend), [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://laravel-livewire.com) and [Inertia](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications. -If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Laravel Mix](/docs/{{version}}/mix). +If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Vite](/docs/{{version}}/vite). > {tip} If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). diff --git a/mix.md b/mix.md index ef0fc1c9962..b7b1f9563b4 100644 --- a/mix.md +++ b/mix.md @@ -1,23 +1,6 @@ -# Compiling Assets (Mix) +# Laravel Mix - [Introduction](#introduction) -- [Installation & Setup](#installation) -- [Running Mix](#running-mix) -- [Working With Stylesheets](#working-with-stylesheets) - - [Tailwind CSS](#tailwindcss) - - [PostCSS](#postcss) - - [Sass](#sass) - - [URL Processing](#url-processing) - - [Source Maps](#css-source-maps) -- [Working With JavaScript](#working-with-scripts) - - [Vue](#vue) - - [React](#react) - - [Vendor Extraction](#vendor-extraction) - - [Custom Webpack Configuration](#custom-webpack-configuration) -- [Versioning / Cache Busting](#versioning-and-cache-busting) -- [Browsersync Reloading](#browsersync-reloading) -- [Environment Variables](#environment-variables) -- [Notifications](#notifications) ## Introduction @@ -33,402 +16,4 @@ mix.js('resources/js/app.js', 'public/js') 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 - - -#### Installing Node - -Before running Mix, you must first ensure that Node.js and NPM are installed on your machine: - -```shell -node -v -npm -v -``` - -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: - -```shell -./vendor/bin/sail node -v -./vendor/bin/sail npm -v -``` - - -#### 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 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: - -```shell -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 are 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: - -```shell -// Run all Mix tasks... -npm run dev - -// Run all Mix tasks and minify output... -npm run prod -``` - - -#### Watching Assets For Changes - -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: - -```shell -npm run watch -``` - -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: - -```shell -npm run watch-poll -``` - - -## Working With Stylesheets - -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. - - -### Tailwind 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: - -```shell -npm install - -npm install -D tailwindcss - -npx tailwindcss init -``` - -The `init` command will generate a `tailwind.config.js` file. The `content` section of this file allows you to configure the paths to all of your HTML templates, JavaScript components, and any other source files that contain Tailwind class names so that any CSS classes that are not used within these files will be purged from your production CSS build: - -```js -content: [ - './storage/framework/views/*.php', - './resources/**/*.blade.php', - './resources/**/*.js', - './resources/**/*.vue', - "./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php", -], -``` - -Next, you should add each of Tailwind's "layers" to your application's `resources/css/app.css` file: - -```css -@tailwind base; -@tailwind components; -@tailwind utilities; -``` - -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: - -```js -mix.js('resources/js/app.js', 'public/js') - .postCss('resources/css/app.css', 'public/css', [ - require('tailwindcss'), - ]); -``` - -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: - -```blade - - - - - -``` - - -### PostCSS - -[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. - -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: - -```js -mix.postCss('resources/css/app.css', 'public/css', [ - require('postcss-custom-properties') -]); -``` - -Or, you may execute `postCss` with no additional plugins in order to achieve simple CSS compilation and minification: - -```js -mix.postCss('resources/css/app.css', 'public/css'); -``` - - -### Sass - -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: - -```js -mix.sass('resources/sass/app.scss', 'public/css'); -``` - -You 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: - -```js -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: - -```css -.example { - background: url('https://github.com/images/example.png'); -} -``` - -> {note} Absolute paths for any given `url()` will be excluded from URL-rewriting. For example, `url('https://github.com/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: - -```css -.example { - background: url(https://github.com/images/example.png?d41d8cd98f00b204e9800998ecf8427e); -} -``` - -As useful as this feature may be, your existing folder structure may already be configured in a way you like. If this is the case, you may disable `url()` rewriting like so: - -```js -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: - -```css -.example { - background: url("https://github.com/images/thing.png"); -} -``` - - -### 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: - -```js -mix.js('resources/js/app.js', 'public/js') - .sourceMaps(); -``` - - -#### Style Of Source Mapping - -Webpack offers a variety of [source mapping styles](https://webpack.js.org/configuration/devtool/#devtool). By default, Mix's source mapping style is set to `eval-source-map`, which provides a fast rebuild time. If you want to change the mapping style, you may do so using the `sourceMaps` method: - -```js -let productionSourceMaps = false; - -mix.js('resources/js/app.js', 'public/js') - .sourceMaps(productionSourceMaps, 'source-map'); -``` - - -## Working With JavaScript - -Mix provides several features to help you work with your JavaScript files, such as compiling modern ECMAScript, module bundling, minification, and concatenating plain JavaScript files. Even better, this all works seamlessly, without requiring an ounce of custom configuration: - -```js -mix.js('resources/js/app.js', 'public/js'); -``` - -With this single line of code, you may now take advantage of: - -
- -- The latest EcmaScript syntax. -- Modules -- Minification for production environments. - -
- - -### Vue - -Mix will automatically install the Babel plugins necessary for Vue single-file component compilation support when using the `vue` method. No further configuration is required: - -```js -mix.js('resources/js/app.js', 'public/js') - .vue(); -``` - -Once your JavaScript has been compiled, you can reference it in your application: - -```blade - - - - - -``` - - -### React - -Mix can automatically install the Babel plugins necessary for React support. To get started, add a call to the `react` method: - -```js -mix.js('resources/js/app.jsx', 'public/js') - .react(); -``` - -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: - -```blade - - - - - -``` - - -### Vendor Extraction - -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: - -```js -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 snippet above as an example, Mix will generate the following files: - -
- -- `public/js/manifest.js`: *The Webpack manifest runtime* -- `public/js/vendor.js`: *Your vendor libraries* -- `public/js/app.js`: *Your application code* - -
- -To avoid JavaScript errors, be sure to load these files in the proper order: - -```html - - - -``` - - -### Custom Webpack Configuration - -Occasionally, you may need to manually modify the underlying Webpack configuration. For example, you might have a special loader or plugin that needs to be referenced. - -Mix provides a useful `webpackConfig` method that allows you to merge any short Webpack configuration overrides. This is particularly appealing, as it doesn't require you to copy and maintain your own copy of the `webpack.config.js` file. The `webpackConfig` method accepts an object, which should contain any [Webpack-specific configuration](https://webpack.js.org/configuration/) that you wish to apply. - -```js -mix.webpackConfig({ - resolve: { - modules: [ - path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js') - ] - } -}); -``` - - -## 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 automatically handle this for you using the `version` method. - -The `version` method will append a unique hash to the filenames of all compiled files, allowing for more convenient cache busting: - -```js -mix.js('resources/js/app.js', 'public/js') - .version(); -``` - -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: - -```blade - -``` - -Because versioned files are usually unnecessary in development, you may instruct the versioning process to only run during `npm run prod`: - -```js -mix.js('resources/js/app.js', 'public/js'); - -if (mix.inProduction()) { - mix.version(); -} -``` - - -#### 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 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: - -```shell -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 for this by calling the `mix.browserSync()` method: - -```js -mix.browserSync('laravel.test'); -``` - -[BrowserSync options](https://browsersync.io/docs/options) may be specified by passing a JavaScript object to the `browserSync` method: - -```js -mix.browserSync({ - proxy: 'laravel.test' -}); -``` - -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 your `webpack.mix.js` script by prefixing one of the environment variables in your `.env` file with `MIX_`: - -```ini -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. However, you will need to restart the task if the environment variable's value changes while the task is running: - -```js -process.env.MIX_SENTRY_DSN_PUBLIC -``` - - -## Notifications - -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: - -```js -mix.disableNotifications(); -``` +> {tip} Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [official Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [Vite migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). diff --git a/sail.md b/sail.md index c2b31be6ede..ad39f0e1954 100644 --- a/sail.md +++ b/sail.md @@ -187,7 +187,7 @@ Node commands may be executed using the `node` command while NPM commands may be ```shell sail node --version -sail npm run prod +sail npm run dev ``` If you wish, you may use Yarn instead of NPM: diff --git a/sanctum.md b/sanctum.md index 1218328acbd..a27eea67b3c 100644 --- a/sanctum.md +++ b/sanctum.md @@ -340,9 +340,9 @@ Next, in order for Pusher's authorization requests to succeed, you will need to ```js window.Echo = new Echo({ broadcaster: "pusher", - cluster: process.env.MIX_PUSHER_APP_CLUSTER, + cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, encrypted: true, - key: process.env.MIX_PUSHER_APP_KEY, + key: import.meta.env.VITE_PUSHER_APP_KEY, authorizer: (channel, options) => { return { authorize: (socketId, callback) => { diff --git a/starter-kits.md b/starter-kits.md index 18f7cce7549..093b6970811 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -3,6 +3,7 @@ - [Introduction](#introduction) - [Laravel Breeze](#laravel-breeze) - [Installation](#laravel-breeze-installation) + - [Breeze & Blade](#breeze-and-blade) - [Breeze & React / Vue](#breeze-and-inertia) - [Breeze & Next.js / API](#breeze-and-next) - [Laravel Jetstream](#laravel-jetstream) @@ -17,7 +18,7 @@ While you are welcome to use these starter kits, they are not required. You are ## Laravel Breeze -[Laravel Breeze](https://github.com/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 default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). +[Laravel Breeze](https://github.com/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 default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Or, Breeze can scaffold your application using Vue or React and [Inertia](https://inertiajs.com). Breeze provides a wonderful starting point for beginning a fresh Laravel application and is also great choice for projects that plan to take their Blade templates to the next level with [Laravel Livewire](https://laravel-livewire.com). @@ -26,23 +27,20 @@ Breeze provides a wonderful starting point for beginning a fresh Laravel applica ### Installation -First, you should [create a new Laravel application](/docs/{{version}}/installation), configure your database, and run your [database migrations](/docs/{{version}}/migrations): +First, you should [create a new Laravel application](/docs/{{version}}/installation), configure your database, and run your [database migrations](/docs/{{version}}/migrations). Once you have created a new Laravel application, you may install Laravel Breeze using Composer: ```shell -curl -s https://laravel.build/example-app | bash - -cd example-app - -php artisan migrate +composer require laravel/breeze --dev ``` -Once you have created a new Laravel application, you may install Laravel Breeze using Composer: +Once Breeze is installed, you may scaffold your application using one of the Breeze "stacks" discussed in the documentation below. -```shell -composer require laravel/breeze --dev -``` + +### Breeze & Blade + +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 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: +The default Breeze "stack" is the Blade stack, which utilizes simple [Blade templates](/docs/{{version}}/blade) to render your application's frontend. The Blade stack may be installed by invoking the `breeze:install` command with no other additional arguments. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: ```shell php artisan breeze:install @@ -54,14 +52,14 @@ php artisan migrate 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). +> {tip} To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). ### Breeze & React / Vue -Laravel Breeze also offers React and Vue scaffolding via an [Inertia.js](https://inertiajs.com) frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers. +Laravel Breeze also offers React and Vue scaffolding via an [Inertia](https://inertiajs.com) frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers. -Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel. To use an Inertia stack, specify `vue` or `react` as your desired stack when executing the `breeze:install` Artisan command: +Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel and lightning-fast [Vite](https://vitejs.dev) compilation. To use an Inertia stack, specify `vue` or `react` as your desired stack when executing the `breeze:install` Artisan command. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: ```shell php artisan breeze:install vue @@ -75,6 +73,11 @@ npm run dev php artisan migrate ``` +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. + + +#### Server-Side Rendering + If you would like Breeze to scaffold support for [Inertia SSR](https://inertiajs.com/server-side-rendering), you may provide the `ssr` option when invoking the `breeze:install` command: ```shell @@ -105,6 +108,6 @@ Finally, you are ready to pair this backend with the frontend of your choice. A 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. +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](https://inertiajs.com) driven frontend scaffolding. Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/2.x/introduction.html). diff --git a/vite.md b/vite.md new file mode 100644 index 00000000000..36d4b8f1ce4 --- /dev/null +++ b/vite.md @@ -0,0 +1,395 @@ +# Bundling Assets (Vite) + +- [Introduction](#introduction) +- [Installation & Setup](#installation) + - [Installing Node](#installing-node) + - [Installing Vite And The Laravel Plugin](#installing-vite-and-laravel-plugin) + - [Configuring Vite](#configuring-vite) + - [Loading Your Scripts And Styles](#loading-your-scripts-and-styles) +- [Running Vite](#running-vite) +- [Working With JavaScript](#working-with-scripts) + - [Aliases](#aliases) + - [Vue](#vue) + - [React](#react) + - [Inertia](#inertia) + - [URL Processing](#url-processing) +- [Working With Stylesheets](#working-with-stylesheets) +- [Custom Base URLs](#custom-base-urls) +- [Environment Variables](#environment-variables) +- [Server-Side Rendering (SSR)](#ssr) + + +## Introduction + +[Vite](https://vitejs.dev) is a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. When building applications with Laravel, you will typically use Vite to bundle your application's CSS and JavaScript files into production ready assets. + +Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production. + +> {tip} Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). + + +#### Choosing Between Vite And Laravel Mix + +Before transitioning to Vite, new Laravel applications utilized [Mix](https://laravel-mix.com/), which is powered by [webpack](https://webpack.js.org/), when bundling assets. Vite focuses on providing a faster and more productive experience when building rich JavaScript applications. If you are developing a Single Page Application (SPA), including those developed with tools like [Inertia](https://inertiajs.com), Vite will be the perfect fit. + +Vite also works well with traditional server-side rendered applications with JavaScript "sprinkles", including those using [Livewire](https://laravel-livewire.com). However, it lacks some features that Laravel Mix supports, such as the ability to copy arbitrary assets into the build that are not referenced directly in your JavaScript application. + + +#### Migrating Back To Mix + +Have you started a new Laravel application using our Vite scaffolding but need to move back to Laravel Mix and webpack? No problem. Please consult our [official guide on migrating from Vite to Mix](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-vite-to-laravel-mix). + + +## Installation & Setup + +> {tip} The following documentation discusses how to manually install and configure the Laravel Vite plugin. However, Laravel's [starter kits](/docs/{{version}}/starter-kits) already include all of this scaffolding and are the fastest way to get started with Laravel and Vite. + + +### Installing Node + +You must ensure that Node.js and NPM are installed before running Vite and the Laravel plugin: + +```sh +node -v +npm -v +``` + +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](https://laravel.com/docs/{{version}}/sail), you may invoke Node and NPM through Sail: + +```sh +./vendor/bin/sail node -v +./vendor/bin/sail npm -v +``` + + +### Installing Vite And The Laravel Plugin + +Within a fresh installation of Laravel, you will find a `package.json` file in the root of your application's directory structure. The default `package.json` file already includes everything you need to get started using Vite and the Laravel plugin. You may install your application's frontend dependencies via NPM: + +```sh +npm install +``` + + +### Configuring Vite + +Vite is configured via a `vite.config.js` file in the root of your project. You are free to customize this file based on your needs, and you may also install any other plugins your application requires, such as `@vitejs/plugin-vue` or `@vitejs/plugin-react`. + +The Laravel Vite plugin requires you to specify the entry points for your application. These may be JavaScript or CSS files, and include preprocessed languages such as TypeScript, JSX, TSX, and Sass. + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/css/app.css', + 'resources/js/app.js', + ]), + ], +}); +``` + +If you are building an SPA, including applications built using Inertia, Vite works best without CSS entry points: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/css/app.css', // [tl! remove] + 'resources/js/app.js', + ]), + ], +}); +``` + +Instead, you should import your CSS via JavaScript. Typically, this would be done in your application's `resources/js/app.js` file: + +```js +import './bootstrap'; +import '../css/app.css'; // [tl! add] +``` + +The Laravel plugin also supports multiple entry points and advanced configuration options such as [SSR entry points](#ssr). + + +#### Working With A Secure Development Server + +If your development web server is running on HTTPS, include Valet's [secure command](/docs/{{version}}/valet#securing-sites), you may run into issues connecting to the Vite development server. You may configure Vite to also run on HTTPS by adding the following to your `vite.config.js` configuration file: + +```js +export default defineConfig({ + // ... + server: { // [tl! add] + https: true, // [tl! add] + host: 'localhost', // [tl! add] + }, // [tl! add] +}); +``` + +You will also need to accept the certificate warning for Vite's development server in your browser by following the "Local" link in your console when running the `npm run dev` command. + + +### Loading Your Scripts And Styles + +With your Vite entry points configured, you only need reference them in a `@vite()` Blade directive that you add to the `` of your application's root template: + +```blade + + + {{-- ... --}} + + @vite(['resources/css/app.css', 'resources/js/app.js']) + +``` + +If you're importing your CSS via JavaScript, you only need to include the JavaScript entry point: + +```blade + + + {{-- ... --}} + + @vite('resources/js/app.js') + +``` + +The `@vite` directive will automatically detect the Vite development server and inject the Vite client to enable Hot Module Replacement. In build mode, the directive will load your compiled and versioned assets, including any imported CSS. + + +## Running Vite + +There are two ways you can run Vite. You may run the development server via the `dev` command, which is useful while developing locally. The development server will automatically detect changes to your files and instantly reflect them in any open browser windows. + +Or, running the `build` command will version and bundle your application's assets and get them ready for you to deploy to production: + +```shell +# Run the Vite development server... +npm run dev + +# Build and version the assets for production... +npm run build +``` + + +## Working With JavaScript + + +### Aliases + +By default, The Laravel plugin provides a common alias to help you hit the ground running and conveniently import your application's assets: + +```js +{ + '@' => 'resources/js' +} +``` + +You may overwrite the `'@'` alias by adding your own to the `vite.config.js` configuration file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel(['resources/ts/app.tsx']), + ], + resolve: { + alias: { + '@': 'resources/ts', + }, + }, +}); +``` + + +### Vue + +There are a few additional options you will need to include in the `vite.config.js` configuration file when using the Vue plugin with the Laravel plugin: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + plugins: [ + laravel(['resources/js/app.js']), + vue({ + template: { + transformAssetUrls: { + // The Vue plugin will re-write asset URLs, when referenced + // in Single File Components, to point to the Laravel web + // server. Setting this to `null` allows the Laravel plugin + // to instead re-write asset URLs to point to the Vite + // server instead. + base: null, + + // The Vue plugin will parse absolute URLs and treat them + // as absolute paths to files on disk. Setting this to + // `false` will leave absolute URLs un-touched so they can + // reference assets in the public directly as expected. + includeAbsolute: false, + }, + }, + }), + ], +}); +``` + +> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Vue, and Vite. + + +### React + +When using Vite with React, you will need to ensure that any files containing JSX have a `.jsx` or `.tsx` extension, remembering to update your entry point, if required, as [shown above](#configuring-vite). You will also need to include the additional `@viteReactRefresh` Blade directive alongside your existing `@vite` directive. + +```blade +@viteReactRefresh +@vite('resources/js/app.jsx') +``` + +The `@viteReactRefresh` directive must be called before the `@vite` directive. + +> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, React, and Vite. + + +### Inertia + +The Laravel Vite plugin provides a convenient `resolvePageComponent` function to help you resolve your Inertia page components. Below is an example of the helper in use with Vue 3; however, you may also utilize the function in other frameworks such as React: + +```js +import { createApp, h } from 'vue'; +import { createInertiaApp } from '@inertiajs/inertia-vue3'; +import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; + +createInertiaApp({ + resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')), + setup({ el, App, props, plugin }) { + createApp({ render: () => h(App, props) }) + .use(plugin) + .mount(el) + }, +}); +``` + +> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. + + +### URL Processing + +When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of things to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory. + +When referencing relative asset paths, you should remember that the paths are relative to the file where they are referenced. Any assets referenced via a relative path will be re-written, versioned, and bundled by Vite. + +Consider the following project structure: + +```nothing +public/ + taylor.png +resources/ + js/ + Pages/ + Welcome.vue + images/ + abigail.png +``` + +The following example demonstrates how Vite will treat relative and absolute URLs: + +```html + + + + + +``` + + +## Working With Stylesheets + +You can learn more about Vite's CSS support within the [Vite documentation](https://vitejs.dev/guide/features.html#css). If you are using PostCSS plugins such as [Tailwind](https://tailwindcss.com), you may create a `postcss.config.js` file in the root of your project and Vite will automatically apply it: + +```js +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; +``` + + +## Custom Base URLs + +If your Vite compiled assets are deployed to a domain separate from your application, such as via a CDN, you must specify the `ASSET_URL` environment variable within your application's `.env` file: + +```env +ASSET_URL=https://cdn.example.com +``` + +After configuring the asset URL, all re-written URLs to your assets will be prefixed with the configured value: + +```nothing +https://cdn.example.com/build/assets/app.9dce8d17.js +``` + +Remember that [absolute URLs are not re-written by Vite](#url-processing), so they will not be prefixed. + + +## Environment Variables + +You may inject environment variables into your JavaScript by prefixing them with `VITE_` in your application's `.env` file: + +```env +VITE_SENTRY_DSN_PUBLIC=http://example.com +``` + +You may access injected environment variables via the `import.meta.env` object: + +```js +import.meta.env.VITE_SENTRY_DSN_PUBLIC +``` + + +## Server-Side Rendering (SSR) + +The Laravel Vite plugin makes it painless to set up server-side rending with Vite. To get started, create an SSR entry point at `resources/js/ssr.js` and specify the entry point by passing a configuration option to the Laravel plugin: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: 'resources/js/app.js', + ssr: 'resources/js/ssr.js', + }), + ], +}); +``` + +To ensure you don't forget to rebuild the SSR entry point, we recommend augmenting the "build" script in your application's `package.json` to create your SSR build: + +```json +"scripts": { + "dev": "vite", + "build": "vite build" // [tl! remove] + "build": "vite build && vite build --ssr" // [tl! add] +} +``` + +Then, to build and start the SSR server, you may run the following commands: + +```sh +npm run build +node storage/ssr/ssr.js +``` + +> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. From fff551bdfae4929c8fa44aacab4dbf7261052502 Mon Sep 17 00:00:00 2001 From: damiantw Date: Wed, 29 Jun 2022 09:44:54 -0400 Subject: [PATCH 0242/2609] Update vite.md (#8014) Co-authored-by: Damian Crisafulli --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 36d4b8f1ce4..483ca59a4fa 100644 --- a/vite.md +++ b/vite.md @@ -119,7 +119,7 @@ The Laravel plugin also supports multiple entry points and advanced configuratio #### Working With A Secure Development Server -If your development web server is running on HTTPS, include Valet's [secure command](/docs/{{version}}/valet#securing-sites), you may run into issues connecting to the Vite development server. You may configure Vite to also run on HTTPS by adding the following to your `vite.config.js` configuration file: +If your development web server is running on HTTPS, including Valet's [secure command](/docs/{{version}}/valet#securing-sites), you may run into issues connecting to the Vite development server. You may configure Vite to also run on HTTPS by adding the following to your `vite.config.js` configuration file: ```js export default defineConfig({ From c628a0aab6acdbf4c794ebb9cefe34511de44bca Mon Sep 17 00:00:00 2001 From: Danyell Noe Date: Wed, 29 Jun 2022 07:46:06 -0600 Subject: [PATCH 0243/2609] Update collections.md (#8012) $tinyFix = 'Just a missing semicolon'; --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index abd6197c261..6ddce5993c2 100644 --- a/collections.md +++ b/collections.md @@ -2641,7 +2641,7 @@ The `undot` method expands a single-dimensional collection that uses "dot" notat 'address.suburb' => 'Detroit', 'address.state' => 'MI', 'address.postcode' => '48219' - ]) + ]); $person = $person->undot(); From af4e0a84ebb34881972182ac9241403d3930c7d1 Mon Sep 17 00:00:00 2001 From: Eric Jensen Date: Wed, 29 Jun 2022 08:46:46 -0500 Subject: [PATCH 0244/2609] add leading slash (#8013) --- vite.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vite.md b/vite.md index 483ca59a4fa..9d233396222 100644 --- a/vite.md +++ b/vite.md @@ -185,7 +185,7 @@ By default, The Laravel plugin provides a common alias to help you hit the groun ```js { - '@' => 'resources/js' + '@' => '/resources/js' } ``` @@ -201,7 +201,7 @@ export default defineConfig({ ], resolve: { alias: { - '@': 'resources/ts', + '@': '/resources/ts', }, }, }); From fa71412da8a9f156e02d19150c7d1fae4f0bff7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEuris?= Date: Wed, 29 Jun 2022 17:47:41 +0300 Subject: [PATCH 0245/2609] Clarify octane max-requests defaults (#8010) --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index e660c61182c..48659f3e271 100644 --- a/octane.md +++ b/octane.md @@ -257,7 +257,7 @@ php artisan octane:start --workers=4 --task-workers=6 ### Specifying The Max Request Count -To help prevent stray memory leaks, Octane can gracefully restart a worker once it has handled a given number of requests. To instruct Octane to do this, you may use the `--max-requests` option: +To help prevent stray memory leaks, Octane gracefully restarts any worker once it has handled 500 requests. To adjust this number, you may use the `--max-requests` option: ```shell php artisan octane:start --max-requests=250 From b10a7d8f400ffbaf27cc9a4e558268f29f5368eb Mon Sep 17 00:00:00 2001 From: Guillaume Briday <8252238+guillaumebriday@users.noreply.github.com> Date: Thu, 30 Jun 2022 15:31:01 +0200 Subject: [PATCH 0246/2609] Adding link to Rails website in Frontend docs (#8018) --- frontend.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend.md b/frontend.md index 9a7a24008ac..b67562396ae 100644 --- a/frontend.md +++ b/frontend.md @@ -50,7 +50,7 @@ When building applications in this fashion, form submissions and other page inte However, as user expectations regarding web applications have matured, many developers have found the need to build more dynamic frontends with interactions that feel more polished. In light of this, some developers choose to begin building their application's frontend using JavaScript frameworks such as Vue and React. -Others, preferring to stick with the backend language they are comfortable with, have developed solutions that allow the construction of modern web application UIs while still primarily utilizing their backend language of choice. For example, in the Rails ecosystem, this has spurred the creation of libraries such as [Turbo](https://turbo.hotwired.dev/) [Hotwire](https://hotwired.dev/), and [Stimulus](https://stimulus.hotwired.dev/). +Others, preferring to stick with the backend language they are comfortable with, have developed solutions that allow the construction of modern web application UIs while still primarily utilizing their backend language of choice. For example, in the [Rails](https://rubyonrails.org/) ecosystem, this has spurred the creation of libraries such as [Turbo](https://turbo.hotwired.dev/) [Hotwire](https://hotwired.dev/), and [Stimulus](https://stimulus.hotwired.dev/). Within the Laravel ecosystem, the need to create modern, dynamic frontends by primarily using PHP has led to the creation of [Laravel Livewire](https://laravel-livewire.com) and [Alpine.js](https://alpinejs.dev/). From f9aa45f85cd6b5ee6095dffe288e114f3f205a5b Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Thu, 30 Jun 2022 23:32:05 +1000 Subject: [PATCH 0247/2609] Fix directory paths in Blade docs (#8015) --- blade.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blade.md b/blade.md index 5a8b8a2d9ee..d46c48b8f26 100644 --- a/blade.md +++ b/blade.md @@ -581,7 +581,7 @@ Blade also allows you to define comments in your views. However, unlike HTML com 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: +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: ```shell php artisan make:component Alert @@ -595,7 +595,7 @@ You may also create components within subdirectories: php artisan make:component Forms/Input ``` -The command above will create an `Input` component in the `App\View\Components\Forms` directory and the view will be placed in the `resources/views/components/forms` directory. +The command above will create an `Input` component in the `app/View/Components/Forms` directory and the view will be placed in the `resources/views/components/forms` directory. If you would like to create an anonymous component (a component with only a Blade template and no class), you may use the `--view` flag when invoking the `make:component` command: @@ -662,7 +662,7 @@ To display a component, you may use a Blade component tag within one of your Bla ``` -If the component class is nested deeper within the `App\View\Components` directory, you may use the `.` character to indicate directory nesting. For example, if we assume a component is located at `App\View\Components\Inputs\Button.php`, we may render it like so: +If the component class is nested deeper within the `app/View/Components` directory, you may use the `.` character to indicate directory nesting. For example, if we assume a component is located at `app/View/Components/Inputs/Button.php`, we may render it like so: ```blade From f88ed3a6eb92c33929988dffd11fd14985669426 Mon Sep 17 00:00:00 2001 From: Jeremiasz Major Date: Fri, 1 Jul 2022 22:38:04 +0200 Subject: [PATCH 0248/2609] add missing comma (#8019) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 975f5125ef3..69ca8f15996 100644 --- a/helpers.md +++ b/helpers.md @@ -3736,7 +3736,7 @@ If you would like to manually calculate the number of milliseconds to sleep betw For convenience, you may provide an array as the first argument to the `retry` function. This array will be used to determine how many milliseconds to sleep between subsequent attempts: - return retry([100, 200] function () { + return retry([100, 200], function () { // Sleep for 100ms on first retry, 200ms on second retry... }); From 1a4dd8ee5f3986641da238866c3f0ea564bd95ee Mon Sep 17 00:00:00 2001 From: Azade Ghasemi <46772227+AzadGh95@users.noreply.github.com> Date: Thu, 7 Jul 2022 01:53:28 +0430 Subject: [PATCH 0249/2609] Remove extra spaces when merging two strings (#8028) * Update helpers.md * Update billing.md --- billing.md | 2 +- helpers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index 986789b1ec7..9ef12e38697 100644 --- a/billing.md +++ b/billing.md @@ -1779,7 +1779,7 @@ When defining your `success_url` checkout option, you may instruct Stripe to add Route::get('/product-checkout', function (Request $request) { return $request->user()->checkout(['price_tshirt' => 1], [ - 'success_url' => route('checkout-success') . '?session_id={CHECKOUT_SESSION_ID}', + 'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}', 'cancel_url' => route('checkout-cancel'), ]); }); diff --git a/helpers.md b/helpers.md index 69ca8f15996..2598a1df54f 100644 --- a/helpers.md +++ b/helpers.md @@ -2942,7 +2942,7 @@ The `tap` method passes the string to the given closure, allowing you to examine $string = Str::of('Laravel') ->append(' Framework') ->tap(function ($string) { - dump('String after append: ' . $string); + dump('String after append: '.$string); }) ->upper(); From d9209fdef1fd83a055013aca957d7f7d011baa08 Mon Sep 17 00:00:00 2001 From: Esdert Folkers Date: Thu, 7 Jul 2022 14:26:19 +0200 Subject: [PATCH 0250/2609] Replace locale for name (#8032) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index dad2902ba3b..0953b456fe9 100644 --- a/validation.md +++ b/validation.md @@ -942,7 +942,7 @@ When additional values are provided to the `array` rule, each key in the input a ]; Validator::make($input, [ - 'user' => 'array:username,locale', + 'user' => 'array:name,username', ]); In general, you should always specify the array keys that are allowed to be present within your array. From a047d83f5c75262935295223937fb532ecb7be62 Mon Sep 17 00:00:00 2001 From: "Logan H. Craft" Date: Mon, 11 Jul 2022 07:44:42 -0400 Subject: [PATCH 0251/2609] Fix Rending to Rendering. Update misspelling. (#8034) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 9d233396222..65f8e560ed8 100644 --- a/vite.md +++ b/vite.md @@ -359,7 +359,7 @@ import.meta.env.VITE_SENTRY_DSN_PUBLIC ## Server-Side Rendering (SSR) -The Laravel Vite plugin makes it painless to set up server-side rending with Vite. To get started, create an SSR entry point at `resources/js/ssr.js` and specify the entry point by passing a configuration option to the Laravel plugin: +The Laravel Vite plugin makes it painless to set up server-side rendering with Vite. To get started, create an SSR entry point at `resources/js/ssr.js` and specify the entry point by passing a configuration option to the Laravel plugin: ```js import { defineConfig } from 'vite'; From b69c27a7cd180feec57e1a2611fd3228c980415d Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 12 Jul 2022 04:12:40 +1000 Subject: [PATCH 0252/2609] [9.x] Document Vite refresh option (#8022) * document refresh option * update wording * formatting Co-authored-by: Taylor Otwell --- vite.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/vite.md b/vite.md index 65f8e560ed8..8d4629fd3a8 100644 --- a/vite.md +++ b/vite.md @@ -14,6 +14,7 @@ - [Inertia](#inertia) - [URL Processing](#url-processing) - [Working With Stylesheets](#working-with-stylesheets) +- [Working With Blade & Routes](#working-with-blade-and-routes) - [Custom Base URLs](#custom-base-urls) - [Environment Variables](#environment-variables) - [Server-Side Rendering (SSR)](#ssr) @@ -324,6 +325,64 @@ module.exports = { }; ``` + +## Working With Blade & Routes + +When your application is built using traditional server-side rendering with Blade, Vite can improve your development workflow by automatically refreshing the browser when you make changes to view files in your application. To get started, you can simply specify the `refresh` option as `true`. + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: true, + }), + ], +}); +``` + +When the `refresh` option is `true`, saving files in `resources/views/**`, `app/View/Components/**`, and `routes/**` will trigger the browser to perform a full page refresh while you are running `npm run dev`. + +Watching the `routes/**` directory is useful if you are utilizing [Ziggy](https://github.com/tighten/ziggy) to generate route links within your application's frontend. + +If these default paths do not suit your needs, you can specify your own list of paths to watch: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: ['resources/views/**'], + }), + ], +}); +``` + +Under the hood, the Laravel Vite plugin uses the [`vite-plugin-full-reload`](https://github.com/ElMassimo/vite-plugin-full-reload) package, which offers some advanced configuration options to fine-tune this feature's behavior. If you need this level of customization, you may provide a `config` definition: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: [{ + paths: ['path/to/watch/**'], + config: { delay: 300 }], + }], + }), + ], +}); +``` + ## Custom Base URLs From 5d195fc478f6c2433b1b2bb5a5f5509905bfd16a Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 12 Jul 2022 23:39:06 +1000 Subject: [PATCH 0253/2609] Update SSR directory (#8030) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 8d4629fd3a8..e54b3dde9c2 100644 --- a/vite.md +++ b/vite.md @@ -448,7 +448,7 @@ Then, to build and start the SSR server, you may run the following commands: ```sh npm run build -node storage/ssr/ssr.js +node bootstrap/ssr/ssr.js ``` > {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. From 3c50577d3fcbf41c7175cf04b9b296c96b88b466 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Wed, 13 Jul 2022 09:32:30 +0100 Subject: [PATCH 0254/2609] [9.x] feature: `Str::inlineMarkdown()` --- helpers.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/helpers.md b/helpers.md index 2598a1df54f..f49ea1458be 100644 --- a/helpers.md +++ b/helpers.md @@ -112,6 +112,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::excerpt](#method-excerpt) [Str::finish](#method-str-finish) [Str::headline](#method-str-headline) +[Str::inlineMarkdown](#method-str-inline-markdown) [Str::is](#method-str-is) [Str::isAscii](#method-str-is-ascii) [Str::isJson](#method-str-is-json) @@ -185,6 +186,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [exactly](#method-fluent-str-exactly) [explode](#method-fluent-str-explode) [finish](#method-fluent-str-finish) +[inlineMarkdown](#method-fluent-str-inline-markdown) [is](#method-fluent-str-is) [isAscii](#method-fluent-str-is-ascii) [isEmpty](#method-fluent-str-is-empty) @@ -1446,6 +1448,25 @@ The `Str::headline` method will convert strings delimited by casing, hyphens, or // Email Notification Sent + +#### `Str::inlineMarkdown()` {.collection-method} + +The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/): + + use Illuminate\Support\Str; + + $html = Str::inlineMarkdown('**Laravel**'); + + // Laravel + + $html = Str::markdown('The **Laravel** Framework', [ + 'html_input' => 'strip', + ]); + + // The Laravel Framework + +This differs from the [markdown](#method-str-markdown) method, since that will wrap all generated HTML in a block-level element, which is not the desired behaviour in some cases. + #### `Str::is()` {.collection-method} @@ -2329,6 +2350,25 @@ The `finish` method adds a single instance of the given value to a string if it // this/string/ + +#### `inlineMarkdown` {.collection-method} + +The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML: + + use Illuminate\Support\Str; + + $html = Str::of('**Laravel**')->inlineMarkdown(); + + // Laravel + + $html = Str::of('The **Laravel** Framework')->inlineMarkdown([ + 'html_input' => 'strip', + ]); + + // The Laravel Framework + +This differs from the [markdown](#method-flient-str-markdown) method, since that will wrap all generated HTML in a block-level element, which is not the desired behaviour in some cases. + #### `is` {.collection-method} From a73f76e64fea50a67072f7acb4f2d6e84c3a3d2f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2022 08:42:47 -0500 Subject: [PATCH 0255/2609] formatting --- helpers.md | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/helpers.md b/helpers.md index f49ea1458be..b9001069b8a 100644 --- a/helpers.md +++ b/helpers.md @@ -1451,7 +1451,7 @@ The `Str::headline` method will convert strings delimited by casing, hyphens, or #### `Str::inlineMarkdown()` {.collection-method} -The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/): +The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: use Illuminate\Support\Str; @@ -1459,14 +1459,6 @@ The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline H // Laravel - $html = Str::markdown('The **Laravel** Framework', [ - 'html_input' => 'strip', - ]); - - // The Laravel Framework - -This differs from the [markdown](#method-str-markdown) method, since that will wrap all generated HTML in a block-level element, which is not the desired behaviour in some cases. - #### `Str::is()` {.collection-method} @@ -2353,7 +2345,7 @@ The `finish` method adds a single instance of the given value to a string if it #### `inlineMarkdown` {.collection-method} -The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML: +The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: use Illuminate\Support\Str; @@ -2361,14 +2353,6 @@ The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML: // Laravel - $html = Str::of('The **Laravel** Framework')->inlineMarkdown([ - 'html_input' => 'strip', - ]); - - // The Laravel Framework - -This differs from the [markdown](#method-flient-str-markdown) method, since that will wrap all generated HTML in a block-level element, which is not the desired behaviour in some cases. - #### `is` {.collection-method} From fb4b196e8c48d6693d1f41aa2123f81ac2507ec1 Mon Sep 17 00:00:00 2001 From: Pavel Bychko Date: Thu, 14 Jul 2022 16:57:39 +0300 Subject: [PATCH 0256/2609] Fix vite-plugin-full-reload config example (#8042) * Fix laravel-vite-plugin config example * Update vite.md --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index e54b3dde9c2..6d02fe0ab74 100644 --- a/vite.md +++ b/vite.md @@ -376,7 +376,7 @@ export default defineConfig({ // ... refresh: [{ paths: ['path/to/watch/**'], - config: { delay: 300 }], + config: { delay: 300 } }], }), ], From 0682ca014e5e1768066ef93b4269a83d82978a70 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 15 Jul 2022 00:07:31 +1000 Subject: [PATCH 0257/2609] [9.x] Authorization HTTP status customisation (#8041) * Document customising gate status codes * Document customising policy status codes * Provide better context * clarify HTTP response * Update authorization.md * Update authorization.md Co-authored-by: Taylor Otwell --- authorization.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/authorization.md b/authorization.md index 9e432a28955..2a5f2dbbd01 100644 --- a/authorization.md +++ b/authorization.md @@ -195,6 +195,33 @@ When using the `Gate::authorize` method, which throws an `AuthorizationException // The action is authorized... + +#### Customizing The HTTP Response Status + +When an action is denied via a Gate, a `403` HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the `denyWithStatus` static constructor on the `Illuminate\Auth\Access\Response` class: + + use App\Models\User; + use Illuminate\Auth\Access\Response; + use Illuminate\Support\Facades\Gate; + + Gate::define('edit-settings', function (User $user) { + return $user->isAdmin + ? Response::allow() + : Response::denyWithStatus(404); + }); + +Because hiding resources via a `404` response is such a common pattern for web applications, the `denyAsNotFound` method is offered for convenience: + + use App\Models\User; + use Illuminate\Auth\Access\Response; + use Illuminate\Support\Facades\Gate; + + Gate::define('edit-settings', function (User $user) { + return $user->isAdmin + ? Response::allow() + : Response::denyAsNotFound(); + }); + ### Intercepting Gate Checks @@ -389,6 +416,49 @@ When using the `Gate::authorize` method, which throws an `AuthorizationException // The action is authorized... + +#### Customizing The HTTP Response Status + +When an action is denied via a policy method, a `403` HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the `denyWithStatus` static constructor on the `Illuminate\Auth\Access\Response` class: + + use App\Models\Post; + use App\Models\User; + use Illuminate\Auth\Access\Response; + + /** + * Determine if the given post can be updated by the user. + * + * @param \App\Models\User $user + * @param \App\Models\Post $post + * @return \Illuminate\Auth\Access\Response + */ + public function update(User $user, Post $post) + { + return $user->id === $post->user_id + ? Response::allow() + : Response::denyWithStatus(404); + } + +Because hiding resources via a `404` response is such a common pattern for web applications, the `denyAsNotFound` method is offered for convenience: + + use App\Models\Post; + use App\Models\User; + use Illuminate\Auth\Access\Response; + + /** + * Determine if the given post can be updated by the user. + * + * @param \App\Models\User $user + * @param \App\Models\Post $post + * @return \Illuminate\Auth\Access\Response + */ + public function update(User $user, Post $post) + { + return $user->id === $post->user_id + ? Response::allow() + : Response::denyAsNotFound(); + } + ### Methods Without Models From fc796f67d6267cbbfa041a2b0bab303d8d0740ed Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2022 09:16:53 -0500 Subject: [PATCH 0258/2609] wip --- dusk.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dusk.md b/dusk.md index ec93a824062..4991483655a 100644 --- a/dusk.md +++ b/dusk.md @@ -419,6 +419,10 @@ You may use the `screenshot` method to take a screenshot and store it with the g $browser->screenshot('filename'); +The `responsiveScreenshots` method may be used to take a series of screenshots at various breakpoints: + + $browser->responsiveScreenshots('filename'); + ### Storing Console Output To Disk From 049814cbacbfcadd875a7ef327df042fc9b763b4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2022 09:18:02 -0500 Subject: [PATCH 0259/2609] wip --- blade.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/blade.md b/blade.md index d46c48b8f26..2f752eac37a 100644 --- a/blade.md +++ b/blade.md @@ -467,6 +467,15 @@ Additionally, the `@disabled` directive may be used to indicate if a given eleme ``` +In addition, the `@required` directive may be used to indicate if a given element should be "required": + +```blade +isAdmin()) /> +``` + ### Including Subviews From da6aa731caedbe014dbbf48987fa8f2f0f43c54b Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Thu, 14 Jul 2022 12:08:22 -0400 Subject: [PATCH 0260/2609] Add `whenCounted` resource method docs --- eloquent-resources.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/eloquent-resources.md b/eloquent-resources.md index a9c5a73d9e2..e981af21dfa 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -587,6 +587,35 @@ The `whenLoaded` method may be used to conditionally load a relationship. In ord In this example, if the relationship has not been loaded, the `posts` key will be removed from the resource response before it is sent to the client. + +#### Conditional Relationship Counts + +In addition to conditionally including relationships, you may conditionally include relationship counts on your resource responses based on if the relationship count has been loaded on the model: + + new UserResource($user->loadCount('posts')); + +The `whenCounted` method may be used to conditionally include a relationship's count, in order to avoid unnecessarily including the attribute if the relation count is not present. This method accepts the relationship's name: + + /** + * Transform the resource into an array. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function toArray($request) + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'posts_count' => $this->whenCounted('posts'), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } + +In this example, if the `posts` relationship count has not been loaded, the `posts_count` key will be removed from the resource response before it is sent to the client. + #### Conditional Pivot Information From 6df16abd00d1bab1b4c08378665626ad75f01505 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Thu, 14 Jul 2022 12:12:14 -0400 Subject: [PATCH 0261/2609] Grammatical fixes --- eloquent-resources.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index e981af21dfa..8df7a8b5345 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -590,11 +590,11 @@ In this example, if the relationship has not been loaded, the `posts` key will b #### Conditional Relationship Counts -In addition to conditionally including relationships, you may conditionally include relationship counts on your resource responses based on if the relationship count has been loaded on the model: +In addition to conditionally including relationships, you may conditionally include relationship counts on your resource responses based on if the relationship's count has been loaded on the model: new UserResource($user->loadCount('posts')); -The `whenCounted` method may be used to conditionally include a relationship's count, in order to avoid unnecessarily including the attribute if the relation count is not present. This method accepts the relationship's name: +The `whenCounted` method may be used to conditionally include a relationship's count, in order to avoid unnecessarily including the attribute if the relationships's count is not present. This method accepts the relationship's name: /** * Transform the resource into an array. @@ -614,7 +614,7 @@ The `whenCounted` method may be used to conditionally include a relationship's c ]; } -In this example, if the `posts` relationship count has not been loaded, the `posts_count` key will be removed from the resource response before it is sent to the client. +In this example, if the `posts` relationship's count has not been loaded, the `posts_count` key will be removed from the resource response before it is sent to the client. #### Conditional Pivot Information From d81734a2108ed5e5cbe0d29c58ce220062002644 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 14 Jul 2022 15:51:50 -0500 Subject: [PATCH 0262/2609] add doc about passing a closure to `random()` (#8045) * add doc about passing a closure to `random()` * Update collections.md Co-authored-by: Taylor Otwell --- collections.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/collections.md b/collections.md index 6ddce5993c2..258ee1c6506 100644 --- a/collections.md +++ b/collections.md @@ -1822,6 +1822,14 @@ You may pass an integer to `random` to specify how many items you would like to If the collection instance has fewer items than requested, the `random` method will throw an `InvalidArgumentException`. +The `random` method also accepts a closure, which will receive the current collection instance: + + $random = $collection->random(fn ($items) => min(10, count($items))); + + $random->all(); + + // [1, 2, 3, 4, 5] - (retrieved randomly) + #### `range()` {.collection-method} From 3e159904c68493e98de27345936f1acdd16764f9 Mon Sep 17 00:00:00 2001 From: Mahmoud Mohamed Ramadan <48416569+mahmoudmohamedramadan@users.noreply.github.com> Date: Sat, 16 Jul 2022 20:32:05 +0200 Subject: [PATCH 0263/2609] Updates `attributes checking` section --- blade.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/blade.md b/blade.md index 2f752eac37a..0078bcd5ac2 100644 --- a/blade.md +++ b/blade.md @@ -10,7 +10,7 @@ - [Loops](#loops) - [The Loop Variable](#the-loop-variable) - [Conditional Classes](#conditional-classes) - - [Checked / Selected / Disabled](#checked-and-selected) + - [Attributes Checking](#attributes-checking) - [Including Subviews](#including-subviews) - [The `@once` Directive](#the-once-directive) - [Raw PHP](#raw-php) @@ -437,8 +437,8 @@ The `@class` directive conditionally compiles a CSS class string. The directive ``` - -### Checked / Selected / Disabled + +### Attributes Checking For convenience, you may use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will echo `checked` if the provided condition evaluates to `true`: @@ -467,6 +467,12 @@ Additionally, the `@disabled` directive may be used to indicate if a given eleme ``` +Moreover, the `@readonly` directive may be used to indicate if a given element should be "readonly": + +```blade +isNotAdmin()) /> +``` + In addition, the `@required` directive may be used to indicate if a given element should be "required": ```blade From 91aa9a8448ec605c2c5c60fe56355701e0606c5d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 18 Jul 2022 08:59:47 -0500 Subject: [PATCH 0264/2609] formatting --- blade.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blade.md b/blade.md index 0078bcd5ac2..42d052cdb67 100644 --- a/blade.md +++ b/blade.md @@ -10,7 +10,7 @@ - [Loops](#loops) - [The Loop Variable](#the-loop-variable) - [Conditional Classes](#conditional-classes) - - [Attributes Checking](#attributes-checking) + - [Additional Attributes](#additional-attributes) - [Including Subviews](#including-subviews) - [The `@once` Directive](#the-once-directive) - [Raw PHP](#raw-php) @@ -437,8 +437,8 @@ The `@class` directive conditionally compiles a CSS class string. The directive ``` - -### Attributes Checking + +### Additional Attributes For convenience, you may use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will echo `checked` if the provided condition evaluates to `true`: From 5baf9288eda5a6a32b5cce0d039a26ca7feb6da5 Mon Sep 17 00:00:00 2001 From: Nzagha David Eze <33941830+zaghadon@users.noreply.github.com> Date: Mon, 18 Jul 2022 07:04:28 -0700 Subject: [PATCH 0265/2609] Update --path Flag in the Route Listing Documentation (#8047) * Add Flags to Route Listing, to enable listing a particular Routes file like php artisan route:list --api-only #43214 * Update routing.md Co-authored-by: Taylor Otwell --- routing.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/routing.md b/routing.md index 743b23b1465..fa5f4a28123 100644 --- a/routing.md +++ b/routing.md @@ -140,6 +140,12 @@ By default, the route middleware that are assigned to each route will not be dis php artisan route:list -v ``` +You may also instruct Laravel to only show routes that begin with a given URI: + +```shell +php artisan route:list --path=api +``` + In addition, you may instruct Laravel to hide any routes that are defined by third-party packages by providing the `--except-vendor` option when executing the `route:list` command: ```shell From 60dbcc56c05b9a6f2ca30832153652c098e89243 Mon Sep 17 00:00:00 2001 From: Mahmoud Mohamed Ramadan <48416569+mahmoudmohamedramadan@users.noreply.github.com> Date: Mon, 18 Jul 2022 23:02:21 +0200 Subject: [PATCH 0266/2609] Adds `@pushIf` directive with some formatting (#8051) * Adds `@pushIf` directive with some formatting * Update blade.md Co-authored-by: Taylor Otwell --- blade.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/blade.md b/blade.md index 42d052cdb67..3e498ab056e 100644 --- a/blade.md +++ b/blade.md @@ -470,7 +470,10 @@ Additionally, the `@disabled` directive may be used to indicate if a given eleme Moreover, the `@readonly` directive may be used to indicate if a given element should be "readonly": ```blade -isNotAdmin()) /> +isNotAdmin()) /> ``` In addition, the `@required` directive may be used to indicate if a given element should be "required": @@ -1588,6 +1591,14 @@ Blade allows you to push to named stacks which can be rendered somewhere else in @endpush ``` +If you would like to `@push` content if a given boolean expression evaluates to `true`, you may use the `@pushIf` directive: + +```blade +@pushIf($shouldPush, 'scripts') + +@endPushIf +``` + 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: ```blade From 78dd87135e103e5fa706926a5080fad3eb51f81a Mon Sep 17 00:00:00 2001 From: kamasuPaul <35300826+kamasuPaul@users.noreply.github.com> Date: Tue, 19 Jul 2022 16:50:04 +0300 Subject: [PATCH 0267/2609] -- little grammar fix (#8053) changed ` authenticate an SPA` to ` authenticate a SPA` --- sanctum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index a27eea67b3c..0cf08452f4b 100644 --- a/sanctum.md +++ b/sanctum.md @@ -73,7 +73,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 application's `app/Http/Kernel.php` file: +Next, if you plan to utilize Sanctum to authenticate a 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, From a33676bfec47b3b4ef7ce27abc2bc6ac9eeaa35e Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 19 Jul 2022 15:18:32 +0100 Subject: [PATCH 0268/2609] [9.x] Adds `model:show` documentation (#8054) * Adds `model:show` documentation * Moves section to `Retrieving Models` * formatting Co-authored-by: Taylor Otwell --- eloquent.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/eloquent.md b/eloquent.md index 67fca1ca55c..c841d9f65be 100644 --- a/eloquent.md +++ b/eloquent.md @@ -90,6 +90,15 @@ php artisan make:model Flight --all php artisan make:model Member --pivot ``` + +#### Inspecting Models + +Sometimes it can be difficult to determine all of a model's available attributes and relationships just by skimming its code. Instead, try the `model:show` Artisan command, which provides a convenient overview of all the model's attributes and relations: + +```shell +php artisan model:show Flight +``` + ## Eloquent Model Conventions From 8d8231e621f276eb3a410b103c2aeed41e86c859 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 19 Jul 2022 15:23:17 +0100 Subject: [PATCH 0269/2609] [9.x] Adds Laravel Pint documentation (#8043) * Adds Laravel Pint documentation * formatting Co-authored-by: Taylor Otwell --- contributions.md | 1 + documentation.md | 1 + pint.md | 128 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 pint.md diff --git a/contributions.md b/contributions.md index 223a08c0317..938ca8daddf 100644 --- a/contributions.md +++ b/contributions.md @@ -38,6 +38,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Horizon](https://github.com/laravel/horizon) - [Laravel Jetstream](https://github.com/laravel/jetstream) - [Laravel Passport](https://github.com/laravel/passport) +- [Laravel Pint](https://github.com/laravel/pint) - [Laravel Sail](https://github.com/laravel/sail) - [Laravel Sanctum](https://github.com/laravel/sanctum) - [Laravel Scout](https://github.com/laravel/scout) diff --git a/documentation.md b/documentation.md index be6a940e2ad..b51c6cd6bc1 100644 --- a/documentation.md +++ b/documentation.md @@ -87,6 +87,7 @@ - [Mix](/docs/{{version}}/mix) - [Octane](/docs/{{version}}/octane) - [Passport](/docs/{{version}}/passport) + - [Pint](/docs/{{version}}/pint) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) - [Scout](/docs/{{version}}/scout) diff --git a/pint.md b/pint.md new file mode 100644 index 00000000000..e9bd77bd975 --- /dev/null +++ b/pint.md @@ -0,0 +1,128 @@ +# Laravel Pint + +- [Introduction](#introduction) +- [Running Pint](#running-pint) +- [Configuring Pint](#configuring-pint) + - [Presets](#presets) + - [Rules](#rules) + - [Excluding Files / Folders](#excluding-files-or-folders) + + +## Introduction + +[Laravel Pint](https://github.com/laravel/pint) is an opinionated PHP code style fixer for minimalists. Pint is built on top of PHP-CS-Fixer and makes it simple to ensure that your code style stays clean and consistent. + +Pint is automatically installed with all new Laravel applications so you may start using it immediately. By default, Pint does not require any configuration and will fix code style issues in your code by following the opinionated coding style of Laravel. + + +## Running Pint + +You instruct Pint to fix code style issues by running the `pint` binary that is available in your project's `vendor/bin` directory: + +```shell +./vendor/bin/pint +``` + +When running Pint, it will output a list of files that have been fixed. It is possible to see the changes made in more detail using the `-v` option: + +```shell +./vendor/bin/pint -v +``` + +If you would like Pint to simply inspect your code for style errors without actually changing the files, you may use the `--test` option: + +```shell +./vendor/bin/pint --test +``` + + +## Configuring Pint + +As previously mentioned, Pint does not require any configuration. However, if you wish to customize the presets, rules, or inspected folders, you may do so by creating a `pint.json` file in your project's root directory: + +```json +{ + "preset": "laravel" +} +``` + +In addition, if you wish to use a `pint.json` from a specific directory, you may provide the `--config` option when invoking Pint: + +```shell +pint --config vendor/my-company/coding-style/pint.json +``` + + +### Presets + +Presets defines a set of rules that can be used to fix code style issues in your code. By default, Pint uses the `laravel` preset, which fixes issues by following the opinionated coding style of Laravel. However, you may specify a different preset by providing the `--preset` option to Pint: + +```shell +pint --preset psr12 +``` + +If you wish, you may also set the preset in your project's `pint.json` file: + +```json +{ + "preset": "psr12" +} +``` + +Pint's currently supported presets are: `laravel`, `psr12`, and `symfony`. + + +### Rules + +Rules are style guidelines that Pint will use to fix code style issues in your code. As mentioned above, presets are predefined groups of rules that should be perfect for most PHP projects, so you typically will not need to worry about the individual rules they contain. + +However, if you wish, you may enable or disable specific rules in your `pint.json` file: + +```json +{ + "preset": "laravel", + "rules": { + "simplified_null_return": true, + "braces": false, + "new_with_braces": { + "anonymous_class": false, + "named_class": false + } + } +} +``` + +Pint is built on top of [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer). Therefore, you may use any of its rules to fix code style issues in your project: [PHP-CS-Fixer Configurator](https://mlocati.github.io/php-cs-fixer-configurator). + + +### Excluding Files / Folders + +By default, Pint will inspect all `.php` files in your project except those in the `vendor` directory. If you wish to exclude more folders, you may do so using the `exclude` configuration option: + +```json +{ + "exclude": [ + "my-specific/folder" + ] +} +``` + +If you wish to exclude all files that contain a given name pattern, you may do so using the `notName` configuration option: + +```json +{ + "notName": [ + "*-my-file.php" + ] +} +``` + +If you would like to exclude a file by providing an exact path to the file, you may do so using the `notPath` configuration option: + +```json +{ + "notPath": [ + "path/to/excluded-file.php" + ] +} +``` From f8e382719cfb7cc1885d2ce5560f39a0a811c306 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 20 Jul 2022 00:23:32 +1000 Subject: [PATCH 0270/2609] vite 3 support (#8049) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 6d02fe0ab74..91067c41cee 100644 --- a/vite.md +++ b/vite.md @@ -448,7 +448,7 @@ Then, to build and start the SSR server, you may run the following commands: ```sh npm run build -node bootstrap/ssr/ssr.js +node bootstrap/ssr/ssr.mjs ``` > {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. From 615fc774c549c34ac3d11567936fa684f9cd0221 Mon Sep 17 00:00:00 2001 From: James Brooks Date: Tue, 19 Jul 2022 15:31:47 +0100 Subject: [PATCH 0271/2609] [9.x] Document `about` command (#8055) * Document `about` command * wip * wip * wip * Update packages.md * formatting * remove entry Co-authored-by: Taylor Otwell --- configuration.md | 15 +++++++++++++++ packages.md | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/configuration.md b/configuration.md index b44f1369a9a..2fc483e6561 100644 --- a/configuration.md +++ b/configuration.md @@ -17,6 +17,21 @@ All of the configuration files for the Laravel framework are stored in the `conf These configuration files allow you to configure things like your database connection information, your mail server information, as well as various other core configuration values such as your application timezone and encryption key. + +#### Application Overview + +In a hurry? You can get a quick overview of your application's configuration, drivers, and environment via the `about` Artisan command: + +```shell +php artisan about +``` + +If you're only interested in a particular section of the application overview output, you may filter for that section using the `--only` option: + +```shell +php artisan about --only=environment +``` + ## Environment Configuration diff --git a/packages.md b/packages.md index 6e667aec555..6646e0d0488 100644 --- a/packages.md +++ b/packages.md @@ -11,6 +11,7 @@ - [Translations](#translations) - [Views](#views) - [View Components](#view-components) + - ["About" Artisan Command](#about-artisan-command) - [Commands](#commands) - [Public Assets](#public-assets) - [Publishing File Groups](#publishing-file-groups) @@ -308,6 +309,30 @@ If your package contains anonymous components, they must be placed within a `com ``` + +### "About" Artisan Command + +Laravel's built-in `about` Artisan command provides a synopsis of the application's environment and configuration. Packages may push additional information to this command's output via the `AboutCommand` class. Typically, this information may be added from your package service provider's `register` method: + + use Illuminate\Foundation\Console\AboutCommand; + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + AboutCommand::add('My Package', 'Version', '1.0.0'); + } + +The `about` command's values may also be provided a closures if deferred execution is desirable: + + AboutCommand::add('My Package', [ + 'Version' => '1.0.0', + 'Driver' => fn () => config('my-package.driver'), + ]); + ## Commands From 62002c390b6423ebbec88df2035faa68a267691e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 19 Jul 2022 10:59:54 -0500 Subject: [PATCH 0272/2609] document request enum? --- requests.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/requests.md b/requests.md index 1c2888089f6..a9105f3b450 100644 --- a/requests.md +++ b/requests.md @@ -318,6 +318,15 @@ The second and third arguments accepted by the `date` method may be used to spec If the input value is present but has an invalid format, an `InvalidArgumentException` will be thrown; therefore, it is recommended that you validate the input before invoking the `date` method. + +#### Retrieving Enum Input Values + +Input values that correspond to [PHP enums](https://www.php.net/manual/en/language.types.enumerations.php) may also be retrieved from the request. If the request does not contain an input value with the given name or the enum does not have a backing value that matches the input value, `null` will be returned. The `enum` method accepts the name of the input value and the enum class as its first and second arguments: + + use App\Enums\Status; + + $status = $request->enum('status', Status::class); + #### Retrieving Input Via Dynamic Properties From 63b921ce75c3c296a38e1c63cc54d7479fe4c704 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 19 Jul 2022 11:03:00 -0500 Subject: [PATCH 0273/2609] formatting --- eloquent-resources.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index 8df7a8b5345..beb7aa3f1cd 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -590,11 +590,11 @@ In this example, if the relationship has not been loaded, the `posts` key will b #### Conditional Relationship Counts -In addition to conditionally including relationships, you may conditionally include relationship counts on your resource responses based on if the relationship's count has been loaded on the model: +In addition to conditionally including relationships, you may conditionally include relationship "counts" on your resource responses based on if the relationship's count has been loaded on the model: new UserResource($user->loadCount('posts')); -The `whenCounted` method may be used to conditionally include a relationship's count, in order to avoid unnecessarily including the attribute if the relationships's count is not present. This method accepts the relationship's name: +The `whenCounted` method may be used to conditionally include a relationship's count in your resource response. This method avoids unnecessarily including the attribute if the relationships's count is not present: /** * Transform the resource into an array. From 74623cd095ad0876bb069acba241e096995327fa Mon Sep 17 00:00:00 2001 From: Andy Hinkle Date: Tue, 19 Jul 2022 12:48:58 -0500 Subject: [PATCH 0274/2609] Fix Eloquent mutator docblock (#8056) --- eloquent-mutators.md | 1 - 1 file changed, 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index e743370bf7e..85ea3df5d08 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -152,7 +152,6 @@ A mutator transforms an Eloquent attribute value when it is set. To define a mut /** * Interact with the user's first name. * - * @param string $value * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function firstName(): Attribute From e9cb74f1a892d46794d7ea0ab709e7d9cd99ed08 Mon Sep 17 00:00:00 2001 From: crazy252 Date: Wed, 20 Jul 2022 14:54:08 +0200 Subject: [PATCH 0275/2609] fix octane roadrunner port for server (#8057) --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index 48659f3e271..9e31fa73352 100644 --- a/octane.md +++ b/octane.md @@ -82,7 +82,7 @@ After installing the RoadRunner binary, you may exit your Sail shell session. Yo Next, update the `command` directive of your application's `docker/supervisord.conf` file so that Sail serves your application using Octane instead of the PHP development server: ```ini -command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=8000 +command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=80 ``` Finally, ensure the `rr` binary is executable and build your Sail images: From 4611b1233de2e2ae1899bf0c639e038ccdf6e0c8 Mon Sep 17 00:00:00 2001 From: Caneco Date: Wed, 20 Jul 2022 15:12:06 +0100 Subject: [PATCH 0276/2609] added installation tip for older applications --- pint.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pint.md b/pint.md index e9bd77bd975..fa0bef56af5 100644 --- a/pint.md +++ b/pint.md @@ -1,6 +1,7 @@ # Laravel Pint - [Introduction](#introduction) +- [Installation](#installation) - [Running Pint](#running-pint) - [Configuring Pint](#configuring-pint) - [Presets](#presets) @@ -14,6 +15,15 @@ Pint is automatically installed with all new Laravel applications so you may start using it immediately. By default, Pint does not require any configuration and will fix code style issues in your code by following the opinionated coding style of Laravel. + +## Installation + +For older applications you might need to install Pint via the Composer package manager: + +```shell +composer require laravel/pint +``` + ## Running Pint From 5dcb6ba9ea4c9a42bf7c5f53a174b40d0229092b Mon Sep 17 00:00:00 2001 From: Brett B Date: Thu, 21 Jul 2022 00:14:46 +1000 Subject: [PATCH 0277/2609] [9.x] Http client middleware (#8050) * Add withMiddleware examples to the documentation I always find i need to go somewhere else to find info about the middleware for Http Client, so thought i'd add it to the docs * Update http-client.md * formatting * remove extra stuff Co-authored-by: Taylor Otwell --- http-client.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/http-client.md b/http-client.md index 3d48063f415..214aeeb04f1 100644 --- a/http-client.md +++ b/http-client.md @@ -8,6 +8,7 @@ - [Timeout](#timeout) - [Retries](#retries) - [Error Handling](#error-handling) + - [Guzzle Middleware](#guzzle-middleware) - [Guzzle Options](#guzzle-options) - [Concurrent Requests](#concurrent-requests) - [Macros](#macros) @@ -251,6 +252,35 @@ If you would like to perform some additional logic before the exception is throw // })->json(); + +### Guzzle Middleware + +Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guzzle Middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html) to manipulate the outgoing request or inspect the incoming response. To manipulate the outgoing request, register a Guzzle middleware via the `withMiddleware` method in combination with Guzzle's `mapRequest` middleware factory: + + use GuzzleHttp\Middleware; + use Illuminate\Support\Facades\Http; + use Psr\Http\Message\RequestInterface; + + $response = Http::withMiddleware( + Middleware::mapRequest(function (RequestInterface $request) { + $request->withHeader('X-Example', 'Value'); + }) + ->get('http://example.com/'); + +Likewise, you can inspect the incoming HTTP response by registering a middleware via the `withMiddleware` method in combination with Guzzle's `mapResponse` middleware factory: + + use GuzzleHttp\Middleware; + use Illuminate\Support\Facades\Http; + use Psr\Http\Message\ResponseInterface; + + $response = Http::withMiddleware( + Middleware::mapResponse(function (ResponseInterface $response) { + $header = $response->getHeader('X-Example'); + + // ... + }) + )->get('http://example.com/'); + ### Guzzle Options From 4cde7a68145246c4ccd395d01c7fddd2b070d973 Mon Sep 17 00:00:00 2001 From: Amir Moharami Fard Date: Wed, 20 Jul 2022 20:27:09 +0430 Subject: [PATCH 0278/2609] added hasAny method document (#8060) * added hasAny method document * formatting Co-authored-by: Taylor Otwell --- collections.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/collections.md b/collections.md index 258ee1c6506..395869e2edc 100644 --- a/collections.md +++ b/collections.md @@ -131,6 +131,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [get](#method-get) [groupBy](#method-groupby) [has](#method-has) +[hasAny](#method-hasany) [implode](#method-implode) [intersect](#method-intersect) [intersectByKeys](#method-intersectbykeys) @@ -1083,6 +1084,21 @@ The `has` method determines if a given key exists in the collection: // false + +#### `hasAny()` {.collection-method} + +The `hasAny` method determines whether any of the given keys exist in the collection: + + $collection = collect(['account_id' => 1, 'product' => 'Desk', 'amount' => 5]); + + $collection->hasAny(['product', 'price']); + + // true + + $collection->hasAny(['name', 'price']); + + // false + #### `implode()` {.collection-method} From 5b9fa0fb41bcbb2f29f06d9fb7e12cc0af8d196d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Jul 2022 11:06:16 -0500 Subject: [PATCH 0279/2609] wip --- pint.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pint.md b/pint.md index fa0bef56af5..6ba284ca99c 100644 --- a/pint.md +++ b/pint.md @@ -18,22 +18,22 @@ Pint is automatically installed with all new Laravel applications so you may sta ## Installation -For older applications you might need to install Pint via the Composer package manager: +Laravel is included in recent releases of the Laravel framework, so installation is typically unnecessary. However, for older applications, you may install Laravel Pint via Composer: ```shell -composer require laravel/pint +composer require laravel/pint --dev ``` ## Running Pint -You instruct Pint to fix code style issues by running the `pint` binary that is available in your project's `vendor/bin` directory: +You can instruct Pint to fix code style issues by invoking the `pint` binary that is available in your project's `vendor/bin` directory: ```shell ./vendor/bin/pint ``` -When running Pint, it will output a list of files that have been fixed. It is possible to see the changes made in more detail using the `-v` option: +Pint will display a thorough list of all of the files that it updates while fixing code style issues throughout your project. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: ```shell ./vendor/bin/pint -v From 84c6efaf3f37189269ac302fc544df2a2fe67767 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Jul 2022 11:06:49 -0500 Subject: [PATCH 0280/2609] wip --- pint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint.md b/pint.md index 6ba284ca99c..917f6790602 100644 --- a/pint.md +++ b/pint.md @@ -33,7 +33,7 @@ You can instruct Pint to fix code style issues by invoking the `pint` binary tha ./vendor/bin/pint ``` -Pint will display a thorough list of all of the files that it updates while fixing code style issues throughout your project. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: +Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: ```shell ./vendor/bin/pint -v From 7afa693afcb9ac1ebd23f358be77f9e624e05161 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Jul 2022 11:06:58 -0500 Subject: [PATCH 0281/2609] wip --- pint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint.md b/pint.md index 917f6790602..00fdb18fb6f 100644 --- a/pint.md +++ b/pint.md @@ -18,7 +18,7 @@ Pint is automatically installed with all new Laravel applications so you may sta ## Installation -Laravel is included in recent releases of the Laravel framework, so installation is typically unnecessary. However, for older applications, you may install Laravel Pint via Composer: +Pint is included in recent releases of the Laravel framework, so installation is typically unnecessary. However, for older applications, you may install Laravel Pint via Composer: ```shell composer require laravel/pint --dev From 0df79ae06227088bd095aa615d9bce39d4e109b8 Mon Sep 17 00:00:00 2001 From: Amir Moharami Fard Date: Thu, 21 Jul 2022 18:25:07 +0430 Subject: [PATCH 0282/2609] containsOneItem method document (#8062) --- collections.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/collections.md b/collections.md index 395869e2edc..2c4d07cad42 100644 --- a/collections.md +++ b/collections.md @@ -103,6 +103,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [combine](#method-combine) [concat](#method-concat) [contains](#method-contains) +[containsOneItem](#method-containsoneitem) [containsStrict](#method-containsstrict) [count](#method-count) [countBy](#method-countBy) @@ -437,6 +438,23 @@ The `contains` method uses "loose" comparisons when checking item values, meanin For the inverse of `contains`, see the [doesntContain](#method-doesntcontain) method. + +#### `containsOneItem()` {.collection-method} + +The `containsOneItem` method determines whether the collection contains only one single item. + + collect([])->containsOneItem(); + + // false + + collect(['1'])->containsOneItem(); + + // true + + collect(['1','2'])->containsOneItem(); + + //false + #### `containsStrict()` {.collection-method} From 7397c92340cd8d72a96f32730ebd65233bc1d5df Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 21 Jul 2022 08:55:38 -0500 Subject: [PATCH 0283/2609] wip --- collections.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/collections.md b/collections.md index 2c4d07cad42..89b339ad737 100644 --- a/collections.md +++ b/collections.md @@ -441,7 +441,7 @@ For the inverse of `contains`, see the [doesntContain](#method-doesntcontain) me #### `containsOneItem()` {.collection-method} -The `containsOneItem` method determines whether the collection contains only one single item. +The `containsOneItem` method determines whether the collection contains a single item: collect([])->containsOneItem(); @@ -451,9 +451,9 @@ The `containsOneItem` method determines whether the collection contains only one // true - collect(['1','2'])->containsOneItem(); + collect(['1', '2'])->containsOneItem(); - //false + // false #### `containsStrict()` {.collection-method} From 845f041b79565fa78efc831c395826d5c95d172e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 21 Jul 2022 15:30:06 -0500 Subject: [PATCH 0284/2609] document attemptWhen --- authentication.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/authentication.md b/authentication.md index 54e1ae6bb3c..faa8c931c42 100644 --- a/authentication.md +++ b/authentication.md @@ -274,6 +274,17 @@ If you wish, you may also add extra query conditions to the authentication query > {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. +The `attemptWhen` method, which receives a closure as its second argument, may be used to perform more extensive inspection of the potential user before actually authenticating the user. The closure receives the potential user and should return `true` or `false` to indicate if the user may be authenticated: + + if (Auth::attemptWhen([ + 'email' => $email, + 'password' => $password, + ], function ($user) { + return $user->isNotBanned(); + })) { + // Authentication was successful... + } + #### Accessing Specific Guard Instances From 5274e324d732951ca5d49b882ef52c8fbc08235b Mon Sep 17 00:00:00 2001 From: Chris Sternal-Johnson Date: Fri, 22 Jul 2022 10:40:28 -0400 Subject: [PATCH 0285/2609] Update sail.md to reflect initial setup for MinIO (#8067) * Update sail.md to reflect initial setup for MinIO On first run, the MinIO container starts without any buckets. Clarify to indicate that on first run the user must create the `local` bucket. * Update sail.md Co-authored-by: Taylor Otwell --- sail.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sail.md b/sail.md index ad39f0e1954..062bcba1f0d 100644 --- a/sail.md +++ b/sail.md @@ -247,6 +247,8 @@ In order for Laravel's Flysystem integration to generate proper URLs when using AWS_URL=http://localhost:9000/local ``` +You may create buckets via the MinIO console, which is available at `http://localhost:8900`. The default username for the MinIO console is `sail` while the default password is `password`. + ## Running Tests From 0f8711e7ee16e0047abf442978123e1f2fc8735e Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Fri, 22 Jul 2022 17:00:11 +0200 Subject: [PATCH 0286/2609] [9.x] Clarify actingAs behavior when setting a guard (#8065) * Update http-tests.md * Update http-tests.md Co-authored-by: Taylor Otwell --- http-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index c0e09f5d191..085ade74bed 100644 --- a/http-tests.md +++ b/http-tests.md @@ -172,7 +172,7 @@ Laravel's session is typically used to maintain state for the currently authenti } } -You may also specify which guard should be used to authenticate the given user by passing the guard name as the second argument to the `actingAs` method: +You may also specify which guard should be used to authenticate the given user by passing the guard name as the second argument to the `actingAs` method. The guard that is provided to the `actingAs` method will also become the default guard for the duration of the test: $this->actingAs($user, 'web') From 6a578a62a39bb5904b9740f795d27795d481d4ca Mon Sep 17 00:00:00 2001 From: David Llop Date: Fri, 22 Jul 2022 17:10:06 +0200 Subject: [PATCH 0287/2609] Explain how to bind Exception Handler after upgrade (#8066) * Explain how to bind Exception Handler after upgrade * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/upgrade.md b/upgrade.md index d8948234762..fcd52141398 100644 --- a/upgrade.md +++ b/upgrade.md @@ -128,6 +128,12 @@ The exception handler's `ignore` method is now `public` instead of `protected`. public function ignore(string $class); ``` +#### Exception Handler Contract Binding + +**Likelihood Of Impact: Very Low** + +Previously, in order to override the default exception handler, custom implementations were bound into the container using the `\App\Exceptions\Handler::class` type. However, you should now bind custom implementations using the `\Illuminate\Contracts\Debug\ExceptionHandler::class` type. + ### Blade #### Lazy Collections & The `$loop` Variable From ba7b5902c337112efc06be2ee6e04089cd7b9a43 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 22 Jul 2022 10:11:05 -0500 Subject: [PATCH 0288/2609] wip --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index fcd52141398..3e771f08328 100644 --- a/upgrade.md +++ b/upgrade.md @@ -132,7 +132,7 @@ public function ignore(string $class); **Likelihood Of Impact: Very Low** -Previously, in order to override the default exception handler, custom implementations were bound into the container using the `\App\Exceptions\Handler::class` type. However, you should now bind custom implementations using the `\Illuminate\Contracts\Debug\ExceptionHandler::class` type. +Previously, in order to override the default Laravel exception handler, custom implementations were bound into the service container using the `\App\Exceptions\Handler::class` type. However, you should now bind custom implementations using the `\Illuminate\Contracts\Debug\ExceptionHandler::class` type. ### Blade From b9997940068da5785a1f0489a989def7ed4a8040 Mon Sep 17 00:00:00 2001 From: Michael Dyrynda Date: Sun, 24 Jul 2022 10:00:21 +0930 Subject: [PATCH 0289/2609] Update Scout docs to clarify configuration of queue connection and name (#8068) * Update Scout docs to clarify configuration of queue connection and name * Update scout.md Co-authored-by: Taylor Otwell --- scout.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scout.md b/scout.md index 772863e854f..5753da86064 100644 --- a/scout.md +++ b/scout.md @@ -112,6 +112,13 @@ Once you have configured a queue driver, set the value of the `queue` option in Even when the `queue` option is set to `false`, it's important to remember that some Scout drivers like Algolia and Meilisearch always index records asynchronously. Meaning, even though the index operation has completed within your Laravel application, the search engine itself may not reflect the new and updated records immediately. +To specify the connection and queue that your Scout jobs utilize, you may define the `queue` configuration option as an array: + + 'queue' => [ + 'connection' => 'redis', + 'queue' => 'scout' + ], + ## Configuration From 154058e26bd3a51cea9966ffc12385d034290d1c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 25 Jul 2022 08:37:45 -0500 Subject: [PATCH 0290/2609] wip --- queries.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/queries.md b/queries.md index 64ded76de61..3b7d05d69be 100644 --- a/queries.md +++ b/queries.md @@ -868,10 +868,14 @@ If the table has an auto-incrementing id, use the `insertGetId` method to insert 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: - DB::table('flights')->upsert([ - ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], - ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] - ], ['departure', 'destination'], ['price']); + 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. From f03219cc4922655b7350a1cefa263d98f78975c1 Mon Sep 17 00:00:00 2001 From: Luke Oslizlo Date: Mon, 25 Jul 2022 17:36:00 +0200 Subject: [PATCH 0291/2609] Fix Paddle link (#8072) --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 6f7fe88b4a8..c0da2a4b92e 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -947,7 +947,7 @@ You may use the `onGenericTrial` method if you wish to know specifically that th 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. -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 dunning settings](https://vendors.paddle.com/recover-settings#dunning-form-id)), 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. 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: From 810d59ba12585f8718b328d2da09c0e743ee7656 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 25 Jul 2022 15:10:30 -0500 Subject: [PATCH 0292/2609] wip --- verification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verification.md b/verification.md index b3ed062aad3..a79e2617277 100644 --- a/verification.md +++ b/verification.md @@ -105,7 +105,7 @@ Sometimes a user may misplace or accidentally delete the email address verificat $request->user()->sendEmailVerificationNotification(); return back()->with('message', 'Verification link sent!'); - })->middleware(['auth', 'throttle:6,1'])->name('verification.send'); + })->middleware(['auth', 'throttle:6,1'])->name('verification.resend'); ### Protecting Routes From 9c965937fd74d9cd140c2b74b275d7b8947ecdf0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 25 Jul 2022 15:11:09 -0500 Subject: [PATCH 0293/2609] wip --- verification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verification.md b/verification.md index a79e2617277..b3ed062aad3 100644 --- a/verification.md +++ b/verification.md @@ -105,7 +105,7 @@ Sometimes a user may misplace or accidentally delete the email address verificat $request->user()->sendEmailVerificationNotification(); return back()->with('message', 'Verification link sent!'); - })->middleware(['auth', 'throttle:6,1'])->name('verification.resend'); + })->middleware(['auth', 'throttle:6,1'])->name('verification.send'); ### Protecting Routes From 9dafec1547b8ad5076bb1bf010402e6f4e252245 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 26 Jul 2022 08:48:24 -0500 Subject: [PATCH 0294/2609] document validating files --- validation.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/validation.md b/validation.md index 0953b456fe9..b2f7cf8c15f 100644 --- a/validation.md +++ b/validation.md @@ -29,6 +29,7 @@ - [Validating Arrays](#validating-arrays) - [Validating Nested Array Input](#validating-nested-array-input) - [Error Message Indexes & Positions](#error-message-indexes-and-positions) +- [Validating Files](#validating-files) - [Validating Passwords](#validating-passwords) - [Custom Validation Rules](#custom-validation-rules) - [Using Rule Objects](#using-rule-objects) @@ -1776,6 +1777,48 @@ When validating arrays, you may want to reference the index or position of a par Given the example above, validation will fail and the user will be presented with the following error of _"Please describe photo #2."_ + +## Validating Files + +Laravel provides a variety of validation rules that may be used to validate uploaded files, such as `mimes`, `image`, `min`, and `max`. While you are free to specify these rules individually when validating files, Laravel also offers a fluent file validation rule builder that you may find convenient: + + use Illuminate\Support\Facades\Validator; + use Illuminate\Validation\Rules\File; + + Validator::validate($input, [ + 'attachment' => [ + 'required', + File::types(['mp3', 'wav']) + ->min(1024) + ->max(12 * 1024), + ], + ]); + +If your application accepts images uploaded by your users, you may use the `File` rule's `image` constructor method to indicate that the uploaded file should be an image. In addition, the `dimensions` rule may be used to limit the dimensions of the image: + + use Illuminate\Support\Facades\Validator; + use Illuminate\Validation\Rules\Dimensions; + use Illuminate\Validation\Rules\File; + + Validator::validate($input, [ + 'photo' => [ + 'required', + File::image() + ->min(1024) + ->max(12 * 1024) + ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)), + ], + ]); + +> {tip} More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). + + +#### File Types + +Even though you only need to specify the extensions when invoking the `types` method, this method 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: + +[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) + ## Validating Passwords From db8b2d6d680e38312fae49561002a42e25083f7f Mon Sep 17 00:00:00 2001 From: Owen Voke Date: Tue, 26 Jul 2022 15:45:36 +0100 Subject: [PATCH 0295/2609] docs: remove unused import from File validation (#8075) --- validation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/validation.md b/validation.md index b2f7cf8c15f..49a5290ca02 100644 --- a/validation.md +++ b/validation.md @@ -1797,7 +1797,6 @@ Laravel provides a variety of validation rules that may be used to validate uplo If your application accepts images uploaded by your users, you may use the `File` rule's `image` constructor method to indicate that the uploaded file should be an image. In addition, the `dimensions` rule may be used to limit the dimensions of the image: use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rules\Dimensions; use Illuminate\Validation\Rules\File; Validator::validate($input, [ From 41d7f41c603155e3f98e83c14d22c389591b3e16 Mon Sep 17 00:00:00 2001 From: Takinur M <60936167+takinur@users.noreply.github.com> Date: Wed, 27 Jul 2022 19:41:13 +0600 Subject: [PATCH 0296/2609] [9.x] Dry running migrations command (#8077) * Dry running migrations command * Update migrations.md Co-authored-by: Taylor Otwell --- migrations.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/migrations.md b/migrations.md index ffcbd2336ed..fad9ec83ee3 100644 --- a/migrations.md +++ b/migrations.md @@ -141,6 +141,12 @@ If you would like to see which migrations have run thus far, you may use the `mi php artisan migrate:status ``` +If you would like to see the SQL statements that will be executed by the migrations without actually running them, you may provide the `--pretend` flag to the `migrate` command: + +```shell +php artisan migrate --pretend +``` + #### Forcing Migrations To Run In Production From 16e18d9f6d35b4023e7cc39e9e6b4422ae999144 Mon Sep 17 00:00:00 2001 From: Abbas mkhzomi Date: Wed, 27 Jul 2022 18:13:57 +0430 Subject: [PATCH 0297/2609] [9.x] add api config in auth (#8076) * Update passport.md * Update passport.md Co-authored-by: Taylor Otwell --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index aaa7c86ca6e..bd0f4082851 100644 --- a/passport.md +++ b/passport.md @@ -132,7 +132,7 @@ Next, you should call the `Passport::routes` method within the `boot` method of } } -Finally, in your application's `config/auth.php` configuration file, you should set the `driver` option of the `api` authentication guard to `passport`. This will instruct your application to use Passport's `TokenGuard` when authenticating incoming API requests: +Finally, in your application's `config/auth.php` configuration file, you should define an `api` authentication guard and set the `driver` option to `passport`. This will instruct your application to use Passport's `TokenGuard` when authenticating incoming API requests: 'guards' => [ 'web' => [ From 4422c8a1a1d9d8f83541606a8b6294b5653ea6e8 Mon Sep 17 00:00:00 2001 From: Mahmoud Mohamed Ramadan <48416569+mahmoudmohamedramadan@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:44:34 +0200 Subject: [PATCH 0298/2609] wip (#8079) * wip * Update eloquent-resources.md Co-authored-by: Taylor Otwell --- eloquent-resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index beb7aa3f1cd..f6169796e03 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -344,7 +344,7 @@ If you would like to use a custom key instead of `data`, you may define a `$wrap /** * The "data" wrapper that should be applied. * - * @var string + * @var string|null */ public static $wrap = 'user'; } From 17e6fa36c639ab180355ff76e29172ed50a1b255 Mon Sep 17 00:00:00 2001 From: Anjorin Damilare Date: Thu, 28 Jul 2022 15:08:57 +0100 Subject: [PATCH 0299/2609] [9.x]: add `throwUnless` doc to http client (#8078) --- http-client.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/http-client.md b/http-client.md index 214aeeb04f1..9c8ac39cf98 100644 --- a/http-client.md +++ b/http-client.md @@ -237,6 +237,9 @@ If you have a response instance and would like to throw an instance of `Illumina // Throw an exception if an error occurred and the given condition is true... $response->throwIf($condition); + + // Throw an exception if an error occurred and the given condition is false... + $response->throwUnless($condition); return $response['user']['id']; From a3f76894e748e9e3c0db053fd66dca2d00dd6661 Mon Sep 17 00:00:00 2001 From: Chris Hayes Date: Fri, 29 Jul 2022 09:00:56 -0400 Subject: [PATCH 0300/2609] Remove apostrophe (#8081) --- frontend.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend.md b/frontend.md index b67562396ae..c79a847fe83 100644 --- a/frontend.md +++ b/frontend.md @@ -189,7 +189,7 @@ If you would like to build your frontend using Inertia and Vue / React, you can Regardless of whether you choose to develop your frontend using Blade and Livewire or Vue / React and Inertia, you will likely need to bundle your application's CSS into production ready assets. Of course, if you choose to build your application's frontend with Vue or React, you will also need to bundle your components into browser ready JavaScript assets. -By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. Vite provides lightning-fast build times and near instantaneous Hot Module Replacement (HMR) during local development. In all new Laravel application's, including those using our [starter kits](/docs/{{version}}/starter-kits), you will find a `vite.config.js` file that loads our light-weight Laravel Vite plugin that makes Vite a joy to use with Laravel applications. +By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. Vite provides lightning-fast build times and near instantaneous Hot Module Replacement (HMR) during local development. In all new Laravel applications, including those using our [starter kits](/docs/{{version}}/starter-kits), you will find a `vite.config.js` file that loads our light-weight Laravel Vite plugin that makes Vite a joy to use with Laravel applications. The fastest way to get started with Laravel and Vite is by beginning your application's development using [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), our simplest starter kit that jump-starts your application by providing frontend and backend authentication scaffolding. From 638b372f9bebaaa3f235f4be8dff767f99ba2c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20B=C3=B6hnisch?= Date: Fri, 29 Jul 2022 09:01:37 -0400 Subject: [PATCH 0301/2609] Remove erroneous 'to' and clarify balancing behavior. (#8080) The last sentence in the balancing block was slightly confusing to read. I believe this more clearly reflects what the default behavior is. Also removed an erroneous 'to'. --- horizon.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/horizon.md b/horizon.md index 359fabb6902..e71d1a4a7ef 100644 --- a/horizon.md +++ b/horizon.md @@ -112,9 +112,9 @@ When using the `auto` strategy, you may define the `minProcesses` and `maxProces ], ], -The `balanceMaxShift` and `balanceCooldown` configuration values to determine how quickly Horizon will scale to meet worker demand. In the example above, a maximum of one new process will be created or destroyed every three seconds. You are free to tweak these values as necessary based on your application's needs. +The `balanceMaxShift` and `balanceCooldown` configuration values determine how quickly Horizon will scale to meet worker demand. In the example above, a maximum of one new process will be created or destroyed every three seconds. You are free to tweak these values as necessary based on your application's needs. -When the `balance` option is set to `false`, the default Laravel behavior will be used, which processes queues in the order they are listed in your configuration. +When the `balance` option is set to `false`, the default Laravel behavior will be used, wherein queues are processed in the order they are listed in your configuration. ### Dashboard Authorization From 9e99d5d67409c61df2671276fca452c120488063 Mon Sep 17 00:00:00 2001 From: Ivanka Todorova <1038697+fakeheal@users.noreply.github.com> Date: Mon, 1 Aug 2022 16:33:09 +0300 Subject: [PATCH 0302/2609] Added `return` in `withMiddleware` examples (#8088) --- http-client.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/http-client.md b/http-client.md index 9c8ac39cf98..31e64d186c5 100644 --- a/http-client.md +++ b/http-client.md @@ -267,6 +267,8 @@ Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guz $response = Http::withMiddleware( Middleware::mapRequest(function (RequestInterface $request) { $request->withHeader('X-Example', 'Value'); + + return $request; }) ->get('http://example.com/'); @@ -281,6 +283,8 @@ Likewise, you can inspect the incoming HTTP response by registering a middleware $header = $response->getHeader('X-Example'); // ... + + return $response; }) )->get('http://example.com/'); From 33b59d5edbd8484ca9c35d7b5b9c2eaffdf20206 Mon Sep 17 00:00:00 2001 From: Jeroen van Rensen <46967616+jeroenvanrensen@users.noreply.github.com> Date: Mon, 1 Aug 2022 20:34:37 +0700 Subject: [PATCH 0303/2609] Add Node version message (#8087) * Add version message * Update vite.md Co-authored-by: Taylor Otwell --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 91067c41cee..91fe44c12a4 100644 --- a/vite.md +++ b/vite.md @@ -48,7 +48,7 @@ Have you started a new Laravel application using our Vite scaffolding but need t ### Installing Node -You must ensure that Node.js and NPM are installed before running Vite and the Laravel plugin: +You must ensure that Node.js (16+) and NPM are installed before running Vite and the Laravel plugin: ```sh node -v From 43090fc1ab2ff665cd0f109e5d4b7634fb75221d Mon Sep 17 00:00:00 2001 From: El Shino <44731419+shino47@users.noreply.github.com> Date: Mon, 1 Aug 2022 08:47:17 -0500 Subject: [PATCH 0304/2609] [9.x] Added docs for lineIf and attachMany (#8085) * Added docs for lineIf and attachMany * tabs * formatting Co-authored-by: Taylor Otwell --- notifications.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/notifications.md b/notifications.md index c84750063b8..5e176d03fd9 100644 --- a/notifications.md +++ b/notifications.md @@ -317,6 +317,7 @@ The `MailMessage` class contains a few simple methods to help you build transact return (new MailMessage) ->greeting('Hello!') ->line('One of your invoices has been paid!') + ->lineIf($this->amount > 0, "Amount paid: {$this->amount}") ->action('View Invoice', $url) ->line('Thank you for using our application!'); } @@ -532,6 +533,27 @@ Unlike attaching files in mailable objects, you may not attach a file directly f ->attachFromStorage('/path/to/file'); } +When necessary, multiple files may be attached to a message using the `attachMany` method: + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->greeting('Hello!') + ->attachMany([ + '/path/to/forge.svg', + '/path/to/vapor.svg' => [ + 'as' => 'Logo.svg', + 'mime' => 'image/svg+xml', + ], + ]); + } + #### Raw Data Attachments From 26398757067916bacbb409129ba0770ed866f109 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 2 Aug 2022 02:13:06 +1000 Subject: [PATCH 0305/2609] [9.x] Document Script & Style Attributes with Vite (#8086) * Document Script & Style Attributes * formatting * formatting * formatting Co-authored-by: Taylor Otwell --- vite.md | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/vite.md b/vite.md index 91fe44c12a4..3469e73acbd 100644 --- a/vite.md +++ b/vite.md @@ -18,6 +18,10 @@ - [Custom Base URLs](#custom-base-urls) - [Environment Variables](#environment-variables) - [Server-Side Rendering (SSR)](#ssr) +- [Script & Style Tag Attributes](#script-and-style-attributes) + - [Content Security Policy (CSP) Nonce](#content-security-policy-csp-nonce) + - [Subresource Integrity (SRI)](#subresource-integrity-sri) + - [Arbitrary Attributes](#arbitrary-attributes) ## Introduction @@ -452,3 +456,128 @@ node bootstrap/ssr/ssr.mjs ``` > {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. + + +## Script & Style Tag Attributes + + +### Content Security Policy (CSP) Nonce + +If you wish to include a [`nonce` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) on your script and style tags as part of your [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), you may generate or specify a nonce using the `useCspNonce` method within a custom [middleware](/docs/{{version}}/middleware): + +```php +withHeaders([ + 'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'", + ]); + } +} +``` + +After invoking the `useCspNonce` method, Laravel will automatically include the `nonce` attributes on all generated script and style tags. + +If you need to specify the nonce elsewhere, including the [Ziggy `@route` directive](https://github.com/tighten/ziggy#using-routes-with-a-content-security-policy) included with Laravel's [starter kits](/docs/{{version}}/starter-kits), you may retrieve it using the `cspNonce` method: + +```blade +@routes(nonce: Vite::cspNonce()) +``` + +If you already have a nonce that you would like to instruct Laravel to use, you may pass the nonce to the `useCspNonce` method: + +```php +Vite::useCspNonce($nonce); +``` + + +### Subresource Integrity (SRI) + +If your Vite manifest includes `integrity` hashes for your assets, Laravel will automatically add the `integrity` attribute on any script and style tags it generates in order to enforce [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). By default, Vite does not include the `integrity` hash in its manifest, but you may enable it by installing the [`vite-plugin-manifest-uri`](https://www.npmjs.com/package/vite-plugin-manifest-sri) NPM plugin: + +```shell +npm install -D vite-plugin-manifest-sri +``` + +You may then enable this plugin in your `vite.config.js` file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import manifestSRI from 'vite-plugin-manifest-sri';// [tl! add] + +export default defineConfig({ + plugins: [ + laravel({ + // ... + }), + manifestSRI(),// [tl! add] + ], +}); +``` + +If required, you may also customize the manifest key where the integrity hash can be found: + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useIntegrityKey('custom-integrity-key'); +``` + +If you would like to disable this auto-detection completely, you may pass `false` to the `useIntegrityKey` method: + +```php +Vite::useIntegrityKey(false); +``` + + +### Arbitrary Attributes + +If you need to include additional attributes on your script and style tags, such as the [`data-turbo-track`](https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change) attribute, you may specify them via the `useScriptTagAttributes` and `useStyleTagAttributes` methods. Typically, this methods should be invoked from a [service provider](/docs/{{version}}/providers): + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useScriptTagAttributes([ + 'data-turbo-track' => 'reload', // Specify a value for the attribute... + 'async' => true, // Specify an attribute without a value... + 'integrity' => false, // Exclude an attribute that would otherwise be included... +]); + +Vite::useStyleTagAttributes([ + 'data-turbo-track' => 'reload', +]); +``` + +If you need to conditionally add attributes, you may pass a callback that will receive the asset source path, its URL, its manifest chunk, and the entire manifest: + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [ + 'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false, +]); + +Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [ + 'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false, +]); +``` + +> {note} The `$chunk` and `$manifest` arguments will be `null` while the Vite development server is running. From c8a13859495499584044a10babaf1621ffc2966b Mon Sep 17 00:00:00 2001 From: Hamees Ahmed Khan <32523517+hameesakhan@users.noreply.github.com> Date: Mon, 1 Aug 2022 23:36:17 +0500 Subject: [PATCH 0306/2609] Made installation scripts order suitable for exec (#8089) `npm run dev` keeps running so it's better to put `php artisan migrate` before this command so that it executes successfully if copied and pasted for execution. --- starter-kits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index 093b6970811..70b6defd923 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -45,9 +45,9 @@ The default Breeze "stack" is the Blade stack, which utilizes simple [Blade temp ```shell php artisan breeze:install +php artisan migrate npm install npm run dev -php artisan migrate ``` 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. @@ -68,9 +68,9 @@ php artisan breeze:install vue php artisan breeze:install react +php artisan migrate npm install npm run dev -php artisan migrate ``` 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. From e49a6728342f781bbda24c2207b7fce1ff5f8a98 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 2 Aug 2022 09:35:37 -0500 Subject: [PATCH 0307/2609] add whereNot --- http-tests.md | 1 + 1 file changed, 1 insertion(+) diff --git a/http-tests.md b/http-tests.md index 085ade74bed..529fe359caa 100644 --- a/http-tests.md +++ b/http-tests.md @@ -362,6 +362,7 @@ Laravel also offers a beautiful way to fluently test your application's JSON res ->assertJson(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->whereNot('status', 'pending') ->missing('password') ->etc() ); From a9caab17b3b7d3174342406155523f9d305c85e9 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 2 Aug 2022 19:59:41 +0200 Subject: [PATCH 0308/2609] [9.x] Improve wording for subscriptions with multiple products (#8091) * Update billing.md * Update billing.md --- billing.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/billing.md b/billing.md index 9ef12e38697..ace531a7b37 100644 --- a/billing.md +++ b/billing.md @@ -31,7 +31,7 @@ - [Checking Subscription Status](#checking-subscription-status) - [Changing Prices](#changing-prices) - [Subscription Quantity](#subscription-quantity) - - [Multiprice Subscriptions](#multiprice-subscriptions) + - [Subscriptions With Multiple Products](#subscriptions-with-multiple-products) - [Metered Billing](#metered-billing) - [Subscription Taxes](#subscription-taxes) - [Subscription Anchor Date](#subscription-anchor-date) @@ -955,19 +955,19 @@ 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). - -#### Multiprice Subscription Quantities + +#### Quantities For Subscriptions With Multiple Products -If your subscription is a [multiprice subscription](#multiprice-subscriptions), you should pass the name of the price whose quantity you wish to increment or decrement as the second argument to the increment / decrement methods: +If your subscription is a [subscription with multiple products](#subscriptions-with-multiple-products), you should pass the ID of the price whose quantity you wish to increment or decrement as the second argument to the increment / decrement methods: $user->subscription('default')->incrementQuantity(1, 'price_chat'); - -### Multiprice Subscriptions + +### Subscriptions With Multiple Products -[Multiprice subscriptions](https://stripe.com/docs/billing/subscriptions/multiple-products) allow you to assign multiple billing prices to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription price of $10 per month but offers a live chat add-on price for an additional $15 per month. Multiprice subscription information is stored in Cashier's `subscription_items` database table. +[Subscription with multiple products](https://stripe.com/docs/billing/subscriptions/multiple-products) allow you to assign multiple billing products to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription price of $10 per month but offers a live chat add-on product for an additional $15 per month. Information for subscriptions with multiple products is stored in Cashier's `subscription_items` database table. -You may specify multiple prices for a given subscription by passing an array of prices as the second argument to the `newSubscription` method: +You may specify multiple products for a given subscription by passing an array of prices as the second argument to the `newSubscription` method: use Illuminate\Http\Request; @@ -1013,7 +1013,7 @@ You may remove prices from subscriptions using the `removePrice` method: #### Swapping Prices -You may also change the prices attached to a multiprice subscription. For example, imagine a customer has a `price_basic` subscription with a `price_chat` add-on price and you want to upgrade the customer from the `price_basic` to the `price_pro` price: +You may also change the prices attached to a subscription with multiple products. For example, imagine a customer has a `price_basic` subscription with a `price_chat` add-on product and you want to upgrade the customer from the `price_basic` to the `price_pro` price: use App\Models\User; @@ -1043,7 +1043,7 @@ If you want to swap a single price on a subscription, you may do so using the `s #### Proration -By default, Stripe will prorate charges when adding or removing prices from a multiprice subscription. If you would like to make a price adjustment without proration, you should chain the `noProrate` method onto your price operation: +By default, Stripe will prorate charges when adding or removing prices from a subscription with multiple products. If you would like to make a price adjustment without proration, you should chain the `noProrate` method onto your price operation: $user->subscription('default')->noProrate()->removePrice('price_chat'); @@ -1182,7 +1182,7 @@ To specify the tax rates a user pays on a subscription, you should implement the 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 multiprice subscriptions, you may define different tax rates for each price by implementing a `priceTaxRates` method on your billable model: +If you're offering subscriptions with multiple products, you may define different tax rates for each price by implementing a `priceTaxRates` method on your billable model: /** * The tax rates that should apply to the customer's subscriptions. @@ -1205,7 +1205,7 @@ When changing the hard-coded tax rate IDs returned by the `taxRates` method, the $user->subscription('default')->syncTaxRates(); -This will also sync any multiprice subscription item tax rates. If your application is offering multiprice subscriptions, you should ensure that your billable model implements the `priceTaxRates` method [discussed above](#subscription-taxes). +This will also sync any item tax rates for a subscription with multiple products. If your application is offering subscriptions with multiple products, you should ensure that your billable model implements the `priceTaxRates` method [discussed above](#subscription-taxes). #### Tax Exemption From 12c93430d4ad303dafe6f7b85c72a313a57768ae Mon Sep 17 00:00:00 2001 From: Danilo Pinotti Date: Tue, 2 Aug 2022 15:06:21 -0300 Subject: [PATCH 0309/2609] Add documentation about many schedules using same job with onOneServer (#8090) * Add documentation about many schedules using same job with onOneServer option * Update scheduling.md Co-authored-by: Taylor Otwell --- scheduling.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/scheduling.md b/scheduling.md index e32ef5ccdc2..d9bef6d6fbd 100644 --- a/scheduling.md +++ b/scheduling.md @@ -278,6 +278,23 @@ To indicate that the task should run on only one server, use the `onOneServer` m ->at('17:00') ->onOneServer(); + +#### Naming Single Server Jobs + +Sometimes you may need to schedule the same job to be dispatched with different parameters, while still instructing Laravel to run each permutation of the job on a single server. To accomplish this, you may assign each schedule definition a unique name via the `name` method: + +```php +$schedule->job(new CheckUptime('https://laravel.com/')) + ->name('check_uptime:laravel.com') + ->everyFiveMinutes() + ->onOneServer(); + +$schedule->job(new CheckUptime('https://vapor.laravel.com/')) + ->name('check_uptime:vapor.laravel.com') + ->everyFiveMinutes() + ->onOneServer(); +``` + ### Background Tasks From c3ee18951a084d79ca3117738964bd7fad865c4d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 3 Aug 2022 09:25:18 -0500 Subject: [PATCH 0310/2609] add arg --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 4173a62cd36..104381aec2f 100644 --- a/mail.md +++ b/mail.md @@ -523,7 +523,7 @@ Of course, attachment data may be stored on a remote file storage service such a In addition, you may create attachment instances via data that you have in memory. To accomplish this, provide a closure to the `fromData` method. The closure should return the raw data that represents the attachment: - return Attachment::fromData(fn () => $this->content); + return Attachment::fromData(fn () => $this->content, 'Photo Name'); Laravel also provides additional methods that you may use to customize your attachments. For example, you may use the `as` and `withMime` methods to customize the file's name and MIME type: From 508559f0f0cde83ea3cea1f1f668d623d584e8f6 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Thu, 4 Aug 2022 23:44:08 +1000 Subject: [PATCH 0311/2609] Fix Vite CSP middleware example (#8097) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 3469e73acbd..a03b35b3366 100644 --- a/vite.md +++ b/vite.md @@ -486,7 +486,7 @@ class AddContentSecurityPolicyHeaders { Vite::useCspNonce(); - return $next($response)->withHeaders([ + return $next($request)->withHeaders([ 'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'", ]); } From 9e0d1080f860c52bc048a99f3dbea2706a41a901 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 4 Aug 2022 20:19:31 +0200 Subject: [PATCH 0312/2609] Adopt GitHub style of notes (#8092) --- artisan.md | 12 +++-- authentication.md | 18 ++++--- authorization.md | 15 ++++-- billing.md | 102 +++++++++++++++++++++++++------------- blade.md | 42 ++++++++++------ broadcasting.md | 30 +++++++---- cache.md | 21 +++++--- cashier-paddle.md | 45 +++++++++++------ collections.md | 57 ++++++++++++++------- configuration.md | 15 ++++-- container.md | 3 +- controllers.md | 9 ++-- csrf.md | 6 ++- database-testing.md | 6 ++- database.md | 7 +-- deployment.md | 6 ++- dusk.md | 30 +++++++---- eloquent-mutators.md | 12 +++-- eloquent-relationships.md | 36 +++++++++----- eloquent-resources.md | 15 ++++-- eloquent-serialization.md | 6 ++- eloquent.md | 39 ++++++++++----- errors.md | 9 ++-- events.md | 12 +++-- filesystem.md | 9 ++-- fortify.md | 3 +- frontend.md | 3 +- hashing.md | 3 +- helpers.md | 3 +- homestead.md | 33 ++++++++---- horizon.md | 24 ++++++--- http-client.md | 3 +- http-tests.md | 18 ++++--- installation.md | 27 ++++++---- localization.md | 6 ++- logging.md | 6 ++- mail.md | 18 ++++--- middleware.md | 6 ++- migrations.md | 28 +++++++---- mix.md | 3 +- mocking.md | 9 ++-- notifications.md | 27 ++++++---- octane.md | 30 +++++++---- packages.md | 6 ++- pagination.md | 6 ++- passport.md | 42 ++++++++++------ passwords.md | 6 ++- providers.md | 3 +- queries.md | 36 +++++++++----- queues.md | 87 +++++++++++++++++++++----------- rate-limiting.md | 3 +- redis.md | 6 ++- releases.md | 3 +- requests.md | 12 +++-- responses.md | 6 ++- routing.md | 27 ++++++---- sail.md | 6 ++- sanctum.md | 21 +++++--- scheduling.md | 12 +++-- scout.md | 18 ++++--- seeding.md | 6 ++- session.md | 15 ++++-- socialite.md | 15 ++++-- starter-kits.md | 3 +- structure.md | 3 +- telescope.md | 3 +- testing.md | 12 +++-- upgrade.md | 9 ++-- valet.md | 12 +++-- validation.md | 42 ++++++++++------ verification.md | 9 ++-- views.md | 10 ++-- vite.md | 21 +++++--- 73 files changed, 846 insertions(+), 426 deletions(-) diff --git a/artisan.md b/artisan.md index 86dba2b6693..683c4b023cd 100644 --- a/artisan.md +++ b/artisan.md @@ -60,7 +60,8 @@ All Laravel applications include Tinker by default. However, you may install Tin composer require laravel/tinker ``` -> {tip} Looking for a graphical UI for interacting with your Laravel application? Check out [Tinkerwell](https://tinkerwell.app)! +> **Note** +> Looking for a graphical UI for interacting with your Laravel application? Check out [Tinkerwell](https://tinkerwell.app)! #### Usage @@ -77,7 +78,8 @@ You can publish Tinker's configuration file using the `vendor:publish` command: php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider" ``` -> {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. +> **Warning** +> 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 Allow List @@ -154,7 +156,8 @@ Let's take a look at an example command. Note that we are able to request any de } } -> {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. +> **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. ### Closure Commands @@ -495,7 +498,8 @@ Sometimes, you may need more manual control over how a progress bar is advanced. $bar->finish(); -> {tip} For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html). +> **Note** +> For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html). ## Registering Commands diff --git a/authentication.md b/authentication.md index faa8c931c42..2749b7b3eb6 100644 --- a/authentication.md +++ b/authentication.md @@ -39,7 +39,8 @@ Providers define how users are retrieved from your persistent storage. Laravel s 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. -> {tip} Guards and providers should not be confused with "roles" and "permissions". To learn more about authorizing user actions via permissions, please refer to the [authorization](/docs/{{version}}/authorization) documentation. +> **Note** +> Guards and providers should not be confused with "roles" and "permissions". To learn more about authorizing user actions via permissions, please refer to the [authorization](/docs/{{version}}/authorization) documentation. ### Starter Kits @@ -114,7 +115,8 @@ 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 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). +> **Warning** +> 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). ### Install A Starter Kit @@ -171,7 +173,8 @@ To determine if the user making the incoming HTTP request is authenticated, you // The user is logged in... } -> {tip} Even though it is possible to determine if a user is authenticated using the `check` method, you will typically use a middleware to verify that the user is authenticated before allowing the user access to certain routes / controllers. To learn more about this, check out the documentation on [protecting routes](/docs/{{version}}/authentication#protecting-routes). +> **Note** +> Even though it is possible to determine if a user is authenticated using the `check` method, you will typically use a middleware to verify that the user is authenticated before allowing the user access to certain routes / controllers. To learn more about this, check out the documentation on [protecting routes](/docs/{{version}}/authentication#protecting-routes). ### Protecting Routes @@ -212,7 +215,8 @@ When attaching the `auth` middleware to a route, you may also specify which "gua 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 other routes in your application, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). +> **Note** +> 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 @@ -272,7 +276,8 @@ If you wish, you may also add extra query conditions to the authentication query // 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 table. +> **Warning** +> 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. The `attemptWhen` method, which receives a closure as its second argument, may be used to perform more extensive inspection of the potential user before actually authenticating the user. The closure receives the potential user and should return `true` or `false` to indicate if the user may be authenticated: @@ -464,7 +469,8 @@ When the `logoutOtherDevices` method is invoked, the user's other sessions will 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 application starter kits](/docs/{{version}}/starter-kits) include support for this feature! +> **Note** +> 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 diff --git a/authorization.md b/authorization.md index 2a5f2dbbd01..3175bcd6bd3 100644 --- a/authorization.md +++ b/authorization.md @@ -38,7 +38,8 @@ You do not need to choose between exclusively using gates or exclusively using p ### Writing Gates -> {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. +> **Warning** +> 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. @@ -335,7 +336,8 @@ If you would like to define your own policy discovery logic, you may register a // 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. +> **Warning** +> Any policies that are explicitly mapped in your `AuthServiceProvider` will take precedence over any potentially auto-discovered policies. ## Writing Policies @@ -373,7 +375,8 @@ You may continue to define additional methods on the policy as needed for the va 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. +> **Note** +> 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 @@ -525,7 +528,8 @@ For certain users, you may wish to authorize all actions within a given policy. 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. +> **Warning** +> 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. ## Authorizing Actions Using Policies @@ -692,7 +696,8 @@ The following controller methods will be mapped to their corresponding policy me | update | update | | destroy | delete | -> {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`. +> **Note** +> 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 diff --git a/billing.md b/billing.md index ace531a7b37..17e88f40a4a 100644 --- a/billing.md +++ b/billing.md @@ -76,7 +76,8 @@ When upgrading to a new version of Cashier, it's important that you carefully review [the upgrade guide](https://github.com/laravel/cashier-stripe/blob/master/UPGRADE.md). -> {note} To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 13 utilizes Stripe API version `2020-08-27`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. +> **Warning** +> To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 13 utilizes Stripe API version `2020-08-27`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. ## Installation @@ -87,7 +88,8 @@ 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). +> **Warning** +> To ensure Cashier properly handles all Stripe events, remember to [set up Cashier's webhook handling](#handling-stripe-webhooks). ### Database Migrations @@ -118,7 +120,8 @@ If you would like to prevent Cashier's migrations from running entirely, you may 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 `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). +> **Warning** +> 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 @@ -150,7 +153,8 @@ Cashier assumes your billable model will be the `App\Models\User` class that shi Cashier::useCustomerModel(User::class); } -> {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. +> **Warning** +> 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 @@ -163,7 +167,8 @@ STRIPE_SECRET=your-stripe-secret STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret ``` -> {note} You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. +> **Warning** +> You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. ### Currency Configuration @@ -180,7 +185,8 @@ In addition to configuring Cashier's currency, you may also specify a locale to CASHIER_CURRENCY_LOCALE=nl_BE ``` -> {note} In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. +> **Warning** +> In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. ### Tax Configuration @@ -203,7 +209,8 @@ Once tax calculation has been enabled, any new subscriptions and any one-off inv For this feature to work properly, your customer's billing details, such as the customer's name, address, and tax ID, need to be synced to Stripe. You may use the [customer data synchronization](#syncing-customer-data-with-stripe) and [Tax ID](#tax-ids) methods offered by Cashier to accomplish this. -> {note} Unfortunately, for now, no tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). In addition, Stripe Tax is currently "invite-only" during its beta period. You can request access to Stripe Tax via the [Stripe Tax website](https://stripe.com/tax#request-access). +> **Warning** +> Unfortunately, for now, no tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). In addition, Stripe Tax is currently "invite-only" during its beta period. You can request access to Stripe Tax via the [Stripe Tax website](https://stripe.com/tax#request-access). ### Logging @@ -465,7 +472,8 @@ cardButton.addEventListener('click', async (e) => { 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). -> {tip} If you would like more information about Setup Intents and gathering customer payment details please [review this overview provided by Stripe](https://stripe.com/docs/payments/save-and-reuse#php). +> **Note** +> If you would like more information about Setup Intents and gathering customer payment details please [review this overview provided by Stripe](https://stripe.com/docs/payments/save-and-reuse#php). #### Payment Methods For Single Charges @@ -572,7 +580,8 @@ 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 imposed by Stripe, it may not be used for single charges. +> **Warning** +> 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 @@ -581,7 +590,8 @@ To add a new payment method, you may call the `addPaymentMethod` method on the b $user->addPaymentMethod($paymentMethod); -> {tip} To learn how to retrieve payment method identifiers please review the [payment method storage documentation](#storing-payment-methods). +> **Note** +> To learn how to retrieve payment method identifiers please review the [payment method storage documentation](#storing-payment-methods). ### Deleting Payment Methods @@ -602,7 +612,8 @@ By default, this method will delete payment methods of the `card` type. To delet $user->deletePaymentMethods('sepa_debit'); -> {note} If a user has an active subscription, your application should not allow them to delete their default payment method. +> **Warning** +> If a user has an active subscription, your application should not allow them to delete their default payment method. ## Subscriptions @@ -628,7 +639,8 @@ The first argument passed to the `newSubscription` method should be the internal 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. +> **Warning** +> Passing a payment method identifier directly to the `create` subscription method will also automatically add it to the user's stored payment methods. #### Collecting Recurring Payments Via Invoice Emails @@ -803,7 +815,8 @@ The `recurring` method may be used to determine if the user is currently subscri // } -> {note} If a user has two subscriptions with the same name, the most recent subscription will always be returned by the `subscription` method. For example, a user might have two subscription records named `default`; however, one of the subscriptions may be an old, expired subscription, while the other is the current, active subscription. The most recent subscription will always be returned while older subscriptions are kept in the database for historical review. +> **Warning** +> If a user has two subscriptions with the same name, the most recent subscription will always be returned by the `subscription` method. For example, a user might have two subscription records named `default`; however, one of the subscriptions may be an old, expired subscription, while the other is the current, active subscription. The most recent subscription will always be returned while older subscriptions are kept in the database for historical review. #### Canceled Subscription Status @@ -863,7 +876,8 @@ If you would like the subscription to still be considered active when it's in a Cashier::keepPastDueSubscriptionsActive(); } -> {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. +> **Warning** +> 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 @@ -924,7 +938,8 @@ By default, Stripe prorates charges when swapping between prices. The `noProrate For more information on subscription proration, consult the [Stripe documentation](https://stripe.com/docs/billing/subscriptions/prorations). -> {note} Executing the `noProrate` method before the `swapAndInvoice` method will have no effect on proration. An invoice will always be issued. +> **Warning** +> Executing the `noProrate` method before the `swapAndInvoice` method will have no effect on proration. An invoice will always be issued. ### Subscription Quantity @@ -1008,7 +1023,8 @@ You may remove prices from subscriptions using the `removePrice` method: $user->subscription('default')->removePrice('price_chat'); -> {note} You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. +> **Warning** +> You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. #### Swapping Prices @@ -1060,7 +1076,8 @@ If you would like to update quantities on individual subscription prices, you ma $user->subscription('default')->updateQuantity(10, 'price_chat'); -> {note} When a subscription has multiple prices the `stripe_price` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual price attributes, you should use the `items` relationship available on the `Subscription` model. +> **Warning** +> When a subscription has multiple prices the `stripe_price` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual price attributes, you should use the `items` relationship available on the `Subscription` model. #### Subscription Items @@ -1166,7 +1183,8 @@ For a full reference of all usage data returned and how to use Stripe's cursor b ### Subscription Taxes -> {note} Instead of calculating Tax Rates manually, you can [automatically calculate taxes using Stripe Tax](#tax-configuration) +> **Warning** +> Instead of calculating Tax Rates manually, you can [automatically calculate taxes using Stripe Tax](#tax-configuration) 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): @@ -1196,7 +1214,8 @@ If you're offering subscriptions with multiple products, you may define differen ]; } -> {note} The `taxRates` method only applies to subscription charges. If you use Cashier to make "one off" charges, you will need to manually specify the tax rate at that time. +> **Warning** +> The `taxRates` method only applies to subscription charges. If you use Cashier to make "one off" charges, you will need to manually specify the tax rate at that time. #### Syncing Tax Rates @@ -1220,7 +1239,8 @@ Cashier also offers the `isNotTaxExempt`, `isTaxExempt`, and `reverseChargeAppli $user->isNotTaxExempt(); $user->reverseChargeApplies(); -> {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. +> **Warning** +> 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 @@ -1301,7 +1321,8 @@ If you would like to offer trial periods to your customers while still collectin 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 price in Stripe. -> {note} If the customer's subscription is not canceled 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. +> **Warning** +> If the customer's subscription is not canceled 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 that specifies when the trial period should end: @@ -1352,7 +1373,8 @@ If you would like to offer trial periods without collecting the user's payment m 'trial_ends_at' => now()->addDays(10), ]); -> {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. +> **Warning** +> 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 billable model instance will return `true` if the current date is not past the value of `trial_ends_at`: @@ -1400,7 +1422,8 @@ The `extendTrial` method allows you to extend the trial period of a subscription ## Handling Stripe Webhooks -> {tip} You may use [the Stripe CLI](https://stripe.com/docs/stripe-cli) to help test webhooks during local development. +> **Note** +> 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 automatically registered by the Cashier service provider. This controller will handle all incoming webhook requests. @@ -1439,7 +1462,8 @@ After creation, the webhook will be immediately active. If you wish to create th php artisan cashier:webhook --disabled ``` -> {note} Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](#verifying-webhook-signatures) middleware. +> **Warning** +> Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](#verifying-webhook-signatures) middleware. #### Webhooks & CSRF Protection @@ -1546,7 +1570,8 @@ The `charge` method will throw an exception if the charge fails. If the charge i // } -> {note} The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. +> **Warning** +> The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. ### Charge With Invoice @@ -1577,7 +1602,8 @@ Alternatively, you may use the `invoiceFor` method to make a "one-off" charge ag Although the `invoiceFor` method is available for you to use, it is recommended that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. -> {note} The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. +> **Warning** +> The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. ### Creating Payment Intents @@ -1608,7 +1634,8 @@ When using the `pay` method, the default payment methods that are enabled within return $payment->client_secret; }); -> {note} The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. +> **Warning** +> The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. ### Refunding Charges @@ -1814,12 +1841,14 @@ You can also perform a simple charge for an ad-hoc product that has not been cre return $request->user()->checkoutCharge(1200, 'T-Shirt', 5); }); -> {note} When using the `checkoutCharge` method, Stripe will always create a new product and price in your Stripe dashboard. Therefore, we recommend that you create the products up front in your Stripe dashboard and use the `checkout` method instead. +> **Warning** +> When using the `checkoutCharge` method, Stripe will always create a new product and price in your Stripe dashboard. Therefore, we recommend that you create the products up front in your Stripe dashboard and use the `checkout` method instead. ### Subscription Checkouts -> {note} Using Stripe Checkout for subscriptions requires you to enable the `customer.subscription.created` webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items. +> **Warning** +> Using Stripe Checkout for subscriptions requires you to enable the `customer.subscription.created` webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items. You may also use Stripe Checkout to initiate subscriptions. After defining your subscription with Cashier's subscription builder methods, you may call the `checkout `method. When a customer visits this route they will be redirected to Stripe's Checkout page: @@ -1855,7 +1884,8 @@ Of course, you can also enable promotion codes for subscription checkouts: ->checkout(); }); -> {note} Unfortunately Stripe Checkout does not support all subscription billing options when starting subscriptions. Using the `anchorBillingCycleOn` method on the subscription builder, setting proration behavior, or setting payment behavior will not have any effect during Stripe Checkout sessions. Please consult [the Stripe Checkout Session API documentation](https://stripe.com/docs/api/checkout/sessions/create) to review which parameters are available. +> **Warning** +> Unfortunately Stripe Checkout does not support all subscription billing options when starting subscriptions. Using the `anchorBillingCycleOn` method on the subscription builder, setting proration behavior, or setting payment behavior will not have any effect during Stripe Checkout sessions. Please consult [the Stripe Checkout Session API documentation](https://stripe.com/docs/api/checkout/sessions/create) to review which parameters are available. #### Stripe Checkout & Trial Periods @@ -1882,7 +1912,8 @@ Checkout also supports collecting a customer's Tax ID. To enable this on a check When this method is invoked, a new checkbox will be available to the customer that allows them to indicate if they're purchasing as a company. If so, they will have the opportunity to provide their Tax ID number. -> {note} If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. +> **Warning** +> If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. ## Handling Failed Payments @@ -1955,7 +1986,8 @@ You can derive the specific status of an incomplete payment by inspecting the `p If your business or one of your customers 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). +> **Warning** +> 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 @@ -1982,7 +2014,8 @@ CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment To ensure that off-session payment confirmation notifications are delivered, verify that [Stripe webhooks are configured](#handling-stripe-webhooks) for your application and the `invoice.payment_action_required` webhook is enabled in your Stripe dashboard. In addition, your `Billable` model should also use Laravel's `Illuminate\Notifications\Notifiable` trait. -> {note} Notifications will be sent even when customers are manually making a payment that requires additional confirmation. Unfortunately, there is no way for Stripe to know that the payment was done manually or "off-session". But, a customer will simply see a "Payment Successful" message if they visit the payment page after already confirming their payment. The customer will not be allowed to accidentally confirm the same payment twice and incur an accidental second charge. +> **Warning** +> Notifications will be sent even when customers are manually making a payment that requires additional confirmation. Unfortunately, there is no way for Stripe to know that the payment was done manually or "off-session". But, a customer will simply see a "Payment Successful" message if they visit the payment page after already confirming their payment. The customer will not be allowed to accidentally confirm the same payment twice and incur an accidental second charge. ## Stripe SDK @@ -2018,4 +2051,5 @@ 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 / prices 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. +> **Note** +> 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 3e498ab056e..8378e727858 100644 --- a/blade.md +++ b/blade.md @@ -54,7 +54,8 @@ Blade views may be returned from routes or controllers using the global `view` h return view('greeting', ['name' => 'Finn']); }); -> {tip} Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://laravel-livewire.com). +> **Note** +> Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://laravel-livewire.com). ## Displaying Data @@ -71,7 +72,8 @@ You may display the contents of the `name` variable like so: Hello, {{ $name }}. ``` -> {tip} Blade's `{{ }}` echo statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. +> **Note** +> 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: @@ -113,7 +115,8 @@ By default, Blade `{{ }}` statements are automatically sent through PHP's `htmls 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. +> **Warning** +> 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 @@ -165,7 +168,8 @@ The latest versions of the Laravel application skeleton include a `Js` facade, w ``` -> {note} You should only use the `Js::from` method 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. +> **Warning** +> You should only use the `Js::from` method 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. #### The `@verbatim` Directive @@ -340,7 +344,8 @@ In addition to conditional statements, Blade provides simple directives for work @endwhile ``` -> {tip} While iterating through a `foreach` loop, 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. +> **Note** +> While iterating through a `foreach` loop, 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 skip the current iteration or end the loop using the `@continue` and `@break` directives: @@ -488,7 +493,8 @@ In addition, the `@required` directive may be used to indicate if a given elemen ### Including Subviews -> {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. +> **Note** +> 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. 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: @@ -528,7 +534,8 @@ To include the first view that exists from a given array of views, you may use t @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. +> **Warning** +> 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 @@ -547,7 +554,8 @@ You may also pass a fourth argument to the `@each` directive. This argument dete @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. +> **Warning** +> 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 @@ -910,7 +918,8 @@ All of the attributes that are not part of the component's constructor will auto
``` -> {note} Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled. +> **Warning** +> Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled. #### Default / Merged Attributes @@ -956,7 +965,8 @@ If you need to merge other attributes onto your component, you can chain the `me ``` -> {tip} If you need to conditionally compile classes on other HTML elements that shouldn't receive merged attributes, you can use the [`@class` directive](#conditional-classes). +> **Note** +> If you need to conditionally compile classes on other HTML elements that shouldn't receive merged attributes, you can use the [`@class` directive](#conditional-classes). #### Non-Class Attribute Merging @@ -1191,7 +1201,8 @@ Sometimes you may need to render a component but not know which component should ### Manually Registering Components -> {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. +> **Warning** +> 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. When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. @@ -1344,7 +1355,8 @@ Because the `color` prop was only passed into the parent (``), it won't ``` -> {note} The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. +> **Warning** +> The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. ### Anonymous Component Namespaces @@ -1496,7 +1508,8 @@ When defining a child view, use the `@extends` Blade directive to specify which 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. +> **Note** +> 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: @@ -1700,7 +1713,8 @@ As you can see, we will chain the `format` method onto whatever expression is pa format('m/d/Y H:i'); ?> -> {note} After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command. +> **Warning** +> After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command. ### Custom Echo Handlers diff --git a/broadcasting.md b/broadcasting.md index d190a292e7c..15eb33e2323 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -54,7 +54,8 @@ The core concepts behind broadcasting are simple: clients connect to named chann 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) and [soketi](https://docs.soketi.app/) 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). +> **Note** +> Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). ## Server Side Installation @@ -181,7 +182,8 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run dev ``` -> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). +> **Note** +> To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). #### Using An Existing Client Instance @@ -242,7 +244,8 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run dev ``` -> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). +> **Note** +> To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). ## Concept Overview @@ -251,7 +254,8 @@ Laravel's event broadcasting allows you to broadcast your server-side Laravel ev 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. -> {tip} If you would like to explore open source alternatives to Pusher, check out the [open source alternatives](#open-source-alternatives). +> **Note** +> If you would like to explore open source alternatives to Pusher, check out the [open source alternatives](#open-source-alternatives). ### Using An Example Application @@ -510,7 +514,8 @@ If your queue connection's `after_commit` configuration option is set to `false` public $afterCommit = true; } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). ## Authorizing Channels @@ -592,7 +597,8 @@ Just like HTTP routes, channel routes may also take advantage of implicit and ex 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. +> **Warning** +> 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 @@ -652,7 +658,8 @@ Finally, you may place the authorization logic for your channel in the channel c } } -> {tip} 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. +> **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. ## Broadcasting Events @@ -683,7 +690,8 @@ axios.post('/task', task) 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. +> **Warning** +> Your event must use the `Illuminate\Broadcasting\InteractsWithSockets` trait in order to call the `toOthers` method. #### Configuration @@ -881,7 +889,8 @@ Echo.join(`chat.${roomId}`) ## Model Broadcasting -> {note} Before reading the following documentation about model broadcasting, we recommend you become familiar with the general concepts of Laravel's model broadcasting services as well as how to manually create and listen to broadcast events. +> **Warning** +> Before reading the following documentation about model broadcasting, we recommend you become familiar with the general concepts of Laravel's model broadcasting services as well as how to manually create and listen to broadcast events. It is common to broadcast events when your application's [Eloquent models](/docs/{{version}}/eloquent) are created, updated, or deleted. Of course, this can easily be accomplished by manually [defining custom events for Eloquent model state changes](/docs/{{version}}/eloquent#events) and marking those events with the `ShouldBroadcast` interface. @@ -1075,7 +1084,8 @@ Echo.private(`App.Models.User.${this.user.id}`) ## Client Events -> {tip} When using [Pusher Channels](https://pusher.com/channels), you must enable the "Client Events" option in the "App Settings" section of your [application dashboard](https://dashboard.pusher.com/) in order to send client events. +> **Note** +> When using [Pusher Channels](https://pusher.com/channels), you must enable the "Client Events" option in the "App Settings" section of your [application dashboard](https://dashboard.pusher.com/) in order to send client events. Sometimes you may wish to broadcast an event to other connected clients without hitting your Laravel application at all. This can be particularly useful for things like "typing" notifications, where you want to alert users of your application that another user is typing a message on a given screen. diff --git a/cache.md b/cache.md index 6f9057e9c73..0c06d04859a 100644 --- a/cache.md +++ b/cache.md @@ -50,7 +50,8 @@ When using the `database` cache driver, you will need to setup a table to contai $table->integer('expiration'); }); -> {tip} You may also use the `php artisan cache:table` Artisan command to generate a migration with the proper schema. +> **Note** +> You may also use the `php artisan cache:table` Artisan command to generate a migration with the proper schema. #### Memcached @@ -216,7 +217,8 @@ The `forever` method may be used to store an item in the cache permanently. Sinc Cache::forever('key', 'value'); -> {tip} If you are using the Memcached driver, items that are stored "forever" may be removed when the cache reaches its size limit. +> **Note** +> If you are using the Memcached driver, items that are stored "forever" may be removed when the cache reaches its size limit. ### Removing Items From The Cache @@ -235,7 +237,8 @@ You may clear the entire cache using the `flush` method: Cache::flush(); -> {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. +> **Warning** +> 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 @@ -256,12 +259,14 @@ 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 the facade](/docs/{{version}}/mocking#mocking-facades). +> **Note** +> 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 -> {note} Cache tags are not supported when using the `file`, `dynamodb`, or `database` cache drivers. Furthermore, when using multiple tags with caches that are stored "forever", performance will be best with a driver such as `memcached`, which automatically purges stale records. +> **Warning** +> Cache tags are not supported when using the `file`, `dynamodb`, or `database` cache drivers. Furthermore, when using multiple tags with caches that are stored "forever", performance will be best with a driver such as `memcached`, which automatically purges stale records. ### Storing Tagged Cache Items @@ -295,7 +300,8 @@ In contrast, this statement would remove only cached values tagged with `authors ## Atomic Locks -> {note} To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. +> **Warning** +> To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. ### Driver Prerequisites @@ -411,7 +417,8 @@ We just need to implement each of these methods using a MongoDB connection. For return Cache::repository(new MongoStore); }); -> {tip} If you're wondering where to put your custom cache driver code, you could create an `Extensions` namespace within your `app` directory. However, keep in mind that Laravel does not have a rigid application structure and you are free to organize your application according to your preferences. +> **Note** +> If you're wondering where to put your custom cache driver code, you could create an `Extensions` namespace within your `app` directory. However, keep in mind that Laravel does not have a rigid application structure and you are free to organize your application according to your preferences. ### Registering The Driver diff --git a/cashier-paddle.md b/cashier-paddle.md index c0da2a4b92e..31a9f92ae74 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -64,7 +64,8 @@ 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). +> **Warning** +> To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks). ### Paddle Sandbox @@ -175,7 +176,8 @@ In addition to configuring Cashier's currency, you may also specify a locale to CASHIER_CURRENCY_LOCALE=nl_BE ``` -> {note} In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. +> **Warning** +> In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. ### Overriding Default Models @@ -244,7 +246,8 @@ The Paddle checkout widget is asynchronous. Once the user creates or updates a s 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). -> {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. +> **Warning** +> 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. #### Manually Rendering Pay Links @@ -301,7 +304,8 @@ $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 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. +> **Warning** +> 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. #### Manually Rendering An Inline Checkout @@ -431,7 +435,8 @@ You may display the original listed prices (without coupon discounts) using the ``` -> {note} When using the prices API, Paddle only allows applying coupons to one-time purchase products and not to subscription plans. +> **Warning** +> When using the prices API, Paddle only allows applying coupons to one-time purchase products and not to subscription plans. ## Customers @@ -540,7 +545,8 @@ You can also pass an array of metadata using the `withMetadata` method: ->withMetadata(['key' => 'value']) ->create(); -> {note} When providing metadata, please avoid using `subscription_name` as a metadata key. This key is reserved for internal use by Cashier. +> **Warning** +> When providing metadata, please avoid using `subscription_name` as a metadata key. This key is reserved for internal use by Cashier. ### Checking Subscription Status @@ -649,7 +655,8 @@ If you would like subscriptions to still be considered active when they are `pas 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. +> **Warning** +> 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 @@ -726,7 +733,8 @@ If you would like to swap plans and immediately invoice the user instead of wait $user->subscription('default')->swapAndInvoice($premium = 34567); -> {note} Plans may not be swapped when a trial is active. For additional information regarding this limitation, please see the [Paddle documentation](https://developer.paddle.com/api-reference/subscription-api/users/updateuser#usage-notes). +> **Warning** +> Plans may not be swapped when a trial is active. For additional information regarding this limitation, please see the [Paddle documentation](https://developer.paddle.com/api-reference/subscription-api/users/updateuser#usage-notes). #### Prorations @@ -822,7 +830,8 @@ To resume a paused a subscription, you may call the `unpause` method on the user $user->subscription('default')->unpause(); -> {note} A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. +> **Warning** +> A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. ### Cancelling Subscriptions @@ -843,7 +852,8 @@ If you wish to cancel a subscription immediately, you may call the `cancelNow` m $user->subscription('default')->cancelNow(); -> {note} Paddle's subscriptions cannot be resumed after cancellation. If your customer wishes to resume their subscription, they will have to subscribe to a new subscription. +> **Warning** +> Paddle's subscriptions cannot be resumed after cancellation. If your customer wishes to resume their subscription, they will have to subscribe to a new subscription. ## Subscription Trials @@ -851,7 +861,8 @@ If you wish to cancel a subscription immediately, you may call the `cancelNow` m ### With Payment Method Up Front -> {note} While trialing and collecting payment method details up front, Paddle prevents any subscription changes such as swapping plans or updating quantities. If you want to allow a customer to swap plans during a trial the subscription must be cancelled and recreated. +> **Warning** +> While trialing and collecting payment method details up front, Paddle prevents any subscription changes such as swapping plans or updating quantities. If you want to allow a customer to swap plans during a trial the subscription must be cancelled and recreated. 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: @@ -868,7 +879,8 @@ If you would like to offer trial periods to your customers while still collectin 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. +> **Warning** +> 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 are equivalent: @@ -940,7 +952,8 @@ You may use the `onGenericTrial` method if you wish to know specifically that th // User is within their "generic" trial period... } -> {note} There is no way to extend or modify a trial period on a Paddle subscription after it has been created. +> **Warning** +> There is no way to extend or modify a trial period on a Paddle subscription after it has been created. ## Handling Paddle Webhooks @@ -957,7 +970,8 @@ To ensure your application can handle Paddle webhooks, be sure to [configure the - Payment Succeeded - Subscription Payment Succeeded -> {note} Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. +> **Warning** +> Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. #### Webhooks & CSRF Protection @@ -1140,7 +1154,8 @@ You may optionally specify a specific amount to refund as well as a reason for t $receipt->order_id, 5.00, 'Unused product time' ); -> {tip} You can use the `$refundRequestId` as a reference for the refund when contacting Paddle support. +> **Note** +> You can use the `$refundRequestId` as a reference for the refund when contacting Paddle support. ## Receipts diff --git a/collections.md b/collections.md index 89b339ad737..c7cee482bc7 100644 --- a/collections.md +++ b/collections.md @@ -31,7 +31,8 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle $collection = collect([1, 2, 3]); -> {tip} The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. +> **Note** +> The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. ### Extending Collections @@ -368,7 +369,8 @@ The `collect` method is primarily useful for converting [lazy collections](#lazy // [1, 2, 3] -> {tip} The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. +> **Note** +> The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. #### `combine()` {.collection-method} @@ -460,7 +462,8 @@ The `containsOneItem` method determines whether the collection contains a single This method has the same signature as the [`contains`](#method-contains) method; however, all values are compared using "strict" comparisons. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). #### `count()` {.collection-method} @@ -570,7 +573,8 @@ The `diff` method compares the collection against another collection or a plain // [1, 3, 5] -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). #### `diffAssoc()` {.collection-method} @@ -774,7 +778,8 @@ The `except` method returns all items in the collection except for those with th For the inverse of `except`, see the [only](#method-only) method. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). #### `filter()` {.collection-method} @@ -956,7 +961,8 @@ The `forget` method removes an item from the collection by its key: // ['framework' => 'laravel'] -> {note} Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. +> **Warning** +> Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. #### `forPage()` {.collection-method} @@ -1150,7 +1156,8 @@ The `intersect` method removes any values from the original collection that are // [0 => 'Desk', 2 => 'Chair'] -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). #### `intersectByKeys()` {.collection-method} @@ -1317,7 +1324,8 @@ The `map` method iterates through the collection and passes each value to the gi // [2, 4, 6, 8, 10] -> {note} Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. +> **Warning** +> Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. #### `mapInto()` {.collection-method} @@ -1577,7 +1585,8 @@ The `only` method returns the items in the collection with the specified keys: For the inverse of `only`, see the [except](#method-except) method. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). #### `pad()` {.collection-method} @@ -2111,7 +2120,8 @@ You may also pass a simple value to the `skipUntil` method to skip all items unt // [3, 4] -> {note} If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. +> **Warning** +> If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. #### `skipWhile()` {.collection-method} @@ -2128,7 +2138,8 @@ The `skipWhile` method skips over items from the collection while the given call // [4] -> {note} If the callback never returns `false`, the `skipWhile` method will return an empty collection. +> **Warning** +> If the callback never returns `false`, the `skipWhile` method will return an empty collection. #### `slice()` {.collection-method} @@ -2236,7 +2247,8 @@ The `sort` method sorts the collection. The sorted collection keeps the original If your sorting needs are more advanced, you may pass a callback to `sort` with your own algorithm. Refer to the PHP documentation on [`uasort`](https://secure.php.net/manual/en/function.uasort.php#refsect1-function.uasort-parameters), which is what the collection's `sort` method calls utilizes internally. -> {tip} If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. +> **Note** +> If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. #### `sortBy()` {.collection-method} @@ -2579,7 +2591,8 @@ You may also pass a simple value to the `takeUntil` method to get the items unti // [1, 2] -> {note} If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. +> **Warning** +> If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. #### `takeWhile()` {.collection-method} @@ -2596,7 +2609,8 @@ The `takeWhile` method returns items in the collection until the given callback // [1, 2] -> {note} If the callback never returns `false`, the `takeWhile` method will return all items in the collection. +> **Warning** +> If the callback never returns `false`, the `takeWhile` method will return all items in the collection. #### `tap()` {.collection-method} @@ -2640,7 +2654,8 @@ The `toArray` method converts the collection into a plain PHP `array`. If the co ] */ -> {note} `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw array underlying the collection, use the [`all`](#method-all) method instead. +> **Warning** +> `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw array underlying the collection, use the [`all`](#method-all) method instead. #### `toJson()` {.collection-method} @@ -2668,7 +2683,8 @@ The `transform` method iterates over the collection and calls the given callback // [2, 4, 6, 8, 10] -> {note} Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. +> **Warning** +> Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. #### `undot()` {.collection-method} @@ -2771,7 +2787,8 @@ Finally, you may also pass your own closure to the `unique` method to specify wh The `unique` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`uniqueStrict`](#method-uniquestrict) method to filter using "strict" comparisons. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). #### `uniqueStrict()` {.collection-method} @@ -3279,7 +3296,8 @@ Likewise, we can use the `sum` higher order message to gather the total number o ### Introduction -> {note} Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). +> **Warning** +> Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). To supplement the already powerful `Collection` class, the `LazyCollection` class leverages PHP's [generators](https://www.php.net/manual/en/language.generators.overview.php) to allow you to work with very large datasets while keeping memory usage low. @@ -3455,7 +3473,8 @@ Almost all methods available on the `Collection` class are also available on the
-> {note} Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. +> **Warning** +> Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. ### Lazy Collection Methods diff --git a/configuration.md b/configuration.md index 2fc483e6561..da1726b5199 100644 --- a/configuration.md +++ b/configuration.md @@ -43,7 +43,8 @@ Laravel's default `.env` file contains some common configuration values that may If you are developing with a team, you may wish to continue including a `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. -> {tip} Any variable in your `.env` file can be overridden by external environment variables such as server-level or system-level environment variables. +> **Note** +> Any variable in your `.env` file can be overridden by external environment variables such as server-level or system-level environment variables. #### Environment File Security @@ -105,7 +106,8 @@ You may also pass arguments to the `environment` method to determine if the envi // The environment is either local OR staging... } -> {tip} The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. +> **Note** +> The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. ## Accessing Configuration Values @@ -128,7 +130,8 @@ To give your application a speed boost, you should cache all of your configurati You should typically run the `php artisan config:cache` command as part of your production deployment process. The command should not be run during local development as configuration options will frequently need to be changed during the course of your application's development. -> {note} If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. +> **Warning** +> If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. ## Debug Mode @@ -177,7 +180,8 @@ https://example.com/1630542a-246b-4b66-afa1-dd72a4c43515 When accessing this hidden route, you will then be redirected to the `/` route of the application. Once the cookie has been issued to your browser, you will be able to browse the application normally as if it was not in maintenance mode. -> {tip} Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?`. +> **Note** +> Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?`. #### Pre-Rendering The Maintenance Mode View @@ -208,7 +212,8 @@ To disable maintenance mode, use the `up` command: php artisan up ``` -> {tip} You may customize the default maintenance mode template by defining your own template at `resources/views/errors/503.blade.php`. +> **Note** +> You may customize the default maintenance mode template by defining your own template at `resources/views/errors/503.blade.php`. #### Maintenance Mode & Queues diff --git a/container.md b/container.md index 1a20681863a..ee41ac5bbff 100644 --- a/container.md +++ b/container.md @@ -137,7 +137,8 @@ As mentioned, you will typically be interacting with the container within servic // ... }); -> {tip} There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection. +> **Note** +> There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection. #### Binding A Singleton diff --git a/controllers.md b/controllers.md index 9cadef58600..50db5b3e8d1 100644 --- a/controllers.md +++ b/controllers.md @@ -59,7 +59,8 @@ You can define a route to this controller method like so: 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. -> {tip} Controllers are not **required** to extend a base class. However, you will not have access to convenient features such as the `middleware` and `authorize` methods. +> **Note** +> Controllers are not **required** to extend a base class. However, you will not have access to convenient features such as the `middleware` and `authorize` methods. ### Single Action Controllers @@ -98,7 +99,8 @@ You may generate an invokable controller by using the `--invokable` option of th php artisan make:controller ProvisionServer --invokable ``` -> {tip} Controller stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> Controller stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). ## Controller Middleware @@ -359,7 +361,8 @@ If you need to add additional routes to a resource controller beyond the default Route::get('/photos/popular', [PhotoController::class, 'popular']); Route::resource('photos', PhotoController::class); -> {tip} Remember to keep your controllers focused. If you find yourself routinely needing methods outside of the typical set of resource actions, consider splitting your controller into two, smaller controllers. +> **Note** +> Remember to keep your controllers focused. If you find yourself routinely needing methods outside of the typical set of resource actions, consider splitting your controller into two, smaller controllers. ## Dependency Injection & Controllers diff --git a/csrf.md b/csrf.md index fab2546f7b7..c6a1f76f98c 100644 --- a/csrf.md +++ b/csrf.md @@ -94,7 +94,8 @@ Typically, you should place these kinds of routes outside of the `web` middlewar ]; } -> {tip} For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). +> **Note** +> For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). ## X-CSRF-TOKEN @@ -122,4 +123,5 @@ Laravel stores the current CSRF token in an encrypted `XSRF-TOKEN` cookie that i This cookie is primarily sent as a developer convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the `X-XSRF-TOKEN` header on same-origin requests. -> {tip} By default, the `resources/js/bootstrap.js` file includes the Axios HTTP library which will automatically send the `X-XSRF-TOKEN` header for you. +> **Note** +> By default, the `resources/js/bootstrap.js` file includes the Axios HTTP library which will automatically send the `X-XSRF-TOKEN` header for you. diff --git a/database-testing.md b/database-testing.md index 5f309bbeb71..904b2669802 100644 --- a/database-testing.md +++ b/database-testing.md @@ -97,7 +97,8 @@ As you can see, in their most basic form, factories are classes that extend Lara Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing. -> {tip} You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. +> **Note** +> You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. ### Generating Factories @@ -246,7 +247,8 @@ Alternatively, the `state` method may be called directly on the factory instance 'name' => 'Abigail Otwell', ])->make(); -> {tip} [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. +> **Note** +> [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. ### Persisting Models diff --git a/database.md b/database.md index 9eaf476feb2..267f8cd3a99 100644 --- a/database.md +++ b/database.md @@ -214,7 +214,8 @@ Sometimes you may want to execute an SQL statement without binding any values. Y DB::unprepared('update users set votes = 100 where name = "Dries"'); -> {note} Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement. +> **Warning** +> Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement. #### Implicit Commits @@ -358,7 +359,8 @@ Lastly, you can commit a transaction via the `commit` method: DB::commit(); -> {tip} The `DB` facade's transaction methods control the transactions for both the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent). +> **Note** +> The `DB` facade's transaction methods control the transactions for both the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent). ## Connecting To The Database CLI @@ -374,4 +376,3 @@ If needed, you may specify a database connection name to connect to a database c ```shell php artisan db mysql ``` - diff --git a/deployment.md b/deployment.md index 2e3fdd46f38..fd4ff5a9c76 100644 --- a/deployment.md +++ b/deployment.md @@ -97,7 +97,8 @@ When deploying to production, make sure that you are optimizing Composer's class composer install --optimize-autoloader --no-dev ``` -> {tip} In addition to optimizing the autoloader, you should always be sure to include a `composer.lock` file in your project's source control repository. Your project's dependencies can be installed much faster when a `composer.lock` file is present. +> **Note** +> In addition to optimizing the autoloader, you should always be sure to include a `composer.lock` file in your project's source control repository. Your project's dependencies can be installed much faster when a `composer.lock` file is present. ### Optimizing Configuration Loading @@ -110,7 +111,8 @@ php artisan config:cache This command will combine all of Laravel's configuration files into a single, cached file, which greatly reduces the number of trips the framework must make to the filesystem when loading your configuration values. -> {note} If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function for `.env` variables will return `null`. +> **Warning** +> If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function for `.env` variables will return `null`. ### Optimizing Route Loading diff --git a/dusk.md b/dusk.md index 4991483655a..69410f99d78 100644 --- a/dusk.md +++ b/dusk.md @@ -62,7 +62,8 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome composer require --dev laravel/dusk ``` -> {note} If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. +> **Warning** +> If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory and an example Dusk test: @@ -72,7 +73,8 @@ php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. -> {tip} If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). +> **Note** +> If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). ### Managing ChromeDriver Installations @@ -93,7 +95,8 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> {note} Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. +> **Warning** +> Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. ### Using Other Browsers @@ -158,7 +161,8 @@ Most of the tests you write will interact with pages that retrieve data from you use DatabaseMigrations; } -> {note} SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. +> **Warning** +> SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. ### Running Tests @@ -181,7 +185,8 @@ The `dusk` command accepts any argument that is normally accepted by the PHPUnit php artisan dusk --group=foo ``` -> {tip} If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). +> **Note** +> If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). #### Manually Starting ChromeDriver @@ -377,7 +382,8 @@ Often, you will be testing pages that require authentication. You can use Dusk's ->visit('/home'); }); -> {note} After using the `loginAs` method, the user session will be maintained for all tests within the file. +> **Warning** +> After using the `loginAs` method, the user session will be maintained for all tests within the file. ### Cookies @@ -567,7 +573,8 @@ The `attach` method may be used to attach a file to a `file` input element. Like $browser->attach('photo', __DIR__.'/photos/mountains.png'); -> {note} The attach function requires the `Zip` PHP extension to be installed and enabled on your server. +> **Warning** +> The attach function requires the `Zip` PHP extension to be installed and enabled on your server. ### Pressing Buttons @@ -597,7 +604,8 @@ You may use the `seeLink` method to determine if a link with the given display t // ... } -> {note} These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. +> **Warning** +> These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. ### Using The Keyboard @@ -610,7 +618,8 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" $browser->keys('.app', ['{command}', 'j']); -> {tip} All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). +> **Note** +> All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). ### Using The Mouse @@ -1821,7 +1830,8 @@ Once the component has been defined, we can easily select a date within the date ## Continuous Integration -> {note} Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. +> **Warning** +> Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. ### Heroku CI diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 85ea3df5d08..c7ad09b2d41 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -64,7 +64,8 @@ As you can see, the original value of the column is passed to the accessor, allo $firstName = $user->first_name; -> {tip} If you would like these computed values to be added to the array / JSON representations of your model, [you will need to append them](/docs/{{version}}/eloquent-serialization#appending-values-to-json). +> **Note** +> If you would like these computed values to be added to the array / JSON representations of your model, [you will need to append them](/docs/{{version}}/eloquent-serialization#appending-values-to-json). #### Building Value Objects From Multiple Attributes @@ -269,7 +270,8 @@ If you need to add a new, temporary cast at runtime, you may use the `mergeCasts 'options' => 'object', ]); -> {note} Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship. +> **Warning** +> Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship. #### Stringable Casting @@ -423,7 +425,8 @@ If a custom format is applied to the `date` or `datetime` cast, such as `datetim ### Enum Casting -> {note} Enum casting is only available for PHP 8.1+. +> **Warning** +> Enum casting is only available for PHP 8.1+. Eloquent also allows you to cast your attribute values to PHP ["backed" Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: @@ -613,7 +616,8 @@ When casting to value objects, any changes made to the value object will automat $user->save(); -> {tip} 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. +> **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. ### Array / JSON Serialization diff --git a/eloquent-relationships.md b/eloquent-relationships.md index a047a1d401f..2cf66037930 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -343,7 +343,8 @@ public function largestOrder() } ``` -> {note} Because PostgreSQL does not support executing the `MAX` function against UUID columns, it is not currently possible to use one-of-many relationships in combination with PostgreSQL UUID columns. +> **Warning** +> Because PostgreSQL does not support executing the `MAX` function against UUID columns, it is not currently possible to use one-of-many relationships in combination with PostgreSQL UUID columns. #### Advanced Has One Of Many Relationships @@ -609,7 +610,8 @@ If you would like your intermediate table to have `created_at` and `updated_at` return $this->belongsToMany(Role::class)->withTimestamps(); -> {note} Intermediate tables that utilize Eloquent's automatically maintained timestamps are required to have both `created_at` and `updated_at` timestamp columns. +> **Warning** +> Intermediate tables that utilize Eloquent's automatically maintained timestamps are required to have both `created_at` and `updated_at` timestamp columns. #### Customizing The `pivot` Attribute Name @@ -697,7 +699,8 @@ When defining the `RoleUser` model, you should extend the `Illuminate\Database\E // } -> {note} Pivot models may not use the `SoftDeletes` trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model. +> **Warning** +> Pivot models may not use the `SoftDeletes` trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model. #### Custom Pivot Models And Incrementing IDs @@ -950,7 +953,8 @@ public function bestImage() } ``` -> {tip} It is possible to construct more advanced "one of many" relationships. For more information, please consult the [has one of many documentation](#advanced-has-one-of-many-relationships). +> **Note** +> It is possible to construct more advanced "one of many" relationships. For more information, please consult the [has one of many documentation](#advanced-has-one-of-many-relationships). ### Many To Many (Polymorphic) @@ -977,7 +981,8 @@ Many-to-many polymorphic relations are slightly more complicated than "morph one taggable_id - integer taggable_type - string -> {tip} Before diving into polymorphic many-to-many relationships, you may benefit from reading the documentation on typical [many-to-many relationships](#many-to-many). +> **Note** +> Before diving into polymorphic many-to-many relationships, you may benefit from reading the documentation on typical [many-to-many relationships](#many-to-many). #### Model Structure @@ -1086,7 +1091,8 @@ You may determine the morph alias of a given model at runtime using the model's $class = Relation::getMorphedModel($alias); -> {note} When adding a "morph map" to your existing application, every morphable `*_type` column value in your database that still contains a fully-qualified class will need to be converted to its "map" name. +> **Warning** +> When adding a "morph map" to your existing application, every morphable `*_type` column value in your database that still contains a fully-qualified class will need to be converted to its "map" name. ### Dynamic Relationships @@ -1102,7 +1108,8 @@ The `resolveRelationUsing` method accepts the desired relationship name as its f return $orderModel->belongsTo(Customer::class, 'customer_id'); }); -> {note} When defining dynamic relationships, always provide explicit key name arguments to the Eloquent relationship methods. +> **Warning** +> When defining dynamic relationships, always provide explicit key name arguments to the Eloquent relationship methods. ## Querying Relations @@ -1224,7 +1231,8 @@ If you need even more power, you may use the `whereHas` and `orWhereHas` methods $query->where('content', 'like', 'code%'); }, '>=', 10)->get(); -> {note} Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. +> **Warning** +> Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. #### Inline Relationship Existence Queries @@ -1563,7 +1571,8 @@ You may not always need every column from the relationships you are retrieving. $books = Book::with('author:id,name,book_id')->get(); -> {note} When using this feature, you should always include the `id` column and any relevant foreign key columns in the list of columns you wish to retrieve. +> **Warning** +> When using this feature, you should always include the `id` column and any relevant foreign key columns in the list of columns you wish to retrieve. #### Eager Loading By Default @@ -1627,7 +1636,8 @@ In this example, Eloquent will only eager load posts where the post's `title` co $query->orderBy('created_at', 'desc'); }])->get(); -> {note} The `limit` and `take` query builder methods may not be used when constraining eager loads. +> **Warning** +> The `limit` and `take` query builder methods may not be used when constraining eager loads. #### Constraining Eager Loading Of `morphTo` Relationships @@ -1813,7 +1823,8 @@ You may use the `createMany` method to create multiple related models: You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods to [create and update models on relationships](/docs/{{version}}/eloquent#upserts). -> {tip} Before using the `create` method, be sure to review the [mass assignment](/docs/{{version}}/eloquent#mass-assignment) documentation. +> **Note** +> Before using the `create` method, be sure to review the [mass assignment](/docs/{{version}}/eloquent#mass-assignment) documentation. ### Belongs To Relationships @@ -1939,4 +1950,5 @@ For example, when a `Comment` model is updated, you may want to automatically "t } } -> {note} Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. +> **Warning** +> Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. diff --git a/eloquent-resources.md b/eloquent-resources.md index f6169796e03..7c681f6602d 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -44,7 +44,8 @@ php artisan make:resource UserCollection ## Concept Overview -> {tip} 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. +> **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: @@ -195,7 +196,8 @@ For example, `UserCollection` will attempt to map the given user instances into ## Writing Resources -> {tip} If you have not read the [concept overview](#concept-overview), you are highly encouraged to do so before proceeding with this documentation. +> **Note** +> If you have not read the [concept overview](#concept-overview), you are highly encouraged to do so before proceeding with this documentation. In essence, resources are simple. They only need to transform a given model into an array. So, each resource contains a `toArray` method which translates your model's attributes into an API friendly array that can be returned from your application's routes or controllers: @@ -259,7 +261,8 @@ If you would like to include related resources in your response, you may add the ]; } -> {tip} If you would like to include relationships only when they have already been loaded, check out the documentation on [conditional relationships](#conditional-relationships). +> **Note** +> If you would like to include relationships only when they have already been loaded, check out the documentation on [conditional relationships](#conditional-relationships). #### Resource Collections @@ -381,7 +384,8 @@ If you would like to disable the wrapping of the outermost resource, you should } } -> {note} The `withoutWrapping` method only affects the outermost response and will not remove `data` keys that you manually add to your own resource collections. +> **Warning** +> The `withoutWrapping` method only affects the outermost response and will not remove `data` keys that you manually add to your own resource collections. #### Wrapping Nested Resources @@ -556,7 +560,8 @@ Sometimes you may have several attributes that should only be included in the re Again, if the given condition is `false`, these attributes will be removed from the resource response before it is sent to the client. -> {note} The `mergeWhen` method should not be used within arrays that mix string and numeric keys. Furthermore, it should not be used within arrays with numeric keys that are not ordered sequentially. +> **Warning** +> The `mergeWhen` method should not be used within arrays that mix string and numeric keys. Furthermore, it should not be used within arrays with numeric keys that are not ordered sequentially. ### Conditional Relationships diff --git a/eloquent-serialization.md b/eloquent-serialization.md index e90918ae154..d1fabbef9d6 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -13,7 +13,8 @@ When building APIs using Laravel, you will often need to convert your models and relationships to arrays or JSON. Eloquent includes convenient methods for making these conversions, as well as controlling which attributes are included in the serialized representation of your models. -> {tip} For an even more robust way of handling Eloquent model and collection JSON serialization, check out the documentation on [Eloquent API resources](/docs/{{version}}/eloquent-resources). +> **Note** +> For an even more robust way of handling Eloquent model and collection JSON serialization, check out the documentation on [Eloquent API resources](/docs/{{version}}/eloquent-resources). ## Serializing Models & Collections @@ -90,7 +91,8 @@ Sometimes you may wish to limit the attributes, such as passwords, that are incl protected $hidden = ['password']; } -> {tip} To hide relationships, add the relationship's method name to your Eloquent model's `$hidden` property. +> **Note** +> To hide relationships, add the relationship's method name to your Eloquent model's `$hidden` property. Alternatively, you may use the `visible` property to define an "allow list" of attributes that should be included in your model's array and JSON representation. All attributes that are not present in the `$visible` array will be hidden when the model is converted to an array or JSON: diff --git a/eloquent.md b/eloquent.md index c841d9f65be..a1adf005df9 100644 --- a/eloquent.md +++ b/eloquent.md @@ -41,7 +41,8 @@ Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Eloquent, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well. -> {tip} Before getting started, be sure to configure a database connection in your application's `config/database.php` configuration file. For more information on configuring your database, check out [the database configuration documentation](/docs/{{version}}/database#configuration). +> **Note** +> Before getting started, be sure to configure a database connection in your application's `config/database.php` configuration file. For more information on configuring your database, check out [the database configuration documentation](/docs/{{version}}/database#configuration). ## Generating Model Classes @@ -306,7 +307,8 @@ The Eloquent `all` method will return all of the results in the model's table. H ->take(10) ->get(); -> {tip} Since Eloquent models are query builders, you should review all of the methods provided by Laravel's [query builder](/docs/{{version}}/queries). You may use any of these methods when writing your Eloquent queries. +> **Note** +> Since Eloquent models are query builders, you should review all of the methods provided by Laravel's [query builder](/docs/{{version}}/queries). You may use any of these methods when writing your Eloquent queries. #### Refreshing Models @@ -410,7 +412,8 @@ Similar to the `lazy` method, the `cursor` method may be used to significantly r The `cursor` method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor. -> {note} Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. +> **Warning** +> Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality: @@ -624,7 +627,8 @@ Updates can also be performed against models that match a given query. In this e The `update` method expects an array of column and value pairs representing the columns that should be updated. The `update` method returns the number of affected rows. -> {note} When issuing a mass update via Eloquent, the `saving`, `saved`, `updating`, and `updated` model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. +> **Warning** +> When issuing a mass update via Eloquent, the `saving`, `saved`, `updating`, and `updated` model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. #### Examining Attribute Changes @@ -775,7 +779,8 @@ If you would like to perform multiple "upserts" in a single query, then you shou ['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. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. +> **Warning** +> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. ## Deleting Models @@ -805,7 +810,8 @@ In the example above, we are retrieving the model from the database before calli Flight::destroy(collect([1, 2, 3])); -> {note} The `destroy` method loads each model individually and calls the `delete` method so that the `deleting` and `deleted` events are properly dispatched for each model. +> **Warning** +> The `destroy` method loads each model individually and calls the `delete` method so that the `deleting` and `deleted` events are properly dispatched for each model. #### Deleting Models Using Queries @@ -814,7 +820,8 @@ Of course, you may build an Eloquent query to delete all models matching your qu $deleted = Flight::where('active', 0)->delete(); -> {note} When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement. +> **Warning** +> When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement. ### Soft Deleting @@ -833,7 +840,8 @@ In addition to actually removing records from your database, Eloquent can also " use SoftDeletes; } -> {tip} The `SoftDeletes` trait will automatically cast the `deleted_at` attribute to a `DateTime` / `Carbon` instance for you. +> **Note** +> The `SoftDeletes` trait will automatically cast the `deleted_at` attribute to a `DateTime` / `Carbon` instance for you. You should also add the `deleted_at` column to your database table. The Laravel [schema builder](/docs/{{version}}/migrations) contains a helper method to create this column: @@ -981,7 +989,8 @@ You may test your `prunable` query by executing the `model:prune` command with t php artisan model:prune --pretend ``` -> {note} Soft deleting models will be permanently deleted (`forceDelete`) if they match the prunable query. +> **Warning** +> Soft deleting models will be permanently deleted (`forceDelete`) if they match the prunable query. #### Mass Pruning @@ -1083,7 +1092,8 @@ The `Scope` interface requires you to implement one method: `apply`. The `apply` } } -> {tip} If your global scope is adding columns to the select clause of the query, you should use the `addSelect` method instead of `select`. This will prevent the unintentional replacement of the query's existing select clause. +> **Note** +> If your global scope is adding columns to the select clause of the query, you should use the `addSelect` method instead of `select`. This will prevent the unintentional replacement of the query's existing select clause. #### Applying Global Scopes @@ -1273,7 +1283,8 @@ The `is` and `isNot` methods are also available when using the `belongsTo`, `has ## Events -> {tip} Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). +> **Note** +> Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `trashed`, `forceDeleted`, `restoring`, `restored`, and `replicating`. @@ -1306,7 +1317,8 @@ To start listening to model events, define a `$dispatchesEvents` property on you After defining and mapping your Eloquent events, you may use [event listeners](/docs/{{version}}/events#defining-listeners) to handle the events. -> {note} When issuing a mass update or delete query via Eloquent, the `saved`, `updated`, `deleting`, and `deleted` model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing mass updates or deletes. +> **Warning** +> When issuing a mass update or delete query via Eloquent, the `saved`, `updated`, `deleting`, and `deleted` model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing mass updates or deletes. ### Using Closures @@ -1449,7 +1461,8 @@ Alternatively, you may list your observers within an `$observers` property of yo User::class => [UserObserver::class], ]; -> {tip} There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. +> **Note** +> There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. #### Observers & Database Transactions diff --git a/errors.md b/errors.md index e9a5ea44d57..60690789777 100644 --- a/errors.md +++ b/errors.md @@ -57,7 +57,8 @@ When you register a custom exception reporting callback using the `reportable` m return false; }); -> {tip} To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). +> **Note** +> To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). #### Global Log Context @@ -155,7 +156,8 @@ When building your application, there will be some types of exceptions you simpl InvalidOrderException::class, ]; -> {tip} Behind the scenes, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP "not found" errors or 419 HTTP responses generated by invalid CSRF tokens. +> **Note** +> Behind the scenes, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP "not found" errors or 419 HTTP responses generated by invalid CSRF tokens. ### Rendering Exceptions @@ -262,7 +264,8 @@ If your exception contains custom reporting logic that is only necessary when ce return false; } -> {tip} You may type-hint any required dependencies of the `report` method and they will automatically be injected into the method by Laravel's [service container](/docs/{{version}}/container). +> **Note** +> You may type-hint any required dependencies of the `report` method and they will automatically be injected into the method by Laravel's [service container](/docs/{{version}}/container). ## HTTP Exceptions diff --git a/events.md b/events.md index 4daf335edfa..10faa6c872a 100644 --- a/events.md +++ b/events.md @@ -42,7 +42,8 @@ The `App\Providers\EventServiceProvider` included with your Laravel application ], ]; -> {tip} The `event:list` command may be used to display a list of all events and listeners registered by your application. +> **Note** +> The `event:list` command may be used to display a list of all events and listeners registered by your application. ### Generating Events & Listeners @@ -264,7 +265,8 @@ Next, let's take a look at the listener for our example event. Event listeners r } } -> {tip} Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. +> **Note** +> Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. #### Stopping The Propagation Of An Event @@ -439,7 +441,8 @@ If your queue connection's `after_commit` configuration option is set to `false` public $afterCommit = true; } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). ### Handling Failed Jobs @@ -559,7 +562,8 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th OrderShipped::dispatchUnless($condition, $order); -> {tip} When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](/docs/{{version}}/mocking#event-fake) makes it a cinch. +> **Note** +> When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](/docs/{{version}}/mocking#event-fake) makes it a cinch. ## Event Subscribers diff --git a/filesystem.md b/filesystem.md index 6a8f2ca9e5e..aa58d77ba52 100644 --- a/filesystem.md +++ b/filesystem.md @@ -34,7 +34,8 @@ Laravel's filesystem configuration file is located at `config/filesystems.php`. The `local` driver interacts with files stored locally on the server running the Laravel application while the `s3` driver is used to write to Amazon's S3 cloud storage service. -> {tip} You may configure as many disks as you like and may even have multiple disks that use the same driver. +> **Note** +> You may configure as many disks as you like and may even have multiple disks that use the same driver. ### The Local Driver @@ -218,7 +219,8 @@ You may use the `url` method to get the URL for a given file. If you are using t When using the `local` driver, all files that should be publicly accessible should be placed in the `storage/app/public` directory. Furthermore, you should [create a symbolic link](#the-public-disk) at `public/storage` which points to the `storage/app/public` directory. -> {note} When using the `local` driver, the return value of `url` is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs. +> **Warning** +> When using the `local` driver, the return value of `url` is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs. #### Temporary URLs @@ -420,7 +422,8 @@ You may also use the `putFileAs` method on the `Storage` facade, which will perf 'avatars', $request->file('avatar'), $request->user()->id ); -> {note} Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. +> **Warning** +> Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. #### Specifying A Disk diff --git a/fortify.md b/fortify.md index 8c17a250d5e..2cc45d0250e 100644 --- a/fortify.md +++ b/fortify.md @@ -32,7 +32,8 @@ Since Fortify does not provide its own user interface, it is meant to be paired with your own user interface which makes requests to the routes it registers. We will discuss exactly how to make requests to these routes in the remainder of this documentation. -> {tip} Remember, Fortify is a package that is meant to give you a head start implementing Laravel's authentication features. **You are not required to use it.** You are always free to manually interact with Laravel's authentication services by following the documentation available in the [authentication](/docs/{{version}}/authentication), [password reset](/docs/{{version}}/passwords), and [email verification](/docs/{{version}}/verification) documentation. +> **Note** +> Remember, Fortify is a package that is meant to give you a head start implementing Laravel's authentication features. **You are not required to use it.** You are always free to manually interact with Laravel's authentication services by following the documentation available in the [authentication](/docs/{{version}}/authentication), [password reset](/docs/{{version}}/passwords), and [email verification](/docs/{{version}}/verification) documentation. ### What Is Fortify? diff --git a/frontend.md b/frontend.md index c79a847fe83..50174564a90 100644 --- a/frontend.md +++ b/frontend.md @@ -193,4 +193,5 @@ By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. V The fastest way to get started with Laravel and Vite is by beginning your application's development using [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), our simplest starter kit that jump-starts your application by providing frontend and backend authentication scaffolding. -> {tip} For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). +> **Note** +> For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). diff --git a/hashing.md b/hashing.md index 414362496d8..d3f278d254f 100644 --- a/hashing.md +++ b/hashing.md @@ -73,7 +73,8 @@ If you are using the Argon2 algorithm, the `make` method allows you to manage th 'threads' => 2, ]); -> {tip} For more information on these options, please refer to the [official PHP documentation regarding Argon hashing](https://secure.php.net/manual/en/function.password-hash.php). +> **Note** +> For more information on these options, please refer to the [official PHP documentation regarding Argon hashing](https://secure.php.net/manual/en/function.password-hash.php). ### Verifying That A Password Matches A Hash diff --git a/helpers.md b/helpers.md index b9001069b8a..516c8de92c5 100644 --- a/helpers.md +++ b/helpers.md @@ -3542,7 +3542,8 @@ The `env` function retrieves the value of an [environment variable](/docs/{{vers $env = env('APP_ENV', 'production'); -> {note} If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. +> **Warning** +> If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. #### `event()` {.collection-method} diff --git a/homestead.md b/homestead.md index b9947fa4a38..4e746249e47 100644 --- a/homestead.md +++ b/homestead.md @@ -42,7 +42,8 @@ Laravel strives to make the entire PHP development experience delightful, includ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, MySQL, PostgreSQL, Redis, Memcached, Node, and all of the other software you need to develop amazing Laravel applications. -> {note} If you are using Windows, you may need to enable hardware virtualization (VT-x). It can usually be enabled via your BIOS. If you are using Hyper-V on a UEFI system you may additionally need to disable Hyper-V in order to access VT-x. +> **Warning** +> If you are using Windows, you may need to enable hardware virtualization (VT-x). It can usually be enabled via your BIOS. If you are using Hyper-V on a UEFI system you may additionally need to disable Hyper-V in order to access VT-x. ### Included Software @@ -183,7 +184,8 @@ The `provider` key in your `Homestead.yaml` file indicates which Vagrant provide provider: virtualbox -> {note} If you are using Apple Silicon, you should add `box: laravel/homestead-arm` to your `Homestead.yaml` file. Apple Silicon requires the Parallels provider. +> **Warning** +> If you are using Apple Silicon, you should add `box: laravel/homestead-arm` to your `Homestead.yaml` file. Apple Silicon requires the Parallels provider. #### Configuring Shared Folders @@ -196,7 +198,8 @@ folders: to: /home/vagrant/project1 ``` -> {note} Windows users should not use the `~/` path syntax and instead should use the full path to their project, such as `C:\Users\user\Code\project1`. +> **Warning** +> Windows users should not use the `~/` path syntax and instead should use the full path to their project, such as `C:\Users\user\Code\project1`. You should always map individual applications to their own folder mapping instead of mapping a single large directory that contains all of your applications. When you map a folder, the virtual machine must keep track of all disk IO for *every* file in the folder. You may experience reduced performance if you have a large number of files in a folder: @@ -208,7 +211,8 @@ folders: to: /home/vagrant/project2 ``` -> {note} You should never mount `.` (the current directory) when using Homestead. This causes Vagrant to not map the current folder to `/vagrant` and will break optional features and cause unexpected results while provisioning. +> **Warning** +> You should never mount `.` (the current directory) when using Homestead. This causes Vagrant to not map the current folder to `/vagrant` and will break optional features and cause unexpected results while provisioning. To enable [NFS](https://www.vagrantup.com/docs/synced-folders/nfs.html), you may add a `type` option to your folder mapping: @@ -219,7 +223,8 @@ folders: type: "nfs" ``` -> {note} When using NFS on Windows, you should consider installing the [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd) plug-in. This plug-in will maintain the correct user / group permissions for files and directories within the Homestead virtual machine. +> **Warning** +> When using NFS on Windows, you should consider installing the [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd) plug-in. This plug-in will maintain the correct user / group permissions for files and directories within the Homestead virtual machine. You may also pass any options supported by Vagrant's [Synced Folders](https://www.vagrantup.com/docs/synced-folders/basic_usage.html) by listing them under the `options` key: @@ -246,7 +251,8 @@ sites: If you change the `sites` property after provisioning the Homestead virtual machine, you should execute the `vagrant reload --provision` command in your terminal to update the Nginx configuration on the virtual machine. -> {note} Homestead scripts are built to be as idempotent as possible. However, if you are experiencing issues while provisioning you should destroy and rebuild the machine by executing the `vagrant destroy && vagrant up` command. +> **Warning** +> Homestead scripts are built to be as idempotent as possible. However, if you are experiencing issues while provisioning you should destroy and rebuild the machine by executing the `vagrant destroy && vagrant up` command. #### Hostname Resolution @@ -356,7 +362,8 @@ features: You may specify a supported version of Elasticsearch, which must be an exact version number (major.minor.patch). The default installation will create a cluster named 'homestead'. You should never give Elasticsearch more than half of the operating system's memory, so make sure your Homestead virtual machine has at least twice the Elasticsearch allocation. -> {tip} Check out the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current) to learn how to customize your configuration. +> **Note** +> Check out the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current) to learn how to customize your configuration. #### MariaDB @@ -453,7 +460,8 @@ sites: to: /home/vagrant/project2/public ``` -> {note} You should ensure that you have configured a [folder mapping](#configuring-shared-folders) for the project's directory before adding the site. +> **Warning** +> You should ensure that you have configured a [folder mapping](#configuring-shared-folders) for the project's directory before adding the site. If Vagrant is not automatically managing your "hosts" file, you may need to add the new site to that file as well. On macOS and Linux, this file is located at `/etc/hosts`. On Windows, it is located at `C:\Windows\System32\drivers\etc\hosts`: @@ -588,7 +596,8 @@ php81 A `homestead` database is configured for both MySQL and PostgreSQL out of the box. To connect to your MySQL or PostgreSQL database from your host machine's database client, you should connect to `127.0.0.1` on port `33060` (MySQL) or `54320` (PostgreSQL). The username and password for both databases is `homestead` / `secret`. -> {note} You should only use these non-standard ports when connecting to the databases from your host machine. You will use the default 3306 and 5432 ports in your Laravel application's `database` configuration file since Laravel is running _within_ the virtual machine. +> **Warning** +> You should only use these non-standard ports when connecting to the databases from your host machine. You will use the default 3306 and 5432 ports in your Laravel application's `database` configuration file since Laravel is running _within_ the virtual machine. ### Database Backups @@ -702,7 +711,8 @@ After running the command, you will see an Ngrok screen appear which contains th share homestead.test -region=eu -subdomain=laravel ``` -> {note} Remember, Vagrant is inherently insecure and you are exposing your virtual machine to the Internet when running the `share` command. +> **Warning** +> Remember, Vagrant is inherently insecure and you are exposing your virtual machine to the Internet when running the `share` command. ## Debugging & Profiling @@ -714,7 +724,8 @@ Homestead includes support for step debugging using [Xdebug](https://xdebug.org) By default, Xdebug is already running and ready to accept connections. If you need to enable Xdebug on the CLI, execute the `sudo phpenmod xdebug` command within your Homestead virtual machine. Next, follow your IDE's instructions to enable debugging. Finally, configure your browser to trigger Xdebug with an extension or [bookmarklet](https://www.jetbrains.com/phpstorm/marklets/). -> {note} Xdebug causes PHP to run significantly slower. To disable Xdebug, run `sudo phpdismod xdebug` within your Homestead virtual machine and restart the FPM service. +> **Warning** +> Xdebug causes PHP to run significantly slower. To disable Xdebug, run `sudo phpdismod xdebug` within your Homestead virtual machine and restart the FPM service. #### Autostarting Xdebug diff --git a/horizon.md b/horizon.md index e71d1a4a7ef..b4a59566530 100644 --- a/horizon.md +++ b/horizon.md @@ -17,7 +17,8 @@ ## Introduction -> {tip} Before digging into Laravel Horizon, you should familiarize yourself with Laravel's base [queue services](/docs/{{version}}/queues). Horizon augments Laravel's queue with additional features that may be confusing if you are not already familiar with the basic queue features offered by Laravel. +> **Note** +> Before digging into Laravel Horizon, you should familiarize yourself with Laravel's base [queue services](/docs/{{version}}/queues). Horizon augments Laravel's queue with additional features that may be confusing if you are not already familiar with the basic queue features offered by Laravel. [Laravel Horizon](https://github.com/laravel/horizon) provides a beautiful dashboard and code-driven configuration for your Laravel powered [Redis queues](/docs/{{version}}/queues). Horizon allows you to easily monitor key metrics of your queue system such as job throughput, runtime, and job failures. @@ -28,7 +29,8 @@ When using Horizon, all of your queue worker configuration is stored in a single ## Installation -> {note} 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. +> **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: @@ -47,7 +49,8 @@ php artisan horizon:install After publishing Horizon's assets, its primary configuration file will be located at `config/horizon.php`. This configuration file allows you to configure the queue worker options for your application. Each configuration option includes a description of its purpose, so be sure to thoroughly explore this file. -> {note} Horizon uses a Redis connection named `horizon` internally. This Redis connection name is reserved and should not be assigned to another Redis connection in the `database.php` configuration file or as the value of the `use` option in the `horizon.php` configuration file. +> **Warning** +> Horizon uses a Redis connection named `horizon` internally. This Redis connection name is reserved and should not be assigned to another Redis connection in the `database.php` configuration file or as the value of the `use` option in the `horizon.php` configuration file. #### Environments @@ -72,7 +75,8 @@ After installation, the primary Horizon configuration option that you should fam When you start Horizon, it will use the worker process configuration options for the environment that your application is running on. Typically, the environment is determined by the value of the `APP_ENV` [environment variable](/docs/{{version}}/configuration#determining-the-current-environment). For example, the default `local` Horizon environment is configured to start three worker processes and automatically balance the number of worker processes assigned to each queue. The default `production` environment is configured to start a maximum of 10 worker processes and automatically balance the number of worker processes assigned to each queue. -> {note} You should ensure that the `environments` portion of your `horizon` configuration file contains an entry for each [environment](/docs/{{version}}/configuration#environment-configuration) on which you plan to run Horizon. +> **Warning** +> You should ensure that the `environments` portion of your `horizon` configuration file contains an entry for each [environment](/docs/{{version}}/configuration#environment-configuration) on which you plan to run Horizon. #### Supervisors @@ -220,7 +224,8 @@ Supervisor is a process monitor for the Linux operating system and will automati 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. +> **Note** +> 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. #### Supervisor Configuration @@ -241,7 +246,8 @@ stopwaitsecs=3600 When defining your Supervisor configuration, 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. -> {note} While the examples above are valid for Ubuntu based servers, the location and file extension expected of Supervisor configuration files may vary between other server operating systems. Please consult your server's documentation for more information. +> **Warning** +> While the examples above are valid for Ubuntu based servers, the location and file extension expected of Supervisor configuration files may vary between other server operating systems. Please consult your server's documentation for more information. #### Starting Supervisor @@ -256,7 +262,8 @@ sudo supervisorctl update sudo supervisorctl start horizon ``` -> {tip} For more information on running Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html). +> **Note** +> For more information on running Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html). ## Tags @@ -337,7 +344,8 @@ If you would like to manually define the tags for one of your queueable objects, ## Notifications -> {note} When configuring Horizon to send Slack or SMS notifications, you should review the [prerequisites for the relevant notification channel](/docs/{{version}}/notifications). +> **Warning** +> When configuring Horizon to send Slack or SMS notifications, you should review the [prerequisites for the relevant notification channel](/docs/{{version}}/notifications). If you would like to be notified when one of your queues has a long wait time, you may use the `Horizon::routeMailNotificationsTo`, `Horizon::routeSlackNotificationsTo`, and `Horizon::routeSmsNotificationsTo` methods. You may call these methods from the `boot` method of your application's `App\Providers\HorizonServiceProvider`: diff --git a/http-client.md b/http-client.md index 31e64d186c5..3937c6f30b9 100644 --- a/http-client.md +++ b/http-client.md @@ -203,7 +203,8 @@ If all of the requests fail, an instance of `Illuminate\Http\Client\RequestExcep $response = Http::retry(3, 100, throw: false)->post(/* ... */); -> {note} If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. +> **Warning** +> If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. ### Error Handling diff --git a/http-tests.md b/http-tests.md index 529fe359caa..e4d538c109e 100644 --- a/http-tests.md +++ b/http-tests.md @@ -78,7 +78,8 @@ Instead of returning an `Illuminate\Http\Response` instance, test request method In general, each of your tests should only make one request to your application. Unexpected behavior may occur if multiple requests are executed within a single test method. -> {tip} For convenience, the CSRF middleware is automatically disabled when running tests. +> **Note** +> For convenience, the CSRF middleware is automatically disabled when running tests. ### Customizing Request Headers @@ -278,7 +279,8 @@ In addition, JSON response data may be accessed as array variables on the respon $this->assertTrue($response['created']); -> {tip} The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. +> **Note** +> The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. #### Asserting Exact JSON Matches @@ -814,7 +816,8 @@ Assert that the response has no JSON validation errors for the given keys: $response->assertJsonMissingValidationErrors($keys); -> {tip} The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. +> **Note** +> The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. #### assertJsonPath @@ -921,7 +924,8 @@ Assert that the response has the given JSON validation errors for the given keys $response->assertJsonValidationErrors(array $data, $responseKey = 'errors'); -> {tip} The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. +> **Note** +> The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. #### assertJsonValidationErrorFor @@ -1073,7 +1077,8 @@ Or, you may assert that a given field has a particular validation error message: 'name' => 'The given name was invalid.' ]); -> {tip} The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. +> **Note** +> The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. #### assertSessionHasErrorsIn @@ -1096,7 +1101,8 @@ Assert that the session has no validation errors for the given keys: $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); -> {tip} The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. +> **Note** +> The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. #### assertSessionMissing diff --git a/installation.md b/installation.md index aee003d0e5b..f6f712f6d5a 100644 --- a/installation.md +++ b/installation.md @@ -67,7 +67,8 @@ php artisan serve Once you have started the Artisan development server, your application will be accessible in your web browser at `http://localhost:8000`. Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). -> {tip} If you would like a head start when developing your Laravel application, 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. +> **Note** +> If you would like a head start when developing your Laravel application, 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. ## Laravel & Docker @@ -78,7 +79,8 @@ Docker is a tool for running applications and services in small, light-weight "c Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. -> {tip} Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. +> **Note** +> Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. ### Getting Started On macOS @@ -103,14 +105,16 @@ The first time you run the Sail `up` command, Sail's application containers will Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> {tip} To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). +> **Note** +> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). ### Getting Started On Windows Before we create a new Laravel application on your Windows machine, make sure to install [Docker Desktop](https://www.docker.com/products/docker-desktop). Next, you should ensure that Windows Subsystem for Linux 2 (WSL2) is installed and enabled. WSL allows you to run Linux binary executables natively on Windows 10. Information on how to install and enable WSL2 can be found within Microsoft's [developer environment documentation](https://docs.microsoft.com/en-us/windows/wsl/install-win10). -> {tip} After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). +> **Note** +> After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). Next, you are ready to create your first Laravel project. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: @@ -132,7 +136,8 @@ The first time you run the Sail `up` command, Sail's application containers will Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> {tip} To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). +> **Note** +> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). #### Developing Within WSL2 @@ -163,7 +168,8 @@ The first time you run the Sail `up` command, Sail's application containers will Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> {tip} To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). +> **Note** +> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). ### Choosing Your Sail Services @@ -196,7 +202,8 @@ Since many of Laravel's configuration option values may vary depending on whethe Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. -> {tip} For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). +> **Note** +> For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). ### Directory Configuration @@ -259,7 +266,8 @@ If this is how you plan to use Laravel, you may want to check out our documentat If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Vite](/docs/{{version}}/vite). -> {tip} If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). +> **Note** +> If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). ### Laravel The API Backend @@ -268,4 +276,5 @@ Laravel may also serve as an API backend to a JavaScript single-page application If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [Laravel Sanctum](/docs/{{version}}/sanctum), and the [Eloquent ORM](/docs/{{version}}/eloquent). -> {tip} Need a head start scaffolding your Laravel backend and Next.js frontend? Laravel Breeze offers an [API stack](/docs/{{version}}/starter-kits#breeze-and-next) as well as a [Next.js frontend implementation](https://github.com/laravel/breeze-next) so you can get started in minutes. +> **Note** +> Need a head start scaffolding your Laravel backend and Next.js frontend? Laravel Breeze offers an [API stack](/docs/{{version}}/starter-kits#breeze-and-next) as well as a [Next.js frontend implementation](https://github.com/laravel/breeze-next) so you can get started in minutes. diff --git a/localization.md b/localization.md index a934480d061..2b40df70e86 100644 --- a/localization.md +++ b/localization.md @@ -87,7 +87,8 @@ You may instruct Laravel's "pluralizer", which is used by Eloquent and other por // ... } -> {note} If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). +> **Warning** +> If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). ## Defining Translation Strings @@ -113,7 +114,8 @@ All language files return an array of keyed strings. For example: 'welcome' => 'Welcome to our application!', ]; -> {note} For languages that differ by territory, you should name the language directories according to the ISO 15897. For example, "en_GB" should be used for British English rather than "en-gb". +> **Warning** +> For languages that differ by territory, you should name the language directories according to the ISO 15897. For example, "en_GB" should be used for British English rather than "en-gb". ### Using Translation Strings As Keys diff --git a/logging.md b/logging.md index e7b905a9d34..691deb6ed87 100644 --- a/logging.md +++ b/logging.md @@ -59,7 +59,8 @@ Name | Description `stack` | A wrapper to facilitate creating "multi-channel" channels `syslog` | A `SyslogHandler` based Monolog driver -> {tip} Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. +> **Note** +> Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. ### Channel Prerequisites @@ -336,7 +337,8 @@ Once you have configured the `tap` option on your channel, you're ready to defin } } -> {tip} All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected. +> **Note** +> All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected. ### Creating Monolog Handler Channels diff --git a/mail.md b/mail.md index 104381aec2f..42afed52d6f 100644 --- a/mail.md +++ b/mail.md @@ -172,7 +172,8 @@ php artisan make:mail OrderShipped Once you have generated a mailable class, open it up so we can explore its contents. First, note that all of a mailable class' configuration is done in the `build` method. Within this method, you may call various methods such as `from`, `subject`, `view`, and `attach` to configure the email's presentation and delivery. -> {tip} You may type-hint dependencies on the mailable's `build` method. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies. +> **Note** +> You may type-hint dependencies on the mailable's `build` method. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies. ### Configuring The Sender @@ -219,7 +220,8 @@ Within a mailable class' `build` method, you may use the `view` method to specif return $this->view('emails.orders.shipped'); } -> {tip} You may wish to create a `resources/views/emails` directory to house all of your email templates; however, you are free to place them wherever you wish within your `resources/views` directory. +> **Note** +> You may wish to create a `resources/views/emails` directory to house all of your email templates; however, you are free to place them wherever you wish within your `resources/views` directory. #### Plain Text Emails @@ -457,7 +459,8 @@ Embedding inline images into your emails is typically cumbersome; however, Larav ``` -> {note} The `$message` variable is not available in plain-text message templates since plain-text messages do not utilize inline attachments. +> **Warning** +> The `$message` variable is not available in plain-text message templates since plain-text messages do not utilize inline attachments. #### Embedding Raw Data Attachments @@ -626,7 +629,8 @@ Thanks,
@endcomponent ``` -> {tip} Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. +> **Note** +> Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. #### Button Component @@ -830,7 +834,8 @@ Alternatively, you may call the `afterCommit` method from your mailable's constr } } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). ## Rendering Mailables @@ -855,7 +860,8 @@ When designing a mailable's template, it is convenient to quickly preview the re return new App\Mail\InvoicePaid($invoice); }); -> {note} [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog) or [HELO](https://usehelo.com). +> **Warning** +> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog) or [HELO](https://usehelo.com). ## Localizing Mailables diff --git a/middleware.md b/middleware.md index e911ce65e90..aa66de2de4f 100644 --- a/middleware.md +++ b/middleware.md @@ -57,7 +57,8 @@ As you can see, if the given `token` does not match our secret token, the middle It's best to envision middleware as a series of "layers" HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely. -> {tip} All middleware are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a middleware's constructor. +> **Note** +> All middleware are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a middleware's constructor. @@ -215,7 +216,8 @@ Middleware groups may be assigned to routes and controller actions using the sam // }); -> {tip} Out of the box, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `App\Providers\RouteServiceProvider`. +> **Note** +> Out of the box, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `App\Providers\RouteServiceProvider`. ### Sorting Middleware diff --git a/migrations.md b/migrations.md index fad9ec83ee3..8120e92fc27 100644 --- a/migrations.md +++ b/migrations.md @@ -43,7 +43,8 @@ Laravel will use the name of the migration to attempt to guess the name of the t If you would like to specify a custom path for the generated migration, you may use the `--path` option when executing the `make:migration` command. The given path should be relative to your application's base path. -> {tip} Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). ### Squashing Migrations @@ -61,7 +62,8 @@ When you execute this command, Laravel will write a "schema" file to your applic You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure. -> {note} Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. Schema dumps may not be restored to in-memory SQLite databases. +> **Warning** +> Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. Schema dumps may not be restored to in-memory SQLite databases. ## Migration Structure @@ -206,7 +208,8 @@ php artisan migrate:fresh php artisan migrate:fresh --seed ``` -> {note} The `migrate:fresh` command will drop all database tables regardless of their prefix. This command should be used with caution when developing on a database that is shared with other applications. +> **Warning** +> The `migrate:fresh` command will drop all database tables regardless of their prefix. This command should be used with caution when developing on a database that is shared with other applications. ## Tables @@ -941,7 +944,8 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi } }; -> {note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. In addition, it is not possible to combine raw `default` expressions (using `DB::raw`) with column changes via the `change` method. +> **Warning** +> Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. In addition, it is not possible to combine raw `default` expressions (using `DB::raw`) with column changes via the `change` method. #### Column Order @@ -976,7 +980,8 @@ use Illuminate\Database\DBAL\TimestampType; ], ``` -> {note} If your application is using Microsoft SQL Server, please ensure that you install `doctrine/dbal:^3.0`. +> **Warning** +> If your application is using Microsoft SQL Server, please ensure that you install `doctrine/dbal:^3.0`. #### Updating Column Attributes @@ -993,7 +998,8 @@ We could also modify a column to be nullable: $table->string('name', 50)->nullable()->change(); }); -> {note} The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). +> **Warning** +> The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). #### Renaming Columns @@ -1004,7 +1010,8 @@ To rename a column, you may use the `renameColumn` method provided by the schema $table->renameColumn('from', 'to'); }); -> {note} Renaming an `enum` column is not currently supported. +> **Warning** +> Renaming an `enum` column is not currently supported. ### Dropping Columns @@ -1021,7 +1028,8 @@ You may drop multiple columns from a table by passing an array of column names t $table->dropColumn(['votes', 'avatar', 'location']); }); -> {note} Dropping or modifying multiple columns within a single migration while using an SQLite database is not supported. +> **Warning** +> Dropping or modifying multiple columns within a single migration while using an SQLite database is not supported. #### Available Command Aliases @@ -1193,7 +1201,8 @@ You may enable or disable foreign key constraints within your migrations by usin Schema::disableForeignKeyConstraints(); -> {note} SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. In addition, SQLite only supports foreign keys upon creation of the table and [not when tables are altered](https://www.sqlite.org/omitted.html). +> **Warning** +> SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. In addition, SQLite only supports foreign keys upon creation of the table and [not when tables are altered](https://www.sqlite.org/omitted.html). ## Events @@ -1208,4 +1217,3 @@ For convenience, each migration operation will dispatch an [event](/docs/{{versi | `Illuminate\Database\Events\MigrationEnded` | A single migration has finished executing. | | `Illuminate\Database\Events\SchemaDumped` | A database schema dump has completed. | | `Illuminate\Database\Events\SchemaLoaded` | An existing database schema dump has loaded. | - diff --git a/mix.md b/mix.md index b7b1f9563b4..803a3181ceb 100644 --- a/mix.md +++ b/mix.md @@ -16,4 +16,5 @@ mix.js('resources/js/app.js', 'public/js') 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} Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [official Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [Vite migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). +> **Note** +> Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [official Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [Vite migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). diff --git a/mocking.md b/mocking.md index 8e316d8c8fd..b03c6bf4e8f 100644 --- a/mocking.md +++ b/mocking.md @@ -123,7 +123,8 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, } } -> {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. +> **Warning** +> 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. ### Facade Spies @@ -288,7 +289,8 @@ If you would simply like to assert that an event listener is listening to a give SendShipmentNotification::class ); -> {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. +> **Warning** +> 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. #### Faking A Subset Of Events @@ -618,7 +620,8 @@ The `Storage` facade's `fake` method allows you to easily generate a fake disk t 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. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). -> {note} The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). +> **Warning** +> The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). ## Interacting With Time diff --git a/notifications.md b/notifications.md index 5e176d03fd9..ef6c4a774ec 100644 --- a/notifications.md +++ b/notifications.md @@ -93,7 +93,8 @@ The `notify` method that is provided by this trait expects to receive a notifica $user->notify(new InvoicePaid($invoice)); -> {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. +> **Note** +> 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 @@ -113,7 +114,8 @@ You can also send notifications immediately using the `sendNow` method. This met Every notification class has a `via` method that determines on which channels the notification will be delivered. Notifications may be sent on the `mail`, `database`, `broadcast`, `vonage`, and `slack` channels. -> {tip} If you would like to use other delivery channels such as Telegram or Pusher, check out the community driven [Laravel Notification Channels website](http://laravel-notification-channels.com). +> **Note** +> If you would like to use other delivery channels such as Telegram or Pusher, check out the community driven [Laravel Notification Channels website](http://laravel-notification-channels.com). The `via` method receives a `$notifiable` instance, which will be an instance of the class to which the notification is being sent. You may use `$notifiable` to determine which channels the notification should be delivered on: @@ -131,7 +133,8 @@ The `via` method receives a `$notifiable` instance, which will be an instance of ### Queueing Notifications -> {note} Before queueing notifications you should configure your queue and [start a worker](/docs/{{version}}/queues). +> **Warning** +> 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 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: @@ -257,7 +260,8 @@ Alternatively, you may call the `afterCommit` method from your notification's co } } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). #### Determining If A Queued Notification Should Be Sent @@ -322,13 +326,15 @@ The `MailMessage` class contains a few simple methods to help you build transact ->line('Thank you for using our application!'); } -> {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. +> **Note** +> 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 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` configuration option in your `config/app.php` configuration file. This value will be used in the header and footer of your mail notification messages. +> **Note** +> 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 Mail Notification Formatting Options @@ -496,7 +502,8 @@ To add attachments to an email notification, use the `attach` method while build ->attach('/path/to/file'); } -> {tip} The `attach` method offered by notification mail messages also accepts [attachable objects](/docs/{{version}}/mail#attachable-objects). Please consult the comprehensive [attachable object documentation](/docs/{{version}}/mail#attachable-objects) to learn more. +> **Note** +> The `attach` method offered by notification mail messages also accepts [attachable objects](/docs/{{version}}/mail#attachable-objects). Please consult the comprehensive [attachable object documentation](/docs/{{version}}/mail#attachable-objects) to learn more. When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: @@ -859,7 +866,8 @@ 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 URL from your JavaScript client. +> **Note** +> 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 @@ -1332,7 +1340,8 @@ When a notification is sent, the `Illuminate\Notifications\Events\NotificationSe ], ]; -> {tip} After registering listeners in your `EventServiceProvider`, use the `event:generate` Artisan command to quickly generate listener classes. +> **Note** +> After registering listeners in your `EventServiceProvider`, use the `event:generate` Artisan command to quickly generate listener classes. Within an event listener, you may access the `notifiable`, `notification`, `channel`, and `response` properties on the event to learn more about the notification recipient or the notification itself: diff --git a/octane.md b/octane.md index 9e31fa73352..6a084e86106 100644 --- a/octane.md +++ b/octane.md @@ -46,7 +46,8 @@ php artisan octane:install ## Server Prerequisites -> {note} Laravel Octane requires [PHP 8.0+](https://php.net/releases/). +> **Warning** +> Laravel Octane requires [PHP 8.0+](https://php.net/releases/). ### RoadRunner @@ -105,7 +106,8 @@ pecl install swoole #### Swoole Via Laravel Sail -> {note} Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. +> **Warning** +> Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. Alternatively, you may develop your Swoole based Octane application using [Laravel Sail](/docs/{{version}}/sail), the official Docker based development environment for Laravel. Laravel Sail includes the Swoole extension by default. However, you will still need to adjust the `supervisor.conf` file used by Sail to keep your application running. To get started, execute the `sail:publish` Artisan command: @@ -162,7 +164,8 @@ By default, applications running via Octane generate links prefixed with `http:/ ### Serving Your Application Via Nginx -> {tip} If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). +> **Note** +> If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). In production environments, you should serve your Octane application behind a traditional web server such as a Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. @@ -382,7 +385,8 @@ $service->method($request->input('name')); The global `request` helper will always return the request the application is currently handling and is therefore safe to use within your application. -> {note} It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. +> **Warning** +> It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. ### Configuration Repository Injection @@ -453,7 +457,8 @@ While building your application, you should take special care to avoid creating ## Concurrent Tasks -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's `concurrently` method. You may combine this method with PHP array destructuring to retrieve the results of each operation: @@ -477,7 +482,8 @@ php artisan octane:start --workers=4 --task-workers=6 ## Ticks & Intervals -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may register "tick" operations that will be executed every specified number of seconds. You may register "tick" callbacks via the `tick` method. The first argument provided to the `tick` method should be a string that represents the name of the ticker. The second argument should be a callable that will be invoked at the specified interval. @@ -499,7 +505,8 @@ Octane::tick('simple-ticker', fn () => ray('Ticking...')) ## The Octane Cache -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. Therefore, this cache driver is an excellent choice for applications that need extreme read / write speeds from their caching layer. @@ -509,7 +516,8 @@ This cache driver is powered by [Swoole tables](https://www.swoole.co.uk/docs/mo Cache::store('octane')->put('framework', 'Laravel', 30); ``` -> {tip} The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. +> **Note** +> The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. ### Cache Intervals @@ -527,7 +535,8 @@ Cache::store('octane')->interval('random', function () { ## Tables -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may define and interact with your own arbitrary [Swoole tables](https://www.swoole.co.uk/docs/modules/swoole-table). Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server. However, the data within them will be lost when the server is restarted. @@ -555,4 +564,5 @@ Octane::table('example')->set('uuid', [ return Octane::table('example')->get('uuid'); ``` -> {note} The column types supported by Swoole tables are: `string`, `int`, and `float`. +> **Warning** +> The column types supported by Swoole tables are: `string`, `int`, and `float`. diff --git a/packages.md b/packages.md index 6646e0d0488..b3eead64f93 100644 --- a/packages.md +++ b/packages.md @@ -108,7 +108,8 @@ 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. +> **Warning** +> 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 @@ -129,7 +130,8 @@ The `mergeConfigFrom` method accepts the path to your package's configuration fi ); } -> {note} This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. +> **Warning** +> This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. ### Routes diff --git a/pagination.md b/pagination.md index 135c642c1d5..529c34b964b 100644 --- a/pagination.md +++ b/pagination.md @@ -126,7 +126,8 @@ You may create a cursor based paginator instance via the `cursorPaginate` method Once you have retrieved a cursor paginator instance, you may [display the pagination results](#displaying-pagination-results) as you typically would when using the `paginate` and `simplePaginate` methods. For more information on the instance methods offered by the cursor paginator, please consult the [cursor paginator instance method documentation](#cursor-paginator-instance-methods). -> {note} Your query must contain an "order by" clause in order to take advantage of cursor pagination. +> **Warning** +> Your query must contain an "order by" clause in order to take advantage of cursor pagination. #### Cursor vs. Offset Pagination @@ -161,7 +162,8 @@ The `Paginator` and `CursorPaginator` classes do not need to know the total numb In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, the `CursorPaginator` corresponds to the `cursorPaginate` method, and 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. +> **Warning** +> 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. ### Customizing Pagination URLs diff --git a/passport.md b/passport.md index bd0f4082851..e8099c3567c 100644 --- a/passport.md +++ b/passport.md @@ -48,7 +48,8 @@ [Laravel Passport](https://github.com/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. +> **Warning** +> 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. ### Passport Or Sanctum? @@ -78,7 +79,8 @@ Next, you should execute the `passport:install` Artisan command. This command wi php artisan passport:install ``` -> {tip} If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). +> **Note** +> If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). After running the `passport:install` 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. If your model is already using the `Laravel\Sanctum\HasApiTokens` trait, you may remove that trait: @@ -250,7 +252,8 @@ By default, Passport issues long-lived access tokens that expire after one year. Passport::personalAccessTokensExpireIn(now()->addMonths(6)); } -> {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). +> **Warning** +> 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 @@ -415,7 +418,8 @@ Once a client has been created, developers may use their client ID and secret to return redirect('http://passport-app.test/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. +> **Note** +> Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. #### Approving The Request @@ -478,7 +482,8 @@ If the user approves the authorization request, they will be redirected back to 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. +> **Note** +> 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 @@ -667,7 +672,8 @@ If the state parameter matches, the consumer should issue a `POST` request to yo ## Password Grant Tokens -> {note} We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). +> **Warning** +> We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). 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. @@ -698,7 +704,8 @@ Once you have created a password grant client, you may request an access token b 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. +> **Note** +> Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. ### Requesting All Scopes @@ -783,7 +790,8 @@ When authenticating using the password grant, Passport will use the `password` a ## Implicit Grant Tokens -> {note} We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). +> **Warning** +> We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). 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 application's `App\Providers\AuthServiceProvider` class: @@ -819,7 +827,8 @@ Once the grant has been enabled, developers may use their client ID to request a return redirect('http://passport-app.test/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. +> **Note** +> Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. ## Client Credentials Grant Tokens @@ -873,7 +882,8 @@ To retrieve a token using this grant type, make a request to the `oauth/token` e Sometimes, your users may want to issue access tokens to themselves without going through the typical authorization code redirect flow. Allowing users to issue tokens to themselves via your application's UI can be useful for allowing users to experiment with your API or may serve as a simpler approach to issuing access tokens in general. -> {tip} If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. +> **Note** +> If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. ### Creating A Personal Access Client @@ -978,7 +988,8 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add // })->middleware('auth:api'); -> {note} If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. +> **Warning** +> If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. #### Multiple Authentication Guards @@ -1001,7 +1012,8 @@ The following route will utilize the `api-customers` guard, which uses the `cust // })->middleware('auth:api-customers'); -> {tip} For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). +> **Note** +> For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). ### Passing The Access Token @@ -1159,7 +1171,8 @@ Typically, if you want to consume your API from your JavaScript application, you \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ], -> {note} You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. +> **Warning** +> You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. 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: @@ -1192,7 +1205,8 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo When using this method of authentication, you will need to ensure a valid CSRF token header is included in your requests. The default Laravel JavaScript scaffolding includes an Axios instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. -> {tip} If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. +> **Note** +> If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. ## Events diff --git a/passwords.md b/passwords.md index 1aa2e2ea0c0..fb1efd6cc17 100644 --- a/passwords.md +++ b/passwords.md @@ -15,7 +15,8 @@ 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 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. +> **Note** +> 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 @@ -87,7 +88,8 @@ The `sendResetLink` method returns a "status" slug. This status may be translate You may be 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). +> **Note** +> 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 diff --git a/providers.md b/providers.md index 8fc544bfd09..69aa92f8ffa 100644 --- a/providers.md +++ b/providers.md @@ -18,7 +18,8 @@ If you open the `config/app.php` file included with Laravel, you will see a `pro 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). +> **Note** +> 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 diff --git a/queries.md b/queries.md index 3b7d05d69be..51da3fffd21 100644 --- a/queries.md +++ b/queries.md @@ -41,7 +41,8 @@ Laravel's database query builder provides a convenient, fluent interface to crea 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. +> **Warning** +> 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. ## Running Database Queries @@ -83,7 +84,8 @@ The `get` method returns an `Illuminate\Support\Collection` instance containing echo $user->name; } -> {tip} Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel collections, check out the [collection documentation](/docs/{{version}}/collections). +> **Note** +> Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel collections, check out the [collection documentation](/docs/{{version}}/collections). #### Retrieving A Single Row / Column From A Table @@ -155,7 +157,8 @@ If you are updating database records while chunking results, your chunk results } }); -> {note} When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results. +> **Warning** +> When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results. ### Streaming Results Lazily @@ -181,7 +184,8 @@ DB::table('users')->where('active', false) }); ``` -> {note} When updating or deleting records while iterating over them, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the results. +> **Warning** +> When updating or deleting records while iterating over them, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the results. ### Aggregates @@ -248,7 +252,8 @@ Sometimes you may need to insert an arbitrary string into a query. To create a r ->groupBy('status') ->get(); -> {note} Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities. +> **Warning** +> Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities. ### Raw Methods @@ -433,7 +438,8 @@ You may also pass an array of conditions to the `where` function. Each element o ['subscribed', '<>', '1'], ])->get(); -> {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. +> **Warning** +> 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. ### Or Where Clauses @@ -461,7 +467,8 @@ The example above will produce the following 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. +> **Warning** +> You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. ### Where Not Clauses @@ -539,7 +546,8 @@ The `whereNotIn` method verifies that the given column's value is not contained ->whereNotIn('id', [1, 2, 3]) ->get(); -> {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. +> **Warning** +> 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** @@ -628,7 +636,8 @@ As you can see, passing a closure into the `where` method instructs the query bu 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. +> **Warning** +> You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. ### Advanced Where Clauses @@ -683,7 +692,8 @@ Or, you may need to construct a "where" clause that compares a column to the res ### Full Text Where Clauses -> {note} Full text where clauses are currently supported by MySQL and PostgreSQL. +> **Warning** +> Full text where clauses are currently supported by MySQL and PostgreSQL. The `whereFullText` and `orWhereFullText` methods may be used to add full text "where" clauses to a query for columns that have [full text indexes](/docs/{{version}}/migrations#available-index-types). These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a `MATCH AGAINST` clause will be generated for applications utilizing MySQL: @@ -861,7 +871,8 @@ If the table has an auto-incrementing id, use the `insertGetId` method to insert ['email' => 'john@example.com', 'votes' => 0] ); -> {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. +> **Warning** +> 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. ### Upserts @@ -879,7 +890,8 @@ The `upsert` method will insert records that do not exist and update the records 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. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. +> **Warning** +> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. ## Update Statements diff --git a/queues.md b/queues.md index d5fb40477a4..a020965824c 100644 --- a/queues.md +++ b/queues.md @@ -55,7 +55,8 @@ Laravel queues provide a unified queueing API across a variety of different queu 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. -> {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. +> **Note** +> 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 @@ -126,7 +127,8 @@ Adjusting this value based on your queue load can be more efficient than continu 'block_for' => 5, ], -> {note} Setting `block_for` to `0` will cause queue workers to block indefinitely until a job is available. This will also prevent signals such as `SIGTERM` from being handled until the next job has been processed. +> **Warning** +> Setting `block_for` to `0` will cause queue workers to block indefinitely until a job is available. This will also prevent signals such as `SIGTERM` from being handled until the next job has been processed. #### Other Driver Prerequisites @@ -155,7 +157,8 @@ php artisan make:job ProcessPodcast The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue` interface, indicating to Laravel that the job should be pushed onto the queue to run asynchronously. -> {tip} Job stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> Job stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). ### Class Structure @@ -226,7 +229,8 @@ If you would like to take total control over how the container injects dependenc return $job->handle($app->make(AudioProcessor::class)); }); -> {note} Binary data, such as raw image contents, should be passed through the `base64_encode` function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue. +> **Warning** +> Binary data, such as raw image contents, should be passed through the `base64_encode` function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue. #### Queued Relationships @@ -249,7 +253,8 @@ Furthermore, when a job is deserialized and model relationships are re-retrieved ### Unique Jobs -> {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. In addition, unique job constraints do not apply to jobs within batches. +> **Warning** +> 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. In addition, unique job constraints do not apply to jobs within batches. 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: @@ -302,7 +307,8 @@ In certain cases, you may want to define a specific "key" that makes the job uni In the example above, the `UpdateSearchIndex` job is unique by a product ID. So, any new dispatches of the job with the same product ID will be ignored until the existing job has completed processing. In addition, if the existing job is not processed within one hour, the unique lock will be released and another job with the same unique key can be dispatched to the queue. -> {note} If your application dispatches jobs from multiple web servers or containers, you should ensure that all of your servers are communicating with the same central cache server so that Laravel can accurately determine if a job is unique. +> **Warning** +> If your application dispatches jobs from multiple web servers or containers, you should ensure that all of your servers are communicating with the same central cache server so that Laravel can accurately determine if a job is unique. #### Keeping Jobs Unique Until Processing Begins @@ -342,7 +348,8 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t } } -> {tip} If you only need to limit the concurrent processing of a job, use the [`WithoutOverlapping`](/docs/{{version}}/queues#preventing-job-overlaps) job middleware instead. +> **Note** +> If you only need to limit the concurrent processing of a job, use the [`WithoutOverlapping`](/docs/{{version}}/queues#preventing-job-overlaps) job middleware instead. ## Job Middleware @@ -420,7 +427,8 @@ After creating job middleware, they may be attached to a job by returning them f return [new RateLimited]; } -> {tip} Job middleware can also be assigned to queueable event listeners, mailables, and notifications. +> **Note** +> Job middleware can also be assigned to queueable event listeners, mailables, and notifications. ### Rate Limiting @@ -478,7 +486,8 @@ If you do not want a job to be retried when it is rate limited, you may use the return [(new RateLimited('backups'))->dontRelease()]; } -> {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. +> **Note** +> 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 @@ -535,7 +544,8 @@ The `WithoutOverlapping` middleware is powered by Laravel's atomic lock feature. return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; } -> {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. +> **Warning** +> 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. ### Throttling Exceptions @@ -596,7 +606,8 @@ Internally, this middleware uses Laravel's cache system to implement rate limiti return [(new ThrottlesExceptions(10, 10))->by('key')]; } -> {tip} If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. +> **Note** +> If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. ## Dispatching Jobs @@ -669,7 +680,8 @@ If you would like to specify that a job should not be immediately available for } } -> {note} The Amazon SQS queue service has a maximum delay time of 15 minutes. +> **Warning** +> The Amazon SQS queue service has a maximum delay time of 15 minutes. #### Dispatching After The Response Is Sent To Browser @@ -738,7 +750,8 @@ When the `after_commit` option is `true`, you may dispatch jobs within database If a transaction is rolled back due to an exception that occurs during the transaction, the jobs that were dispatched during that transaction will be discarded. -> {tip} Setting the `after_commit` configuration option to `true` will also cause any queued event listeners, mailables, notifications, and broadcast events to be dispatched after all open database transactions have been committed. +> **Note** +> Setting the `after_commit` configuration option to `true` will also cause any queued event listeners, mailables, notifications, and broadcast events to be dispatched after all open database transactions have been committed. #### Specifying Commit Dispatch Behavior Inline @@ -779,7 +792,8 @@ In addition to chaining job class instances, you may also chain closures: }, ])->dispatch(); -> {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. +> **Warning** +> 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 @@ -982,7 +996,8 @@ As an alternative to defining how many times a job may be attempted before it fa return now()->addMinutes(10); } -> {tip} You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). +> **Note** +> You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). #### Max Exceptions @@ -1032,7 +1047,8 @@ In this example, the job is released for ten seconds if the application is unabl #### Timeout -> {note} The `pcntl` PHP extension must be installed in order to specify job timeouts. +> **Warning** +> The `pcntl` PHP extension must be installed in order to specify job timeouts. Often, you know roughly how long you expect your queued jobs to take. For this reason, Laravel allows you to specify a "timeout" value. By default, the timeout value is 60 seconds. 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). @@ -1123,7 +1139,8 @@ If you would like to mark your job as failed because of an exception that you ha $this->fail($exception); -> {tip} For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). +> **Note** +> For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). ## Job Batching @@ -1201,7 +1218,8 @@ To dispatch a batch of jobs, you should use the `batch` method of the `Bus` faca 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. -> {note} Since batch callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within the callbacks. +> **Warning** +> Since batch callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within the callbacks. #### Naming Batches @@ -1282,7 +1300,8 @@ In this example, we will use the `LoadImportBatch` job to hydrate the batch with })); } -> {note} You may only add jobs to a batch from within a job that belongs to the same batch. +> **Warning** +> You may only add jobs to a batch from within a job that belongs to the same batch. ### Inspecting Batches @@ -1443,7 +1462,8 @@ Laravel includes an Artisan command that will start a queue worker and process n php artisan queue:work ``` -> {tip} To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. +> **Note** +> To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. 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. @@ -1545,7 +1565,8 @@ php artisan queue:restart 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 that a cache driver is properly configured for your application before using this feature. +> **Note** +> 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 @@ -1555,7 +1576,8 @@ This command will instruct all queue workers to gracefully exit after they finis 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. +> **Warning** +> 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 @@ -1568,7 +1590,8 @@ 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 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. +> **Warning** +> 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 @@ -1586,7 +1609,8 @@ Supervisor is a process monitor for the Linux operating system, and will automat sudo apt-get install supervisor ``` -> {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. +> **Note** +> 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 @@ -1610,7 +1634,8 @@ 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. +> **Warning** +> 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. #### Starting Supervisor @@ -1747,7 +1772,8 @@ When a particular job fails, you may want to send an alert to your users or reve } } -> {note} A new instance of the job is instantiated before invoking the `failed` method; therefore, any class property modifications that may have occurred within the `handle` method will be lost. +> **Warning** +> A new instance of the job is instantiated before invoking the `failed` method; therefore, any class property modifications that may have occurred within the `handle` method will be lost. ### Retrying Failed Jobs @@ -1788,7 +1814,8 @@ If you would like to delete a failed job, you may use the `queue:forget` command php artisan queue:forget 91401d2c-0784-4f43-824c-34f94a33c24d ``` -> {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. +> **Note** +> 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 from the `failed_jobs` table, you may use the `queue:flush` command: @@ -1902,7 +1929,8 @@ If you would like to register an event listener that will be invoked when a job ## Clearing Jobs From Queues -> {tip} When using [Horizon](/docs/{{version}}/horizon), you should use the `horizon:clear` command to clear jobs from the queue instead of the `queue:clear` command. +> **Note** +> When using [Horizon](/docs/{{version}}/horizon), you should use the `horizon:clear` command to clear jobs from the queue instead of the `queue:clear` command. If you would like to delete all jobs from the default queue of the default connection, you may do so using the `queue:clear` Artisan command: @@ -1916,7 +1944,8 @@ You may also provide the `connection` argument and `queue` option to delete jobs php artisan queue:clear redis --queue=emails ``` -> {note} Clearing jobs from queues is only available for the SQS, Redis, and database queue drivers. In addition, the SQS message deletion process takes up to 60 seconds, so jobs sent to the SQS queue up to 60 seconds after you clear the queue might also be deleted. +> **Warning** +> Clearing jobs from queues is only available for the SQS, Redis, and database queue drivers. In addition, the SQS message deletion process takes up to 60 seconds, so jobs sent to the SQS queue up to 60 seconds after you clear the queue might also be deleted. ## Monitoring Your Queues diff --git a/rate-limiting.md b/rate-limiting.md index cbffa7486a7..6800b986016 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -11,7 +11,8 @@ Laravel includes a simple to use rate limiting abstraction which, in conjunction with your application's [cache](cache), provides an easy way to limit any action during a specified window of time. -> {tip} If you are interested in rate limiting incoming HTTP requests, please consult the [rate limiter middleware documentation](routing#rate-limiting). +> **Note** +> If you are interested in rate limiting incoming HTTP requests, please consult the [rate limiter middleware documentation](routing#rate-limiting). ### Cache Configuration diff --git a/redis.md b/redis.md index ba670195757..1c2dbf2cddd 100644 --- a/redis.md +++ b/redis.md @@ -264,7 +264,8 @@ The `Redis` facade's `transaction` method provides a convenient wrapper around R $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. +> **Warning** +> 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 @@ -284,7 +285,8 @@ In this example, we will increment a counter, inspect its new value, and increme 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. +> **Warning** +> Please consult the [Redis documentation](https://redis.io/commands/eval) for more information on Redis scripting. ### Pipelining Commands diff --git a/releases.md b/releases.md index 76cbb64d37b..2b6aa90422a 100644 --- a/releases.md +++ b/releases.md @@ -131,7 +131,8 @@ public function address(): Attribute ### Enum Eloquent Attribute Casting -> {note} Enum casting is only available for PHP 8.1+. +> **Warning** +> Enum casting is only available for PHP 8.1+. _Enum casting was contributed by [Mohamed Said](https://github.com/themsaid)_. diff --git a/requests.md b/requests.md index a9105f3b450..f2c52ebf9bc 100644 --- a/requests.md +++ b/requests.md @@ -223,7 +223,8 @@ 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. +> **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. ## Input @@ -349,7 +350,8 @@ If you need to retrieve a subset of the input data, you may use the `only` and ` $input = $request->except('credit_card'); -> {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. +> **Warning** +> 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 Input Is Present @@ -567,7 +569,8 @@ If you do not want a filename to be automatically generated, you may use the `st $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). +> **Note** +> For more information about file storage in Laravel, check out the complete [file storage documentation](/docs/{{version}}/filesystem). ## Configuring Trusted Proxies @@ -603,7 +606,8 @@ To solve this, you may use the `App\Http\Middleware\TrustProxies` middleware tha protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO; } -> {tip} If you are using AWS Elastic Load Balancing, your `$headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `$headers` property, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). +> **Note** +> If you are using AWS Elastic Load Balancing, your `$headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `$headers` property, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). #### Trusting All Proxies diff --git a/responses.md b/responses.md index b74105f0f79..06b232abd95 100644 --- a/responses.md +++ b/responses.md @@ -34,7 +34,8 @@ In addition to returning strings from your routes and controllers, you may also return [1, 2, 3]; }); -> {tip} Did you know you can also return [Eloquent collections](/docs/{{version}}/eloquent-collections) from your routes or controllers? They will automatically be converted to JSON. Give it a shot! +> **Note** +> Did you know you can also return [Eloquent collections](/docs/{{version}}/eloquent-collections) from your routes or controllers? They will automatically be converted to JSON. Give it a shot! #### Response Objects @@ -288,7 +289,8 @@ The `download` method may be used to generate a response that forces the user's return response()->download($pathToFile, $name, $headers); -> {note} Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. +> **Warning** +> Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. #### Streamed Downloads diff --git a/routing.md b/routing.md index fa5f4a28123..355498e5aa9 100644 --- a/routing.md +++ b/routing.md @@ -74,7 +74,8 @@ Sometimes you may need to register a route that responds to multiple HTTP verbs. // }); -> {tip} When defining multiple routes that share the same URI, routes using the `get`, `post`, `put`, `patch`, `delete`, and `options` methods should be defined before routes using the `any`, `match`, and `redirect` methods. This ensures the incoming request is matched with the correct route. +> **Note** +> When defining multiple routes that share the same URI, routes using the `get`, `post`, `put`, `patch`, `delete`, and `options` methods should be defined before routes using the `any`, `match`, and `redirect` methods. This ensures the incoming request is matched with the correct route. #### Dependency Injection @@ -112,7 +113,8 @@ Or, you may use the `Route::permanentRedirect` method to return a `301` status c Route::permanentRedirect('/here', '/there'); -> {note} When using route parameters in redirect routes, the following parameters are reserved by Laravel and cannot be used: `destination` and `status`. +> **Warning** +> When using route parameters in redirect routes, the following parameters are reserved by Laravel and cannot be used: `destination` and `status`. ### View Routes @@ -123,7 +125,8 @@ If your route only needs to return a [view](/docs/{{version}}/views), you may us Route::view('/welcome', 'welcome', ['name' => 'Taylor']); -> {note} When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. +> **Warning** +> When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. ### The Route List @@ -269,7 +272,8 @@ The Laravel routing component allows all characters except `/` to be present wit return $search; })->where('search', '.*'); -> {note} Encoded forward slashes are only supported within the last route segment. +> **Warning** +> Encoded forward slashes are only supported within the last route segment. ## Named Routes @@ -287,7 +291,8 @@ You may also specify route names for controller actions: [UserProfileController::class, 'show'] )->name('profile'); -> {note} Route names should always be unique. +> **Warning** +> Route names should always be unique. #### Generating URLs To Named Routes @@ -318,7 +323,8 @@ If you pass additional parameters in the array, those key / value pairs will aut // /user/1/profile?photos=yes -> {tip} Sometimes, you may wish to specify request-wide default values for URL parameters, such as the current locale. To accomplish this, you may use the [`URL::defaults` method](/docs/{{version}}/urls#default-values). +> **Note** +> Sometimes, you may wish to specify request-wide default values for URL parameters, such as the current locale. To accomplish this, you may use the [`URL::defaults` method](/docs/{{version}}/urls#default-values). #### Inspecting The Current Route @@ -386,7 +392,8 @@ Route groups may also be used to handle subdomain routing. Subdomains may be ass }); }); -> {note} In order to ensure your subdomain routes are reachable, you should register subdomain routes before registering root domain routes. This will prevent root domain routes from overwriting subdomain routes which have the same URI path. +> **Warning** +> In order to ensure your subdomain routes are reachable, you should register subdomain routes before registering root domain routes. This will prevent root domain routes from overwriting subdomain routes which have the same URI path. ### Route Prefixes @@ -643,7 +650,8 @@ Using the `Route::fallback` method, you may define a route that will be executed // }); -> {note} The fallback route should always be the last route registered by your application. +> **Warning** +> The fallback route should always be the last route registered by your application. ## Rate Limiting @@ -775,7 +783,8 @@ You may refer to the API documentation for both the [underlying class of the Rou 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). +> **Note** +> 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 diff --git a/sail.md b/sail.md index 062bcba1f0d..333e8f0fa69 100644 --- a/sail.md +++ b/sail.md @@ -419,7 +419,8 @@ If you would like to choose the subdomain for your shared site, you may provide sail share --subdomain=my-sail-site ``` -> {tip} The `share` command is powered by [Expose](https://github.com/beyondcode/expose), an open source tunneling service by [BeyondCode](https://beyondco.de). +> **Note** +> The `share` command is powered by [Expose](https://github.com/beyondcode/expose), an open source tunneling service by [BeyondCode](https://beyondco.de). ## Debugging With Xdebug @@ -466,7 +467,8 @@ To debug your application while interacting with the application via a web brows If you're using PhpStorm, please review JetBrain's documentation regarding [zero-configuration debugging](https://www.jetbrains.com/help/phpstorm/zero-configuration-debugging.html). -> {note} Laravel Sail relies on `artisan serve` to serve your application. The `artisan serve` command only accepts the `XDEBUG_CONFIG` and `XDEBUG_MODE` variables as of Laravel version 8.53.0. Older versions of Laravel (8.52.0 and below) do not support these variables and will not accept debug connections. +> **Warning** +> Laravel Sail relies on `artisan serve` to serve your application. The `artisan serve` command only accepts the `XDEBUG_CONFIG` and `XDEBUG_MODE` variables as of Laravel version 8.53.0. Older versions of Laravel (8.52.0 and below) do not support these variables and will not accept debug connections. ## Customization diff --git a/sanctum.md b/sanctum.md index 0cf08452f4b..0d901c8ee3c 100644 --- a/sanctum.md +++ b/sanctum.md @@ -48,12 +48,14 @@ For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses 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. +> **Note** +> 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 -> {tip} The most recent versions of Laravel already include Laravel Sanctum. However, if your application's `composer.json` file does not include `laravel/sanctum`, you may follow the installation instructions below. +> **Note** +> The most recent versions of Laravel already include Laravel Sanctum. However, if your application's `composer.json` file does not include `laravel/sanctum`, you may follow the installation instructions below. You may install Laravel Sanctum via the Composer package manager: @@ -119,7 +121,8 @@ 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 features](#spa-authentication). +> **Note** +> 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 @@ -249,7 +252,8 @@ Sanctum also exists to provide a simple method of authenticating single page app 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. Additionally, you should ensure that you send the `Accept: application/json` header with your request. +> **Warning** +> In order to authenticate, your SPA and API must share the same top-level domain. However, they may be placed on different subdomains. Additionally, you should ensure that you send the `Accept: application/json` header with your request. @@ -260,7 +264,8 @@ 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 a port (`127.0.0.1:8000`), you should ensure that you include the port number with the domain. +> **Warning** +> 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 @@ -315,7 +320,8 @@ If the login request is successful, you will be authenticated and subsequent req 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. -> {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. +> **Warning** +> 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 @@ -399,7 +405,8 @@ Typically, you will make a request to the token endpoint from your mobile applic 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). +> **Note** +> When issuing tokens for a mobile application, you are also free to specify [token abilities](#token-abilities). ### Protecting Routes diff --git a/scheduling.md b/scheduling.md index d9bef6d6fbd..a01caff9c00 100644 --- a/scheduling.md +++ b/scheduling.md @@ -247,7 +247,8 @@ If you are repeatedly assigning the same timezone to all of your scheduled tasks return 'America/Chicago'; } -> {note} Remember that some timezones utilize daylight savings time. When daylight saving time changes occur, your scheduled task may run twice or even not run at all. For this reason, we recommend avoiding timezone scheduling when possible. +> **Warning** +> Remember that some timezones utilize daylight savings time. When daylight saving time changes occur, your scheduled task may run twice or even not run at all. For this reason, we recommend avoiding timezone scheduling when possible. ### Preventing Task Overlaps @@ -267,7 +268,8 @@ Behind the scenes, the `withoutOverlapping` method utilizes your application's [ ### Running Tasks On One 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. +> **Warning** +> 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'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! @@ -304,7 +306,8 @@ By default, multiple tasks scheduled at the same time will execute sequentially ->daily() ->runInBackground(); -> {note} The `runInBackground` method may only be used when scheduling tasks via the `command` and `exec` methods. +> **Warning** +> The `runInBackground` method may only be used when scheduling tasks via the `command` and `exec` methods. ### Maintenance Mode @@ -361,7 +364,8 @@ If you only want to email the output if the scheduled Artisan or system command ->daily() ->emailOutputOnFailure('taylor@example.com'); -> {note} The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods. +> **Warning** +> The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods. ## Task Hooks diff --git a/scout.md b/scout.md index 5753da86064..5e8a83afd5d 100644 --- a/scout.md +++ b/scout.md @@ -99,7 +99,8 @@ For more information regarding MeiliSearch, please consult the [MeiliSearch docu In addition, you should ensure that you install a version of `meilisearch/meilisearch-php` that is compatible with your MeiliSearch binary version by reviewing [MeiliSearch's documentation regarding binary compatibility](https://github.com/meilisearch/meilisearch-php#-compatibility-with-meilisearch). -> {note} When upgrading Scout on an application that utilizes MeiliSearch, you should always [review any additional breaking changes](https://github.com/meilisearch/MeiliSearch/releases) to the MeiliSearch service itself. +> **Warning** +> When upgrading Scout on an application that utilizes MeiliSearch, you should always [review any additional breaking changes](https://github.com/meilisearch/MeiliSearch/releases) to the MeiliSearch service itself. ### Queueing @@ -262,7 +263,8 @@ Enabling this feature this will also pass the request's IP address and your auth ### Database Engine -> {note} The database engine currently supports MySQL and PostgreSQL. +> **Warning** +> The database engine currently supports MySQL and PostgreSQL. If your application interacts with small to medium sized databases or has a light workload, you may find it more convenient to get started with Scout's "database" engine. The database engine will use "where like" clauses and full text indexes when filtering results from your existing database to determine the applicable search results for your query. @@ -302,7 +304,8 @@ public function toSearchableArray() } ``` -> {note} Before specifying that a column should use full text query constraints, ensure that the column has been assigned a [full text index](/docs/{{version}}/migrations#available-index-types). +> **Warning** +> Before specifying that a column should use full text query constraints, ensure that the column has been assigned a [full text index](/docs/{{version}}/migrations#available-index-types). ### Collection Engine @@ -387,7 +390,8 @@ Or, if you already have a collection of Eloquent models in memory, you may call $orders->searchable(); -> {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. +> **Note** +> 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 @@ -465,7 +469,8 @@ Sometimes you may need to only make a model searchable under certain conditions. 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. -> {note} The `shouldBeSearchable` method is not applicable when using Scout's "database" engine, as all searchable data is always stored in the database. To achieve similar behavior when using the database engine, you should use [where clauses](#where-clauses) instead. +> **Warning** +> The `shouldBeSearchable` method is not applicable when using Scout's "database" engine, as all searchable data is always stored in the database. To achieve similar behavior when using the database engine, you should use [where clauses](#where-clauses) instead. ## Searching @@ -566,7 +571,8 @@ When this configuration option is `true`, Scout will not remove soft deleted mod // Only include trashed records when retrieving results... $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. +> **Note** +> When a soft deleted model is permanently deleted using `forceDelete`, Scout will remove it from the search index automatically. ### Customizing Engine Searches diff --git a/seeding.md b/seeding.md index 1079248d02b..ee798d3ce43 100644 --- a/seeding.md +++ b/seeding.md @@ -12,7 +12,8 @@ Laravel includes the ability to seed your database with 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. +> **Note** +> [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled during database seeding. ## Writing Seeders @@ -53,7 +54,8 @@ As an example, let's modify the default `DatabaseSeeder` class and add a databas } } -> {tip} 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). +> **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). ### Using Model Factories diff --git a/session.md b/session.md index 9fdf9ec572c..66b19b40459 100644 --- a/session.md +++ b/session.md @@ -39,7 +39,8 @@ The session `driver` configuration option defines where session data will be sto
-> {tip} The array driver is primarily used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted. +> **Note** +> The array driver is primarily used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted. ### Driver Prerequisites @@ -71,7 +72,8 @@ php artisan migrate 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. +> **Note** +> In the `session` configuration file, the `connection` option may be used to specify which Redis connection is used by the session. ## Interacting With The Session @@ -129,7 +131,8 @@ You may also use the global `session` PHP function to retrieve and store data in session(['key' => 'value']); }); -> {tip} There is little practical difference between using the session via an HTTP request instance versus using the global `session` helper. Both methods are [testable](/docs/{{version}}/testing) via the `assertSessionHas` method which is available in all of your test cases. +> **Note** +> There is little practical difference between using the session via an HTTP request instance versus using the global `session` helper. Both methods are [testable](/docs/{{version}}/testing) via the `assertSessionHas` method which is available in all of your test cases. #### Retrieving All Session Data @@ -243,7 +246,8 @@ If you need to regenerate the session ID and remove all data from the session in ## Session Blocking -> {note} To utilize session blocking, your application must be using a cache driver that supports [atomic locks](/docs/{{version}}/cache#atomic-locks). Currently, those cache drivers include the `memcached`, `dynamodb`, `redis`, and `database` drivers. In addition, you may not use the `cookie` session driver. +> **Warning** +> To utilize session blocking, your application must be using a cache driver that supports [atomic locks](/docs/{{version}}/cache#atomic-locks). Currently, those cache drivers include the `memcached`, `dynamodb`, `redis`, and `database` drivers. In addition, you may not use the `cookie` session driver. By default, Laravel allows requests using the same session to execute concurrently. So, for example, if you use a JavaScript HTTP library to make two HTTP requests to your application, they will both execute at the same time. For many applications, this is not a problem; however, session data loss can occur in a small subset of applications that make concurrent requests to two different application endpoints which both write data to the session. @@ -289,7 +293,8 @@ If none of the existing session drivers fit your application's needs, Laravel ma public function gc($lifetime) {} } -> {tip} Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `MongoSessionHandler`. +> **Note** +> Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `MongoSessionHandler`. Since the purpose of these methods is not readily understandable, let's quickly cover what each of the methods do: diff --git a/socialite.md b/socialite.md index 35a352deb3e..88143ae55d9 100644 --- a/socialite.md +++ b/socialite.md @@ -16,7 +16,8 @@ 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 via Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, and Bitbucket. -> {tip} Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. +> **Note** +> Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. ## Installation @@ -45,7 +46,8 @@ These credentials should be placed in your application's `config/services.php` c 'redirect' => 'http://example.com/callback-url', ], -> {tip} If the `redirect` option contains a relative path, it will automatically be resolved to a fully qualified URL. +> **Note** +> If the `redirect` option contains a relative path, it will automatically be resolved to a fully qualified URL. ## Authentication @@ -95,7 +97,8 @@ Once the user has been retrieved from the OAuth provider, you may determine if t return redirect('/dashboard'); }); -> {tip} For more information regarding what user information is available from specific OAuth providers, please consult the documentation on [retrieving user details](#retrieving-user-details). +> **Note** +> For more information regarding what user information is available from specific OAuth providers, please consult the documentation on [retrieving user details](#retrieving-user-details). ### Access Scopes @@ -125,7 +128,8 @@ A number of OAuth providers support other optional parameters on the redirect re ->with(['hd' => 'example.com']) ->redirect(); -> {note} When using the `with` method, be careful not to pass any reserved keywords such as `state` or `response_type`. +> **Warning** +> When using the `with` method, be careful not to pass any reserved keywords such as `state` or `response_type`. ## Retrieving User Details @@ -183,4 +187,5 @@ The `stateless` method may be used to disable session state verification. This i return Socialite::driver('google')->stateless()->user(); -> {note} Stateless authentication is not available for the Twitter OAuth 1.0 driver. +> **Warning** +> Stateless authentication is not available for the Twitter OAuth 1.0 driver. diff --git a/starter-kits.md b/starter-kits.md index 70b6defd923..53d3c6d252d 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -52,7 +52,8 @@ 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 Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). +> **Note** +> To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). ### Breeze & React / Vue diff --git a/structure.md b/structure.md index bfb9525cf5c..6b10eaa061b 100644 --- a/structure.md +++ b/structure.md @@ -110,7 +110,8 @@ The `app` directory contains a variety of additional directories such as `Consol A variety of other directories will be generated inside the `app` directory as you use the `make` Artisan commands to generate classes. So, for example, the `app/Jobs` directory will not exist until you execute the `make:job` Artisan command to generate a job class. -> {tip} Many of the classes in the `app` directory can be generated by Artisan via commands. To review the available commands, run the `php artisan list make` command in your terminal. +> **Note** +> Many of the classes in the `app` directory can be generated by Artisan via commands. To review the available commands, run the `php artisan list make` command in your terminal. #### The Broadcasting Directory diff --git a/telescope.md b/telescope.md index 3ccf67e1fb1..77d2fd39fe4 100644 --- a/telescope.md +++ b/telescope.md @@ -142,7 +142,8 @@ The Telescope dashboard may be accessed at the `/telescope` route. By default, y }); } -> {note} You should ensure you change your `APP_ENV` environment variable to `production` in your production environment. Otherwise, your Telescope installation will be publicly available. +> **Warning** +> You should ensure you change your `APP_ENV` environment variable to `production` in your production environment. Otherwise, your Telescope installation will be publicly available. ## Upgrading Telescope diff --git a/testing.md b/testing.md index 837e0947f58..2545849f6b2 100644 --- a/testing.md +++ b/testing.md @@ -57,7 +57,8 @@ php artisan make:test UserTest --pest php artisan make:test UserTest --unit --pest ``` -> {tip} Test stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> 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](https://phpunit.de). To run your tests, execute the `vendor/bin/phpunit` or `php artisan test` command from your terminal: @@ -80,7 +81,8 @@ Once the test has been generated, you may define test methods as you normally wo } } -> {note} If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. +> **Warning** +> If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. ## Running Tests @@ -118,7 +120,8 @@ By default, Laravel will create as many processes as there are available CPU cor php artisan test --parallel --processes=4 ``` -> {note} When running tests in parallel, some PHPUnit options (such as `--do-not-cache-result`) may not be available. +> **Warning** +> When running tests in parallel, some PHPUnit options (such as `--do-not-cache-result`) may not be available. #### Parallel Testing & Databases @@ -188,7 +191,8 @@ If you would like to access the current parallel process "token" from any other ### Reporting Test Coverage -> {note} This feature requires [Xdebug](https://xdebug.org) or [PCOV](https://pecl.php.net/package/pcov). +> **Warning** +> This feature requires [Xdebug](https://xdebug.org) or [PCOV](https://pecl.php.net/package/pcov). When running your application tests, you may want to determine whether your test cases are actually covering the application code and how much application code is used when running your tests. To accomplish this, you may provide the `--coverage` option when invoking the `test` command: diff --git a/upgrade.md b/upgrade.md index 3e771f08328..a455de415d0 100644 --- a/upgrade.md +++ b/upgrade.md @@ -37,7 +37,8 @@ #### Estimated Upgrade Time: 30 Minutes -> {tip} We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. +> **Note** +> We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. ### Updating Dependencies @@ -514,7 +515,8 @@ Various SwiftMailer related methods, some of which were undocumented, have been ); }); -> {note} Please thoroughly review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#creating-sending-messages) for all possible interactions with the `Symfony\Component\Mime\Email` object. +> **Warning** +> Please thoroughly review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#creating-sending-messages) for all possible interactions with the `Symfony\Component\Mime\Email` object. The list below contains a more thorough overview of renamed methods. Many of these methods are low-level methods used to interact with SwiftMailer / Symfony Mailer directly, so may not be commonly used within most Laravel applications: @@ -590,7 +592,8 @@ Defining stream options for the SMTP transport is no longer supported. Instead, To learn more about the available configuration options, please review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#transport-setup). -> {note} In spite of the example above, you are not generally advised to disable SSL verification since it introduces the possibility of "man-in-the-middle" attacks. +> **Warning** +> In spite of the example above, you are not generally advised to disable SSL verification since it introduces the possibility of "man-in-the-middle" attacks. #### SMTP `auth_mode` diff --git a/valet.md b/valet.md index b7c6207399b..f49bd01e3a3 100644 --- a/valet.md +++ b/valet.md @@ -68,7 +68,8 @@ However, you may extend Valet with your own [custom drivers](#custom-valet-drive ## Installation -> {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. +> **Warning** +> 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. To get started, you first need to ensure that Homebrew is up to date using the `update` command: @@ -117,7 +118,8 @@ php@7.2 Once this file has been created, you may simply execute the `valet use` command and the command will determine the site's preferred PHP version by reading the file. -> {note} Valet only serves one PHP version at a time, even if you have multiple PHP versions installed. +> **Warning** +> Valet only serves one PHP version at a time, even if you have multiple PHP versions installed. #### Database @@ -270,7 +272,8 @@ valet share To stop sharing your site, you may press `Control + C`. Sharing your site using Ngrok requires you to [create an Ngrok account](https://dashboard.ngrok.com/signup) and [setup an authentication token](https://dashboard.ngrok.com/get-started/your-authtoken). -> {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). +> **Note** +> 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 @@ -395,7 +398,8 @@ The `isStaticFile` should determine if the incoming request is for a file that i return false; } -> {note} The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. +> **Warning** +> The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. #### The `frontControllerPath` Method diff --git a/validation.md b/validation.md index 49a5290ca02..c1648a48b36 100644 --- a/validation.md +++ b/validation.md @@ -317,7 +317,8 @@ As you might have guessed, the `authorize` method is responsible for determining ]; } -> {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). +> **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). 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: @@ -436,7 +437,8 @@ If you plan to handle authorization logic for the request in another part of you return true; } -> {tip} You may type-hint any dependencies you need within the `authorize` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). +> **Note** +> You may type-hint any dependencies you need within the `authorize` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). ### Customizing The Error Messages @@ -1092,7 +1094,8 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida 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. +> **Warning** +> The `dns` and `spoof` validators require the PHP `intl` extension. #### ends_with:_foo_,_bar_,... @@ -1111,7 +1114,8 @@ The `Enum` rule is a class based rule that validates whether the field under val 'status' => [new Enum(ServerStatus::class)], ]); -> {note} Enums are only available on PHP 8.1+. +> **Warning** +> Enums are only available on PHP 8.1+. #### exclude @@ -1263,7 +1267,8 @@ The field under validation must exist in _anotherfield_'s values. The field under validation must be an integer. -> {note} This validation rule does not verify that the input is of the "integer" variable type, only that the input is of a type accepted by PHP's `FILTER_VALIDATE_INT` rule. If you need to validate the input as being a number please use this rule in combination with [the `numeric` validation rule](#rule-numeric). +> **Warning** +> This validation rule does not verify that the input is of the "integer" variable type, only that the input is of a type accepted by PHP's `FILTER_VALIDATE_INT` rule. If you need to validate the input as being a number please use this rule in combination with [the `numeric` validation rule](#rule-numeric). #### ip @@ -1338,7 +1343,8 @@ The field under validation must have a minimum _value_. Strings, numerics, array The field under validation must be a multiple of _value_. -> {note} The [`bcmath` PHP extension](https://www.php.net/manual/en/book.bc.php) is required in order to use the `multiple_of` rule. +> **Warning** +> The [`bcmath` PHP extension](https://www.php.net/manual/en/book.bc.php) is required in order to use the `multiple_of` rule. #### not_in:_foo_,_bar_,... @@ -1361,7 +1367,8 @@ 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 your validation rules using an array instead of using `|` delimiters, especially if the regular expression contains a `|` character. +> **Warning** +> 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 @@ -1378,7 +1385,8 @@ The field under validation must be [numeric](https://www.php.net/manual/en/funct The field under validation must match the authenticated user's password. -> {note} This rule was renamed to `current_password` with the intention of removing it in Laravel 9. Please use the [Current Password](#rule-current-password) rule instead. +> **Warning** +> This rule was renamed to `current_password` with the intention of removing it in Laravel 9. Please use the [Current Password](#rule-current-password) rule instead. #### present @@ -1425,7 +1433,8 @@ 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 `|` delimiters, especially if the regular expression contains a `|` character. +> **Warning** +> 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 @@ -1563,7 +1572,8 @@ 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. +> **Warning** +> 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 also pass the entire model instance. Laravel will automatically extract the key from the model: @@ -1628,7 +1638,8 @@ In some situations, you may wish to run validation checks against a field **only In the example above, the `email` field will only be validated if it is present in the `$data` array. -> {tip} If you are attempting to validate a field that should always be present but may be empty, check out [this note on optional fields](#a-note-on-optional-fields). +> **Note** +> If you are attempting to validate a field that should always be present but may be empty, check out [this note on optional fields](#a-note-on-optional-fields). #### Complex Conditional Validation @@ -1654,7 +1665,8 @@ The first argument passed to the `sometimes` method is the name of the field we 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 under validation. +> **Note** +> 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. #### Complex Conditional Array Validation @@ -1809,7 +1821,8 @@ If your application accepts images uploaded by your users, you may use the `File ], ]); -> {tip} More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). +> **Note** +> More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). #### File Types @@ -2076,4 +2089,5 @@ To generate a new implicit rule object, you may use the `make:rule` Artisan comm php artisan make:rule Uppercase --invokable --implicit ``` -> {note} An "implicit" rule only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you. +> **Warning** +> 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 b3ed062aad3..f56f8ece89d 100644 --- a/verification.md +++ b/verification.md @@ -16,7 +16,8 @@ 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 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. +> **Note** +> 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 @@ -75,7 +76,8 @@ As mentioned previously, a route should be defined that will return a view instr The route that returns the email verification notice should be named `verification.notice`. It is important that the route is 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 the [Laravel application starter kits](/docs/{{version}}/starter-kits). +> **Note** +> 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 @@ -148,7 +150,8 @@ To get started, pass a closure to the `toMailUsing` method provided by the `Illu }); } -> {tip} To learn more about mail notifications, please consult the [mail notification documentation](/docs/{{version}}/notifications#mail-notifications). +> **Note** +> To learn more about mail notifications, please consult the [mail notification documentation](/docs/{{version}}/notifications#mail-notifications). ## Events diff --git a/views.md b/views.md index 0df245f8164..30b521b836c 100644 --- a/views.md +++ b/views.md @@ -32,7 +32,8 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return return view('greeting', ['name' => 'James']); }); -> {tip} Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. +> **Note** +> Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. ## Creating & Rendering Views @@ -60,7 +61,8 @@ Views may also be nested within subdirectories of the `resources/views` director return view('admin.profile', $data); -> {note} View directory names should not contain the `.` character. +> **Warning** +> View directory names should not contain the `.` character. ### Creating The First Available View @@ -177,7 +179,8 @@ We'll use the `View` facade's `composer` method to register the view composer. L } } -> {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. +> **Warning** +> 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 `compose` method of the `App\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: @@ -266,4 +269,3 @@ You may use the `view:clear` command to clear the view cache: ```shell php artisan view:clear ``` - diff --git a/vite.md b/vite.md index a03b35b3366..44512d1b356 100644 --- a/vite.md +++ b/vite.md @@ -30,7 +30,8 @@ Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production. -> {tip} Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). +> **Note** +> Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). #### Choosing Between Vite And Laravel Mix @@ -47,7 +48,8 @@ Have you started a new Laravel application using our Vite scaffolding but need t ## Installation & Setup -> {tip} The following documentation discusses how to manually install and configure the Laravel Vite plugin. However, Laravel's [starter kits](/docs/{{version}}/starter-kits) already include all of this scaffolding and are the fastest way to get started with Laravel and Vite. +> **Note** +> The following documentation discusses how to manually install and configure the Laravel Vite plugin. However, Laravel's [starter kits](/docs/{{version}}/starter-kits) already include all of this scaffolding and are the fastest way to get started with Laravel and Vite. ### Installing Node @@ -247,7 +249,8 @@ export default defineConfig({ }); ``` -> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Vue, and Vite. +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Vue, and Vite. ### React @@ -261,7 +264,8 @@ When using Vite with React, you will need to ensure that any files containing JS The `@viteReactRefresh` directive must be called before the `@vite` directive. -> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, React, and Vite. +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, React, and Vite. ### Inertia @@ -283,7 +287,8 @@ createInertiaApp({ }); ``` -> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. ### URL Processing @@ -455,7 +460,8 @@ npm run build node bootstrap/ssr/ssr.mjs ``` -> {tip} Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. ## Script & Style Tag Attributes @@ -580,4 +586,5 @@ Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, arr ]); ``` -> {note} The `$chunk` and `$manifest` arguments will be `null` while the Vite development server is running. +> **Warning** +> The `$chunk` and `$manifest` arguments will be `null` while the Vite development server is running. From 4a3dc71ccdee745ad2b3970b630bd84204c13b09 Mon Sep 17 00:00:00 2001 From: charlesbilbo Date: Fri, 5 Aug 2022 03:02:22 -0500 Subject: [PATCH 0313/2609] added documentation for the laravel record method --- http-client.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/http-client.md b/http-client.md index 3937c6f30b9..0e14815d301 100644 --- a/http-client.md +++ b/http-client.md @@ -512,6 +512,43 @@ Or, you may use the `assertNothingSent` method to assert that no requests were s Http::assertNothingSent(); +You may use the `recorded` method and capture all sent request. The `recorded` method returns a collection of arrays that contains instances of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response` + +```php + Http::fake( + [ + 'https://laravel.com/' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response() + ] + ); + + Http::get('https://laravel.com/'); + Http::get('https://nova.laravel.com/'); + + $recorded = Http::recorded(); +``` + +Additionally, the `recorded` method accepts a closure which will receive an instance of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response` and will filter out request that matches your expectations. + +```php + use Illuminate\Http\Client\Request; + use Illuminate\Http\Client\Response; + + Http::fake( + [ + 'https://laravel.com/' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response() + ] + ); + + Http::get('https://laravel.com/'); + Http::get('https://nova.laravel.com/'); + + $recorded = Http::recorded(function (Request $request, Response $response) { + return $request->url() !== 'https://laravel.com/' && $response->successful(); + }); +``` + ## Events From 7f328ecd07ff1d4b7d672d75b1d258ec01eeca9a Mon Sep 17 00:00:00 2001 From: charlesbilbo Date: Fri, 5 Aug 2022 03:06:39 -0500 Subject: [PATCH 0314/2609] added punctuation --- http-client.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-client.md b/http-client.md index 0e14815d301..c5053df0256 100644 --- a/http-client.md +++ b/http-client.md @@ -412,7 +412,7 @@ Sometimes you may need to specify that a single URL should return a series of fa ->pushStatus(404), ]); -When all of the responses in a response sequence have been consumed, any further requests will cause the response sequence to throw an exception. If you would like to specify a default response that should be returned when a sequence is empty, you may use the `whenEmpty` method: +When all the responses in a response sequence have been consumed, any further requests will cause the response sequence to throw an exception. If you would like to specify a default response that should be returned when a sequence is empty, you may use the `whenEmpty` method: Http::fake([ // Stub a series of responses for GitHub endpoints... @@ -512,7 +512,7 @@ Or, you may use the `assertNothingSent` method to assert that no requests were s Http::assertNothingSent(); -You may use the `recorded` method and capture all sent request. The `recorded` method returns a collection of arrays that contains instances of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response` +You may use the `recorded` method and capture all sent request. The `recorded` method returns a collection of arrays that contains instances of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response`. ```php Http::fake( From b29c6bd86ede9d318ba84106d0fb3eb97a0b631a Mon Sep 17 00:00:00 2001 From: itsMaati Date: Fri, 5 Aug 2022 18:32:11 +0430 Subject: [PATCH 0315/2609] Updated the bugfix branch (#8101) --- contributions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributions.md b/contributions.md index 938ca8daddf..fbe08faf9ab 100644 --- a/contributions.md +++ b/contributions.md @@ -75,7 +75,7 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest version that supports bug fixes (currently `8.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `9.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. **Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `9.x`). From e3f4f044d41d73499d224c3ab5a1002efe2a2eb4 Mon Sep 17 00:00:00 2001 From: Ryuta Hamasaki Date: Fri, 5 Aug 2022 22:02:48 +0800 Subject: [PATCH 0316/2609] fix config option name for s3 (#8100) --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index aa58d77ba52..f1304477a2d 100644 --- a/filesystem.md +++ b/filesystem.md @@ -147,7 +147,7 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu By default, your application's `filesystems` configuration file contains a disk configuration for the `s3` disk. In addition to using this disk to interact with Amazon S3, you may use it to interact with any S3 compatible file storage service such as [MinIO](https://github.com/minio/minio) or [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/). -Typically, after updating the disk's credentials to match the credentials of the service you are planning to use, you only need to update the value of the `url` configuration option. This option's value is typically defined via the `AWS_ENDPOINT` environment variable: +Typically, after updating the disk's credentials to match the credentials of the service you are planning to use, you only need to update the value of the `endpoint` configuration option. This option's value is typically defined via the `AWS_ENDPOINT` environment variable: 'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000/'), From dabafb0206ad47d992f5c53c3fbb6910ae32966b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 5 Aug 2022 09:35:20 -0500 Subject: [PATCH 0317/2609] formatting --- http-client.md | 60 ++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/http-client.md b/http-client.md index c5053df0256..94b73230b83 100644 --- a/http-client.md +++ b/http-client.md @@ -512,41 +512,43 @@ Or, you may use the `assertNothingSent` method to assert that no requests were s Http::assertNothingSent(); -You may use the `recorded` method and capture all sent request. The `recorded` method returns a collection of arrays that contains instances of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response`. + +#### Recording Requests / Responses + +You may use the `recorded` method to gather all requests and their corresponding responses. The `recorded` method returns a collection of arrays that contains instances of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response`: ```php - Http::fake( - [ - 'https://laravel.com/' => Http::response(status: 500), - 'https://nova.laravel.com/' => Http::response() - ] - ); - - Http::get('https://laravel.com/'); - Http::get('https://nova.laravel.com/'); - - $recorded = Http::recorded(); +Http::fake([ + 'https://laravel.com/' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response(), +]); + +Http::get('https://laravel.com/'); +Http::get('https://nova.laravel.com/'); + +$recorded = Http::recorded(); + +[$request, $response] = $recorded[0]; ``` -Additionally, the `recorded` method accepts a closure which will receive an instance of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response` and will filter out request that matches your expectations. +Additionally, the `recorded` method accepts a closure which will receive an instance of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response` and may be used to filter request / response pairs based on your expectations: ```php - use Illuminate\Http\Client\Request; - use Illuminate\Http\Client\Response; - - Http::fake( - [ - 'https://laravel.com/' => Http::response(status: 500), - 'https://nova.laravel.com/' => Http::response() - ] - ); - - Http::get('https://laravel.com/'); - Http::get('https://nova.laravel.com/'); - - $recorded = Http::recorded(function (Request $request, Response $response) { - return $request->url() !== 'https://laravel.com/' && $response->successful(); - }); +use Illuminate\Http\Client\Request; +use Illuminate\Http\Client\Response; + +Http::fake([ + 'https://laravel.com/' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response(), +]); + +Http::get('https://laravel.com/'); +Http::get('https://nova.laravel.com/'); + +$recorded = Http::recorded(function (Request $request, Response $response) { + return $request->url() !== 'https://laravel.com/' && + $response->successful(); +}); ``` From 907ca36d39ec2d074b08d472c0a75a131578d433 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Mon, 8 Aug 2022 23:21:46 +1000 Subject: [PATCH 0318/2609] Improve shell alias docs (#8105) --- sail.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sail.md b/sail.md index 333e8f0fa69..532a6d648b9 100644 --- a/sail.md +++ b/sail.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Installation & Setup](#installation) - [Installing Sail Into Existing Applications](#installing-sail-into-existing-applications) - - [Configuring A Bash Alias](#configuring-a-bash-alias) + - [Configuring A Shell Alias](#configuring-a-shell-alias) - [Starting & Stopping Sail](#starting-and-stopping-sail) - [Executing Commands](#executing-sail-commands) - [Executing PHP Commands](#executing-php-commands) @@ -71,8 +71,8 @@ If you would like to develop within a [Devcontainer](https://code.visualstudio.c php artisan sail:install --devcontainer ``` - -### Configuring A Bash Alias + +### Configuring A Shell Alias By default, Sail commands are invoked using the `vendor/bin/sail` script that is included with all new Laravel applications: @@ -80,13 +80,15 @@ By default, Sail commands are invoked using the `vendor/bin/sail` script that is ./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: +However, instead of repeatedly typing `vendor/bin/sail` to execute Sail commands, you may wish to configure a shell alias that allows you to execute Sail's commands more easily: ```shell -alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail' +alias sail='[ -f sail ] && sh sail || sh 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: +To make sure this is always available, you may add this to your shell configuration file in your home directory, such as `~/.zshrc` or `~/.bashrc`, and then restart your shell. + +Once the shell 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: ```shell sail up From d8fbee08e9104c0680ab51e0a08020815541e00f Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 8 Aug 2022 21:56:54 +0200 Subject: [PATCH 0319/2609] [9.x] Document MinIO limitation with temporaryUrl (#8107) * Update sail.md * Update sail.md Co-authored-by: Taylor Otwell --- sail.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sail.md b/sail.md index 532a6d648b9..8166f1e8717 100644 --- a/sail.md +++ b/sail.md @@ -251,6 +251,9 @@ AWS_URL=http://localhost:9000/local You may create buckets via the MinIO console, which is available at `http://localhost:8900`. The default username for the MinIO console is `sail` while the default password is `password`. +> **Warning** +> Generating temporary storage URLs via the `temporaryUrl` method is not supported when using MinIO. + ## Running Tests From 6d8a96b25c523f6679ab86edbd2ffe2416dcd924 Mon Sep 17 00:00:00 2001 From: Iman Date: Tue, 9 Aug 2022 00:28:12 +0430 Subject: [PATCH 0320/2609] [9.x] Add docs for eloquent quietly methods (#8108) * add docs for eloquent quietly methods * Update eloquent.md * Update eloquent.md Co-authored-by: Taylor Otwell --- eloquent.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eloquent.md b/eloquent.md index a1adf005df9..b0630d6a1c2 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1519,3 +1519,9 @@ Sometimes you may wish to "save" a given model without dispatching any events. Y $user->name = 'Victoria Faith'; $user->saveQuietly(); + +You may also "update", "delete", "soft delete", "restore", and "replicate" a given model without dispatching any events: + + $user->deleteQuietly(); + + $user->restoreQuietly(); From 96707ef6619f67e9934d42ee5c84119b91913a95 Mon Sep 17 00:00:00 2001 From: Josh S Date: Tue, 9 Aug 2022 08:16:31 -0500 Subject: [PATCH 0321/2609] keyby callback has key param (#8113) Co-authored-by: Joshua Schuler --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index c7cee482bc7..06b7f4c3ac2 100644 --- a/collections.md +++ b/collections.md @@ -1228,7 +1228,7 @@ The `keyBy` method keys the collection by the given key. If multiple items have You may also pass a callback to the method. The callback should return the value to key the collection by: - $keyed = $collection->keyBy(function ($item) { + $keyed = $collection->keyBy(function ($item, $key) { return strtoupper($item['product_id']); }); From a06206f15b5ea71d3a6523358471dbb411469efc Mon Sep 17 00:00:00 2001 From: Nikolay Nikolaev Date: Tue, 9 Aug 2022 15:17:29 +0200 Subject: [PATCH 0322/2609] Simplify sentence (#8112) --- structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure.md b/structure.md index 6b10eaa061b..a7cf692f50b 100644 --- a/structure.md +++ b/structure.md @@ -76,7 +76,7 @@ The `resources` directory contains your [views](/docs/{{version}}/views) as well 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 then it is likely that 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 all 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. 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. From d40147230b67713f8b9c849738bfa95f4e3549aa Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 9 Aug 2022 06:24:45 -0700 Subject: [PATCH 0323/2609] Mention pusher/pusher-php-server upgrade from ^4.0|^5.0 to ^5.0 in upgrade guide (#8110) * docs: mention pusher/pusher-php-server upgrade from ^4.0|^5.0 to ^5.0 See https://github.com/laravel/framework/pull/36528 * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index a455de415d0..a2ea821fbea 100644 --- a/upgrade.md +++ b/upgrade.md @@ -60,7 +60,7 @@ You should update the following dependencies in your application's `composer.jso
-In addition, please replace `facade/ignition` with `"spatie/laravel-ignition": "^1.0"` in your application's `composer.json` file. +In addition, please replace `facade/ignition` with `"spatie/laravel-ignition": "^1.0"` and `pusher/pusher-php-server` (if applicable) with `"pusher/pusher-php-server": "^5.0"` in your application's `composer.json` file. Furthermore, the following first-party packages have received new major releases to support Laravel 9.x. If applicable, you should read their individual upgrade guides before upgrading: From bba5ee36ee941f4486ce6df0c812fe13957251a6 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 9 Aug 2022 15:33:10 +0200 Subject: [PATCH 0324/2609] [9.x] Document combining global scopes with Scout (#8104) * Update scout.md * Update scout.md Co-authored-by: Taylor Otwell --- scout.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scout.md b/scout.md index 5e8a83afd5d..3686005d7ad 100644 --- a/scout.md +++ b/scout.md @@ -554,6 +554,9 @@ Of course, if you would like to retrieve the pagination results as JSON, you may return Order::search($request->input('query'))->paginate(15); }); +> **Warning** +> Since search engines are not aware of your Eloquent model's global scope definitions, you should not utilize global scopes in applications that utilize Scout pagination. Or, you should recreate the global scope's constraints when searching via Scout. + ### Soft Deleting From 5ed2c52e42e0a7a1933c7b6fac78c1da008b41aa Mon Sep 17 00:00:00 2001 From: kichetof Date: Tue, 9 Aug 2022 15:50:23 +0200 Subject: [PATCH 0325/2609] Added doc for validation rules `doesnt_end_with` and `doesnt_start_with` (#8095) --- validation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/validation.md b/validation.md index c1648a48b36..10c62f0f70d 100644 --- a/validation.md +++ b/validation.md @@ -823,6 +823,8 @@ Below is a list of all available validation rules and their function: [Digits Between](#rule-digits-between) [Dimensions (Image Files)](#rule-dimensions) [Distinct](#rule-distinct) +[Doesnt End With](#rule-doesnt-end-with) +[Doesnt Start With](#rule-doesnt-start-with) [Email](#rule-email) [Ends With](#rule-ends-with) [Enum](#rule-enum) @@ -1073,6 +1075,16 @@ You may add `ignore_case` to the validation rule's arguments to make the rule ig 'foo.*.id' => 'distinct:ignore_case' + +#### doesnt_end_with:_foo_,_bar_,... + +The field under validation must not end with one of the given values. + + +#### doesnt_start_with:_foo_,_bar_,... + +The field under validation must not start with one of the given values. + #### email From 574147fec104d5b3e2377484fa2cc5d2269179b5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 9 Aug 2022 08:50:46 -0500 Subject: [PATCH 0326/2609] wip --- validation.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/validation.md b/validation.md index 10c62f0f70d..d2f0e4b4940 100644 --- a/validation.md +++ b/validation.md @@ -823,8 +823,8 @@ Below is a list of all available validation rules and their function: [Digits Between](#rule-digits-between) [Dimensions (Image Files)](#rule-dimensions) [Distinct](#rule-distinct) -[Doesnt End With](#rule-doesnt-end-with) [Doesnt Start With](#rule-doesnt-start-with) +[Doesnt End With](#rule-doesnt-end-with) [Email](#rule-email) [Ends With](#rule-ends-with) [Enum](#rule-enum) @@ -1075,16 +1075,16 @@ You may add `ignore_case` to the validation rule's arguments to make the rule ig 'foo.*.id' => 'distinct:ignore_case' - -#### doesnt_end_with:_foo_,_bar_,... - -The field under validation must not end with one of the given values. - #### doesnt_start_with:_foo_,_bar_,... The field under validation must not start with one of the given values. + +#### doesnt_end_with:_foo_,_bar_,... + +The field under validation must not end with one of the given values. + #### email From 432342825e5d27ec6ef945cdfe33f93c136c6c6b Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 11 Aug 2022 14:20:59 +0200 Subject: [PATCH 0327/2609] Update database.md --- database.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.md b/database.md index 267f8cd3a99..0066f6d8e96 100644 --- a/database.md +++ b/database.md @@ -17,7 +17,7 @@ Almost every modern web application interacts with a database. Laravel makes int
-- MariaDB 10.2+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) +- MariaDB 10.3+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) - MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) - PostgreSQL 10.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) - SQLite 3.8.8+ From 55d60ee153f818c0ebbcd6f9506256777f644ba4 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 12 Aug 2022 03:05:28 +1000 Subject: [PATCH 0328/2609] promote lazy about command registration in boot function (#8116) --- packages.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/packages.md b/packages.md index b3eead64f93..427459334e9 100644 --- a/packages.md +++ b/packages.md @@ -314,27 +314,20 @@ If your package contains anonymous components, they must be placed within a `com ### "About" Artisan Command -Laravel's built-in `about` Artisan command provides a synopsis of the application's environment and configuration. Packages may push additional information to this command's output via the `AboutCommand` class. Typically, this information may be added from your package service provider's `register` method: +Laravel's built-in `about` Artisan command provides a synopsis of the application's environment and configuration. Packages may push additional information to this command's output via the `AboutCommand` class. Typically, this information may be added from your package service provider's `boot` method: use Illuminate\Foundation\Console\AboutCommand; /** - * Register any application services. + * Bootstrap any application services. * * @return void */ - public function register() + public function boot() { - AboutCommand::add('My Package', 'Version', '1.0.0'); + AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']); } -The `about` command's values may also be provided a closures if deferred execution is desirable: - - AboutCommand::add('My Package', [ - 'Version' => '1.0.0', - 'Driver' => fn () => config('my-package.driver'), - ]); - ## Commands From c526faec2adfa6c381640fe60b826f508d98fd67 Mon Sep 17 00:00:00 2001 From: Iman Date: Fri, 12 Aug 2022 21:32:01 +0430 Subject: [PATCH 0329/2609] [9.x] Adds collection comparison operators list (#8121) * Adds collection comparison operators list * Update collections.md Co-authored-by: Taylor Otwell --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index 06b7f4c3ac2..b45773e7c9d 100644 --- a/collections.md +++ b/collections.md @@ -3036,7 +3036,7 @@ The `where` method filters the collection by a given key / value pair: The `where` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`whereStrict`](#method-wherestrict) method to filter using "strict" comparisons. -Optionally, you may pass a comparison operator as the second parameter. +Optionally, you may pass a comparison operator as the second parameter. Supported operators are: '===', '!==', '!=', '==', '=', '<>', '>', '<', '>=', and '<=': $collection = collect([ ['name' => 'Jim', 'deleted_at' => '2019-01-01 00:00:00'], From 404711c650b5fc1b5e7623e08cc5bbfc62679f75 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 12 Aug 2022 12:05:32 -0500 Subject: [PATCH 0330/2609] move Docker to "Included Software" (#8119) Docker was moved to the base box on `laravel/settler` and can no longer be installed as optional software. https://github.com/laravel/settler/commit/8b1f706f507d2be1b019d178955bb6b98e996582 https://github.com/laravel/homestead/pull/1716 --- homestead.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homestead.md b/homestead.md index 4e746249e47..c116c301be0 100644 --- a/homestead.md +++ b/homestead.md @@ -74,6 +74,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Sqlite3 - PostgreSQL 13 - Composer +- Docker - Node (With Yarn, Bower, Grunt, and Gulp) - Redis - Memcached @@ -106,7 +107,6 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Chronograf - CouchDB - Crystal & Lucky Framework -- Docker - Elasticsearch - EventStoreDB - Gearman @@ -330,7 +330,6 @@ features: - chronograf: true - couchdb: true - crystal: true - - docker: true - elasticsearch: version: 7.9.0 - eventstore: true From c6512cf702a513debabbb615e57e047a6ca543d5 Mon Sep 17 00:00:00 2001 From: Toby Evans Date: Sat, 13 Aug 2022 05:08:29 +1200 Subject: [PATCH 0331/2609] Document faking a sub-set of jobs (#8117) * Document faking a sub-set of jobs * Update mocking.md * Update mocking.md Co-authored-by: Taylor Otwell --- mocking.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mocking.md b/mocking.md index b03c6bf4e8f..46a71b74865 100644 --- a/mocking.md +++ b/mocking.md @@ -553,6 +553,20 @@ You may pass a closure to the `assertPushed` or `assertNotPushed` methods in ord return $job->order->id === $order->id; }); +If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `fake` method: + + public function test_orders_can_be_shipped() + { + Queue::fake([ + ShipOrder::class, + ]); + + // Perform order shipping... + + // Assert a job was pushed twice... + Queue::assertPushed(ShipOrder::class, 2); + } + ### Job Chains From eb100b1e12870131e140497a0ca4e8f6871698a0 Mon Sep 17 00:00:00 2001 From: Josh S Date: Mon, 15 Aug 2022 09:22:55 -0500 Subject: [PATCH 0332/2609] Collections implode function supports callback (#8123) * Collections implode function supports callback * Update collections.md Co-authored-by: Joshua Schuler Co-authored-by: Taylor Otwell --- collections.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/collections.md b/collections.md index b45773e7c9d..de696d84ab0 100644 --- a/collections.md +++ b/collections.md @@ -1143,6 +1143,14 @@ If the collection contains simple strings or numeric values, you should pass the // '1-2-3-4-5' +You may pass a closure to the `implode` method if you would like to format the values being imploded: + + $collection->implode(function ($item, $key) { + return strtoupper($item['product']); + }, ', '); + + // DESK, CHAIR + #### `intersect()` {.collection-method} From aae906be31ec51827e7fb116f1898867e005f1af Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 16 Aug 2022 00:27:36 +1000 Subject: [PATCH 0333/2609] [9.x] Document `withoutVite` method (#8122) * Document `withoutVite` method * formatting Co-authored-by: Taylor Otwell --- vite.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/vite.md b/vite.md index 44512d1b356..89f7536fa04 100644 --- a/vite.md +++ b/vite.md @@ -17,6 +17,7 @@ - [Working With Blade & Routes](#working-with-blade-and-routes) - [Custom Base URLs](#custom-base-urls) - [Environment Variables](#environment-variables) +- [Disabling Vite In Tests](#disabling-vite-in-tests) - [Server-Side Rendering (SSR)](#ssr) - [Script & Style Tag Attributes](#script-and-style-attributes) - [Content Security Policy (CSP) Nonce](#content-security-policy-csp-nonce) @@ -424,6 +425,49 @@ You may access injected environment variables via the `import.meta.env` object: import.meta.env.VITE_SENTRY_DSN_PUBLIC ``` + +## Disabling Vite In Tests + +Laravel's Vite integration will attempt to resolve your assets while running your tests, which requires you to either run the Vite development server or build your assets. + +If you would prefer to mock Vite during testing, you may call the `withoutVite` method, which is is available for any tests that extend Laravel's `TestCase` class: + +```php +use Tests\TestCase; + +class ExampleTest extends TestCase +{ + public function test_without_vite_example() + { + $this->withoutVite(); + + // ... + } +} +``` + +If you would like to disable Vite for all tests, you may call the `withoutVite` method from the `setUp` method on your base `TestCase` class: + +```php +withoutVite(); + }// [tl! add:end] +} +``` + ## Server-Side Rendering (SSR) From f9cf314af149c3e608301e6393c7c37488c9d922 Mon Sep 17 00:00:00 2001 From: James Brooks Date: Mon, 15 Aug 2022 19:58:41 +0100 Subject: [PATCH 0334/2609] [9.x] Document new `db` commands (#8114) * Document `db:monitor` command * Document `db:show` and `db:table` * Update database.md Co-authored-by: Anjorin Damilare * Update database.md Co-authored-by: Anjorin Damilare * Update database.md Co-authored-by: Anjorin Damilare * Update database.md Co-authored-by: Anjorin Damilare * formatting Co-authored-by: Joe Dixon Co-authored-by: Taylor Otwell Co-authored-by: Anjorin Damilare --- database.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/database.md b/database.md index 0066f6d8e96..073f40e3ff4 100644 --- a/database.md +++ b/database.md @@ -9,6 +9,8 @@ - [Monitoring Cumulative Query Time](#monitoring-cumulative-query-time) - [Database Transactions](#database-transactions) - [Connecting To The Database CLI](#connecting-to-the-database-cli) +- [Inspecting Your Databases](#inspecting-your-databases) +- [Monitoring Your Databases](#monitoring-your-databases) ## Introduction @@ -376,3 +378,69 @@ If needed, you may specify a database connection name to connect to a database c ```shell php artisan db mysql ``` + + +## Inspecting Your Databases + +Using the `db:show` and `db:table` Artisan commands, you can get valuable insight into your database and its associated tables. To see an overview of your database, including its size, type, number of open connections, and a summary of its tables, you may use the `db:show` command: + +```shell +php artisan db:show +``` + +You may specify which database connection should be inspected by providing the database connection name to the command via the `--database` option: + +```shell +php artisan db:show --database=pgsql +``` + +If you would like to include table row counts and database view details within the output of the command, you may provide the `--counts` and `--views` options, respectively. On large databases, retrieving row counts and view details can be slow: + +```shell +php artisan db:show --counts --views +``` + + +#### Table Overview + +If you would like to get an overview of an individual table within your database, you may execute the `db:table` Artisan command. This command provides a general overview of a database table, including its columns, types, attributes, keys, and indexes: + +```shell +php artisan db:table users +``` + + +## Monitoring Your Databases + +Using the `db:monitor` Artisan command, you can instruct Laravel to dispatch an `Illuminate\Database\Events\DatabaseBusy` event if your database is managing more than a specified number of open connections. + +To get started, you should schedule the `db:monitor` command to [run every minute](/docs/{{version}}/scheduling). The command accepts the names of the database connection configurations that you wish to monitor as well as the maximum number of open connections that should be tolerated before dispatching an event: + +```shell +php artisan db:monitor --databases=mysql,pgsql --max=100 +``` + +Scheduling this command alone is not enough to trigger a notification alerting you of the number of open connections. When the command encounters a database that has a open connection count that exceeds your threshold, a `DatabaseBusy` event will be dispatched. You should listen for this event within your application's `EventServiceProvider` in order to send a notification to you or your development team: + +```php +use App\Notifications\DatabaseApproachingMaxConnections; +use Illuminate\Database\Events\DatabaseBusy; +use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\Notification; + +/** + * Register any other events for your application. + * + * @return void + */ +public function boot() +{ + Event::listen(function (DatabaseBusy $event) { + Notification::route('mail', 'dev@example.com') + ->notify(new DatabaseApproachingMaxConnections( + $event->connectionName, + $event->connections + )); + }); +} +``` From c74b7673d0447c2e60c580b95f83022882bf4b17 Mon Sep 17 00:00:00 2001 From: Ashley Shenton Date: Mon, 15 Aug 2022 20:07:22 +0100 Subject: [PATCH 0335/2609] [9.x] Add `withWhereHas` documentation to Eloquent (#8115) * docs: add withWhereHas documentation to eloquent * formatting Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 2cf66037930..6df7a27fb5a 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1660,6 +1660,17 @@ If you are eager loading a `morphTo` relationship, Eloquent will run multiple qu In this example, Eloquent will only eager load posts that have not been hidden and videos that have a `type` value of "educational". + +#### Constraining Eager Loads With Relationship Existence + +You may sometimes find yourself needing to check for the existence of a relationship while simultaneously loading the relationship based on the same conditions. For example, you may wish to only retrieve `User` models that have child `Post` models matching a given query condition while also eager loading the matching posts. You may accomplish this using the `withWhereHas` method: + + use App\Models\User; + + $users = User::withWhereHas('posts', function ($query) { + $query->where('featured', true); + )->get(); + ### Lazy Eager Loading From 1fb02fd1d80d227489e1c9d647516b79462f1e64 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 Aug 2022 10:35:42 -0500 Subject: [PATCH 0336/2609] wip --- helpers.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/helpers.md b/helpers.md index 516c8de92c5..fa7c7a1840e 100644 --- a/helpers.md +++ b/helpers.md @@ -244,6 +244,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [whenStartsWith](#method-fluent-str-when-starts-with) [whenEndsWith](#method-fluent-str-when-ends-with) [whenExactly](#method-fluent-str-when-exactly) +[whenNotExactly](#method-fluent-str-when-not-exactly) [whenIs](#method-fluent-str-when-is) [whenIsAscii](#method-fluent-str-when-is-ascii) [whenIsUuid](#method-fluent-str-when-is-uuid) @@ -3166,6 +3167,19 @@ The `whenExactly` method invokes the given closure if the string exactly matches // 'Laravel' + +#### `whenNotExactly` {.collection-method} + +The `whenNotExactly` method invokes the given closure if the string does not exactly match the given string. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + + $string = Str::of('framework')->whenNotExactly('laravel', function ($string) { + return $string->title(); + }); + + // 'Framework' + #### `whenIs` {.collection-method} From 9560242674b70c2dc1f3fc37d719b1c102bf7bf6 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 17 Aug 2022 23:29:12 +1000 Subject: [PATCH 0337/2609] [9.x] Update docs regarding `sail up` time (#8131) * Update docs regarding `sail up` time * formatting Co-authored-by: Taylor Otwell --- installation.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/installation.md b/installation.md index f6f712f6d5a..17d90492247 100644 --- a/installation.md +++ b/installation.md @@ -93,6 +93,8 @@ curl -s "https://laravel.build/example-app" | bash Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. +Sail installation may take several minutes while Sail's application containers are built on your local machine. + After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```shell @@ -101,8 +103,6 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** - Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. > **Note** @@ -124,6 +124,8 @@ curl -s https://laravel.build/example-app | bash Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. +Sail installation may take several minutes while Sail's application containers are built on your local machine. + After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```shell @@ -132,8 +134,6 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** - Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. > **Note** @@ -156,6 +156,8 @@ curl -s https://laravel.build/example-app | bash Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. +Sail installation may take several minutes while Sail's application containers are built on your local machine. + After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```shell @@ -164,8 +166,6 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** - Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. > **Note** From b6a26a9383795988d2ec69d37fd7ee13cd8934d3 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 17 Aug 2022 23:45:33 +1000 Subject: [PATCH 0338/2609] [9.x] Including static assets in Vite builds (#8124) * add static asset handling to blade section * update TOC * wording * wording * wording * wording * formatting Co-authored-by: Taylor Otwell --- vite.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/vite.md b/vite.md index 89f7536fa04..10643dd3cd8 100644 --- a/vite.md +++ b/vite.md @@ -15,6 +15,8 @@ - [URL Processing](#url-processing) - [Working With Stylesheets](#working-with-stylesheets) - [Working With Blade & Routes](#working-with-blade-and-routes) + - [Processing Static Assets With Vite](#blade-processing-static-assets) + - [Refreshing On Save](#blade-refreshing-on-save) - [Custom Base URLs](#custom-base-urls) - [Environment Variables](#environment-variables) - [Disabling Vite In Tests](#disabling-vite-in-tests) @@ -338,6 +340,29 @@ module.exports = { ## Working With Blade & Routes + +### Processing Static Assets With Vite + +When referencing assets in your JavaScript or CSS, Vite automatically processes and versions them. In addition, when building Blade based applications, Vite can also process and version static assets that you reference solely in Blade templates. + +However, in order to accomplish this, you need to make Vite aware of your assets by importing the static assets into the application's entry point. For example, if you want to process and version all images stored in `resources/images` and all fonts stored in `resources/fonts`, you should add the following in your application's `resources/js/app.js` entry point: + +```js +import.meta.glob([ + '../images/**', + '../fonts/**', +]); +``` + +These assets will now be processed by Vite when running `npm run build`. You can then reference these assets in Blade templates using the `Vite::asset` method, which will return the versioned URL for a given asset: + +```blade + +``` + + +### Refreshing On Save + When your application is built using traditional server-side rendering with Blade, Vite can improve your development workflow by automatically refreshing the browser when you make changes to view files in your application. To get started, you can simply specify the `refresh` option as `true`. ```js From c434a16887d6408f58866473d67d628bf45ddff2 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 17 Aug 2022 23:49:32 +1000 Subject: [PATCH 0339/2609] [9.x] Separate Eloquent factory docs (#8118) * Move Eloquent factories documentation to a dedicated page * Add a section about model factories to the database testing docs * Tweak the factories wording to better suit being on a dedicated page * formatting * Fix callouts Co-authored-by: Taylor Otwell --- database-testing.md | 492 +----------------------------------------- documentation.md | 1 + eloquent-factories.md | 489 +++++++++++++++++++++++++++++++++++++++++ http-tests.md | 2 +- seeding.md | 4 +- 5 files changed, 499 insertions(+), 489 deletions(-) create mode 100644 eloquent-factories.md diff --git a/database-testing.md b/database-testing.md index 904b2669802..2ecdfd98d38 100644 --- a/database-testing.md +++ b/database-testing.md @@ -2,21 +2,7 @@ - [Introduction](#introduction) - [Resetting The Database After Each Test](#resetting-the-database-after-each-test) -- [Defining Model Factories](#defining-model-factories) - - [Concept Overview](#concept-overview) - - [Generating Factories](#generating-factories) - - [Factory States](#factory-states) - - [Factory Callbacks](#factory-callbacks) -- [Creating Models Using Factories](#creating-models-using-factories) - - [Instantiating Models](#instantiating-models) - - [Persisting Models](#persisting-models) - - [Sequences](#sequences) -- [Factory Relationships](#factory-relationships) - - [Has Many Relationships](#has-many-relationships) - - [Belongs To Relationships](#belongs-to-relationships) - - [Many To Many Relationships](#many-to-many-relationships) - - [Polymorphic Relationships](#polymorphic-relationships) - - [Defining Relationships Within Factories](#defining-relationships-within-factories) +- [Model Factories](#model-factories) - [Running Seeders](#running-seeders) - [Available Assertions](#available-assertions) @@ -59,486 +45,20 @@ The `Illuminate\Foundation\Testing\RefreshDatabase` trait does not migrate your If you would like to totally reset the database using migrations, you may use the `Illuminate\Foundation\Testing\DatabaseMigrations` trait instead. However, the `DatabaseMigrations` trait is significantly slower than the `RefreshDatabase` trait. - -## Defining Model Factories + +## Model Factories - -### Concept Overview +When testing, you may need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a set of default attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using [model factories](/docs/{{version}}/eloquent-factories). -First, let's talk about Eloquent model factories. When testing, you may need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a set of default attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using model factories. - -To see an example of how to write a factory, take a look at the `database/factories/UserFactory.php` file in your application. This factory is included with all new Laravel applications and contains the following factory definition: - - namespace Database\Factories; - - use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Str; - - class UserFactory extends Factory - { - /** - * Define the model's default state. - * - * @return array - */ - public function definition() - { - return [ - 'name' => fake()->name(), - 'email' => fake()->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password - 'remember_token' => Str::random(10), - ]; - } - } - -As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. - -Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing. - -> **Note** -> You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. - - -### Generating Factories - -To create a factory, execute the `make:factory` [Artisan command](/docs/{{version}}/artisan): - -```shell -php artisan make:factory PostFactory -``` - -The new factory class will be placed in your `database/factories` directory. - - -#### Model & Factory Discovery Conventions - -Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. - -The `HasFactory` trait's `factory` method will use conventions to determine the proper factory for the model the trait is assigned to. Specifically, the method will look for a factory in the `Database\Factories` namespace that has a class name matching the model name and is suffixed with `Factory`. If these conventions do not apply to your particular application or factory, you may overwrite the `newFactory` method on your model to return an instance of the model's corresponding factory directly: - - use Database\Factories\Administration\FlightFactory; - - /** - * Create a new factory instance for the model. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory - */ - protected static function newFactory() - { - return FlightFactory::new(); - } - -Next, define a `model` property on the corresponding factory: - - use App\Administration\Flight; - use Illuminate\Database\Eloquent\Factories\Factory; - - class FlightFactory extends Factory - { - /** - * The name of the factory's corresponding model. - * - * @var string - */ - protected $model = Flight::class; - } - - -### Factory States - -State manipulation methods allow you to define discrete modifications that can be applied to your model factories in any combination. For example, your `Database\Factories\UserFactory` factory might contain a `suspended` state method that modifies one of its default attribute values. - -State transformation methods typically call the `state` method provided by Laravel's base factory class. The `state` method accepts a closure which will receive the array of raw attributes defined for the factory and should return an array of attributes to modify: - - /** - * Indicate that the user is suspended. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory - */ - public function suspended() - { - return $this->state(function (array $attributes) { - return [ - 'account_status' => 'suspended', - ]; - }); - } - -#### "Trashed" State - -If your Eloquent model can be [soft deleted](/docs/{{version}}/eloquent#soft-deleting), you may invoke the built-in `trashed` state method to indicate that the created model should already be "soft deleted". You do not need to manually define the `trashed` state as it is automatically available to all factories: - - use App\Models\User; - - $user = User::factory()->trashed()->create(); - - -### Factory Callbacks - -Factory callbacks are registered using the `afterMaking` and `afterCreating` methods and allow you to perform additional tasks after making or creating a model. You should register these callbacks by defining a `configure` method on your factory class. This method will be automatically called by Laravel when the factory is instantiated: - - namespace Database\Factories; - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Str; - - class UserFactory extends Factory - { - /** - * Configure the model factory. - * - * @return $this - */ - public function configure() - { - return $this->afterMaking(function (User $user) { - // - })->afterCreating(function (User $user) { - // - }); - } - - // ... - } - - -## Creating Models Using Factories - - -### Instantiating Models - -Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. Let's take a look at a few examples of creating models. First, we'll use the `make` method to create models without persisting them to the database: +To learn more about creating and utilizing model factories to create models, please consult the complete [model factory documentation](/docs/{{version}}/eloquent-factories). Once you have defined a model factory, you may utilize the factory within your test to create models: use App\Models\User; public function test_models_can_be_instantiated() { - $user = User::factory()->make(); - - // Use model in tests... - } - -You may create a collection of many models using the `count` method: - - $users = User::factory()->count(3)->make(); - - -#### Applying States - -You may also apply any of your [states](#factory-states) to the models. If you would like to apply multiple state transformations to the models, you may simply call the state transformation methods directly: - - $users = User::factory()->count(5)->suspended()->make(); - - -#### Overriding Attributes - -If you would like to override some of the default values of your models, you may pass an array of values to the `make` method. Only the specified attributes will be replaced while the rest of the attributes remain set to their default values as specified by the factory: - - $user = User::factory()->make([ - 'name' => 'Abigail Otwell', - ]); - -Alternatively, the `state` method may be called directly on the factory instance to perform an inline state transformation: - - $user = User::factory()->state([ - 'name' => 'Abigail Otwell', - ])->make(); - -> **Note** -> [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. - - -### Persisting Models - -The `create` method instantiates model instances and persists them to the database using Eloquent's `save` method: - - use App\Models\User; - - public function test_models_can_be_persisted() - { - // Create a single App\Models\User instance... $user = User::factory()->create(); - // Create three App\Models\User instances... - $users = User::factory()->count(3)->create(); - - // Use model in tests... - } - -You may override the factory's default model attributes by passing an array of attributes to the `create` method: - - $user = User::factory()->create([ - 'name' => 'Abigail', - ]); - - -### Sequences - -Sometimes you may wish to alternate the value of a given model attribute for each created model. You may accomplish this by defining a state transformation as a sequence. For example, you may wish to alternate the value of an `admin` column between `Y` and `N` for each created user: - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Sequence; - - $users = User::factory() - ->count(10) - ->state(new Sequence( - ['admin' => 'Y'], - ['admin' => 'N'], - )) - ->create(); - -In this example, five users will be created with an `admin` value of `Y` and five users will be created with an `admin` value of `N`. - -If necessary, you may include a closure as a sequence value. The closure will be invoked each time the sequence needs a new value: - - $users = User::factory() - ->count(10) - ->state(new Sequence( - fn ($sequence) => ['role' => UserRoles::all()->random()], - )) - ->create(); - -Within a sequence closure, you may access the `$index` or `$count` properties on the sequence instance that is injected into the closure. The `$index` property contains the number of iterations through the sequence that have occurred thus far, while the `$count` property contains the total number of times the sequence will be invoked: - - $users = User::factory() - ->count(10) - ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) - ->create(); - - -## Factory Relationships - - -### Has Many Relationships - -Next, let's explore building Eloquent model relationships using Laravel's fluent factory methods. First, let's assume our application has an `App\Models\User` model and an `App\Models\Post` model. Also, let's assume that the `User` model defines a `hasMany` relationship with `Post`. We can create a user that has three posts using the `has` method provided by the Laravel's factories. The `has` method accepts a factory instance: - - use App\Models\Post; - use App\Models\User; - - $user = User::factory() - ->has(Post::factory()->count(3)) - ->create(); - -By convention, when passing a `Post` model to the `has` method, Laravel will assume that the `User` model must have a `posts` method that defines the relationship. If necessary, you may explicitly specify the name of the relationship that you would like to manipulate: - - $user = User::factory() - ->has(Post::factory()->count(3), 'posts') - ->create(); - -Of course, you may perform state manipulations on the related models. In addition, you may pass a closure based state transformation if your state change requires access to the parent model: - - $user = User::factory() - ->has( - Post::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['user_type' => $user->type]; - }) - ) - ->create(); - - -#### Using Magic Methods - -For convenience, you may use Laravel's magic factory relationship methods to build relationships. For example, the following example will use convention to determine that the related models should be created via a `posts` relationship method on the `User` model: - - $user = User::factory() - ->hasPosts(3) - ->create(); - -When using magic methods to create factory relationships, you may pass an array of attributes to override on the related models: - - $user = User::factory() - ->hasPosts(3, [ - 'published' => false, - ]) - ->create(); - -You may provide a closure based state transformation if your state change requires access to the parent model: - - $user = User::factory() - ->hasPosts(3, function (array $attributes, User $user) { - return ['user_type' => $user->type]; - }) - ->create(); - - -### Belongs To Relationships - -Now that we have explored how to build "has many" relationships using factories, let's explore the inverse of the relationship. The `for` method may be used to define the parent model that factory created models belong to. For example, we can create three `App\Models\Post` model instances that belong to a single user: - - use App\Models\Post; - use App\Models\User; - - $posts = Post::factory() - ->count(3) - ->for(User::factory()->state([ - 'name' => 'Jessica Archer', - ])) - ->create(); - -If you already have a parent model instance that should be associated with the models you are creating, you may pass the model instance to the `for` method: - - $user = User::factory()->create(); - - $posts = Post::factory() - ->count(3) - ->for($user) - ->create(); - - -#### Using Magic Methods - -For convenience, you may use Laravel's magic factory relationship methods to define "belongs to" relationships. For example, the following example will use convention to determine that the three posts should belong to the `user` relationship on the `Post` model: - - $posts = Post::factory() - ->count(3) - ->forUser([ - 'name' => 'Jessica Archer', - ]) - ->create(); - - -### Many To Many Relationships - -Like [has many relationships](#has-many-relationships), "many to many" relationships may be created using the `has` method: - - use App\Models\Role; - use App\Models\User; - - $user = User::factory() - ->has(Role::factory()->count(3)) - ->create(); - - -#### Pivot Table Attributes - -If you need to define attributes that should be set on the pivot / intermediate table linking the models, you may use the `hasAttached` method. This method accepts an array of pivot table attribute names and values as its second argument: - - use App\Models\Role; - use App\Models\User; - - $user = User::factory() - ->hasAttached( - Role::factory()->count(3), - ['active' => true] - ) - ->create(); - -You may provide a closure based state transformation if your state change requires access to the related model: - - $user = User::factory() - ->hasAttached( - Role::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['name' => $user->name.' Role']; - }), - ['active' => true] - ) - ->create(); - -If you already have model instances that you would like to be attached to the models you are creating, you may pass the model instances to the `hasAttached` method. In this example, the same three roles will be attached to all three users: - - $roles = Role::factory()->count(3)->create(); - - $user = User::factory() - ->count(3) - ->hasAttached($roles, ['active' => true]) - ->create(); - - -#### Using Magic Methods - -For convenience, you may use Laravel's magic factory relationship methods to define many to many relationships. For example, the following example will use convention to determine that the related models should be created via a `roles` relationship method on the `User` model: - - $user = User::factory() - ->hasRoles(1, [ - 'name' => 'Editor' - ]) - ->create(); - - -### Polymorphic Relationships - -[Polymorphic relationships](/docs/{{version}}/eloquent-relationships#polymorphic-relationships) may also be created using factories. Polymorphic "morph many" relationships are created in the same way as typical "has many" relationships. For example, if a `App\Models\Post` model has a `morphMany` relationship with a `App\Models\Comment` model: - - use App\Models\Post; - - $post = Post::factory()->hasComments(3)->create(); - - -#### Morph To Relationships - -Magic methods may not be used to create `morphTo` relationships. Instead, the `for` method must be used directly and the name of the relationship must be explicitly provided. For example, imagine that the `Comment` model has a `commentable` method that defines a `morphTo` relationship. In this situation, we may create three comments that belong to a single post by using the `for` method directly: - - $comments = Comment::factory()->count(3)->for( - Post::factory(), 'commentable' - )->create(); - - -#### Polymorphic Many To Many Relationships - -Polymorphic "many to many" (`morphToMany` / `morphedByMany`) relationships may be created just like non-polymorphic "many to many" relationships: - - use App\Models\Tag; - use App\Models\Video; - - $videos = Video::factory() - ->hasAttached( - Tag::factory()->count(3), - ['public' => true] - ) - ->create(); - -Of course, the magic `has` method may also be used to create polymorphic "many to many" relationships: - - $videos = Video::factory() - ->hasTags(3, ['public' => true]) - ->create(); - - -### Defining Relationships Within Factories - -To define a relationship within your model factory, you will typically assign a new factory instance to the foreign key of the relationship. This is normally done for the "inverse" relationships such as `belongsTo` and `morphTo` relationships. For example, if you would like to create a new user when creating a post, you may do the following: - - use App\Models\User; - - /** - * Define the model's default state. - * - * @return array - */ - public function definition() - { - return [ - 'user_id' => User::factory(), - 'title' => fake()->title(), - 'content' => fake()->paragraph(), - ]; - } - -If the relationship's columns depend on the factory that defines it you may assign a closure to an attribute. The closure will receive the factory's evaluated attribute array: - - /** - * Define the model's default state. - * - * @return array - */ - public function definition() - { - return [ - 'user_id' => User::factory(), - 'user_type' => function (array $attributes) { - return User::find($attributes['user_id'])->type; - }, - 'title' => fake()->title(), - 'content' => fake()->paragraph(), - ]; + // ... } diff --git a/documentation.md b/documentation.md index b51c6cd6bc1..55cde44bf45 100644 --- a/documentation.md +++ b/documentation.md @@ -67,6 +67,7 @@ - [Mutators / Casts](/docs/{{version}}/eloquent-mutators) - [API Resources](/docs/{{version}}/eloquent-resources) - [Serialization](/docs/{{version}}/eloquent-serialization) + - [Factories](/docs/{{version}}/eloquent-factories) - ## Testing - [Getting Started](/docs/{{version}}/testing) - [HTTP Tests](/docs/{{version}}/http-tests) diff --git a/eloquent-factories.md b/eloquent-factories.md new file mode 100644 index 00000000000..02c9e4973c0 --- /dev/null +++ b/eloquent-factories.md @@ -0,0 +1,489 @@ +# Eloquent: Factories + +- [Introduction](#introduction) +- [Defining Model Factories](#defining-model-factories) + - [Generating Factories](#generating-factories) + - [Factory States](#factory-states) + - [Factory Callbacks](#factory-callbacks) +- [Creating Models Using Factories](#creating-models-using-factories) + - [Instantiating Models](#instantiating-models) + - [Persisting Models](#persisting-models) + - [Sequences](#sequences) +- [Factory Relationships](#factory-relationships) + - [Has Many Relationships](#has-many-relationships) + - [Belongs To Relationships](#belongs-to-relationships) + - [Many To Many Relationships](#many-to-many-relationships) + - [Polymorphic Relationships](#polymorphic-relationships) + - [Defining Relationships Within Factories](#defining-relationships-within-factories) + + +## Introduction + +When testing your application or seeding your database, you may need to insert a few records into your database. Instead of manually specifying the value of each column, Laravel allows you to define a set of default attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using model factories. + +To see an example of how to write a factory, take a look at the `database/factories/UserFactory.php` file in your application. This factory is included with all new Laravel applications and contains the following factory definition: + + namespace Database\Factories; + + use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Support\Str; + + class UserFactory extends Factory + { + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } + } + +As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. + +Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing and seeding. + +> **Note** +> You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. + + +## Defining Model Factories + + +### Generating Factories + +To create a factory, execute the `make:factory` [Artisan command](/docs/{{version}}/artisan): + +```shell +php artisan make:factory PostFactory +``` + +The new factory class will be placed in your `database/factories` directory. + + +#### Model & Factory Discovery Conventions + +Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. + +The `HasFactory` trait's `factory` method will use conventions to determine the proper factory for the model the trait is assigned to. Specifically, the method will look for a factory in the `Database\Factories` namespace that has a class name matching the model name and is suffixed with `Factory`. If these conventions do not apply to your particular application or factory, you may overwrite the `newFactory` method on your model to return an instance of the model's corresponding factory directly: + + use Database\Factories\Administration\FlightFactory; + + /** + * Create a new factory instance for the model. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + protected static function newFactory() + { + return FlightFactory::new(); + } + +Next, define a `model` property on the corresponding factory: + + use App\Administration\Flight; + use Illuminate\Database\Eloquent\Factories\Factory; + + class FlightFactory extends Factory + { + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = Flight::class; + } + + +### Factory States + +State manipulation methods allow you to define discrete modifications that can be applied to your model factories in any combination. For example, your `Database\Factories\UserFactory` factory might contain a `suspended` state method that modifies one of its default attribute values. + +State transformation methods typically call the `state` method provided by Laravel's base factory class. The `state` method accepts a closure which will receive the array of raw attributes defined for the factory and should return an array of attributes to modify: + + /** + * Indicate that the user is suspended. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + public function suspended() + { + return $this->state(function (array $attributes) { + return [ + 'account_status' => 'suspended', + ]; + }); + } + +#### "Trashed" State + +If your Eloquent model can be [soft deleted](/docs/{{version}}/eloquent#soft-deleting), you may invoke the built-in `trashed` state method to indicate that the created model should already be "soft deleted". You do not need to manually define the `trashed` state as it is automatically available to all factories: + + use App\Models\User; + + $user = User::factory()->trashed()->create(); + + +### Factory Callbacks + +Factory callbacks are registered using the `afterMaking` and `afterCreating` methods and allow you to perform additional tasks after making or creating a model. You should register these callbacks by defining a `configure` method on your factory class. This method will be automatically called by Laravel when the factory is instantiated: + + namespace Database\Factories; + + use App\Models\User; + use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Support\Str; + + class UserFactory extends Factory + { + /** + * Configure the model factory. + * + * @return $this + */ + public function configure() + { + return $this->afterMaking(function (User $user) { + // + })->afterCreating(function (User $user) { + // + }); + } + + // ... + } + + +## Creating Models Using Factories + + +### Instantiating Models + +Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. Let's take a look at a few examples of creating models. First, we'll use the `make` method to create models without persisting them to the database: + + use App\Models\User; + + $user = User::factory()->make(); + +You may create a collection of many models using the `count` method: + + $users = User::factory()->count(3)->make(); + + +#### Applying States + +You may also apply any of your [states](#factory-states) to the models. If you would like to apply multiple state transformations to the models, you may simply call the state transformation methods directly: + + $users = User::factory()->count(5)->suspended()->make(); + + +#### Overriding Attributes + +If you would like to override some of the default values of your models, you may pass an array of values to the `make` method. Only the specified attributes will be replaced while the rest of the attributes remain set to their default values as specified by the factory: + + $user = User::factory()->make([ + 'name' => 'Abigail Otwell', + ]); + +Alternatively, the `state` method may be called directly on the factory instance to perform an inline state transformation: + + $user = User::factory()->state([ + 'name' => 'Abigail Otwell', + ])->make(); + +> **Note** +> [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. + + +### Persisting Models + +The `create` method instantiates model instances and persists them to the database using Eloquent's `save` method: + + use App\Models\User; + + // Create a single App\Models\User instance... + $user = User::factory()->create(); + + // Create three App\Models\User instances... + $users = User::factory()->count(3)->create(); + +You may override the factory's default model attributes by passing an array of attributes to the `create` method: + + $user = User::factory()->create([ + 'name' => 'Abigail', + ]); + + +### Sequences + +Sometimes you may wish to alternate the value of a given model attribute for each created model. You may accomplish this by defining a state transformation as a sequence. For example, you may wish to alternate the value of an `admin` column between `Y` and `N` for each created user: + + use App\Models\User; + use Illuminate\Database\Eloquent\Factories\Sequence; + + $users = User::factory() + ->count(10) + ->state(new Sequence( + ['admin' => 'Y'], + ['admin' => 'N'], + )) + ->create(); + +In this example, five users will be created with an `admin` value of `Y` and five users will be created with an `admin` value of `N`. + +If necessary, you may include a closure as a sequence value. The closure will be invoked each time the sequence needs a new value: + + $users = User::factory() + ->count(10) + ->state(new Sequence( + fn ($sequence) => ['role' => UserRoles::all()->random()], + )) + ->create(); + +Within a sequence closure, you may access the `$index` or `$count` properties on the sequence instance that is injected into the closure. The `$index` property contains the number of iterations through the sequence that have occurred thus far, while the `$count` property contains the total number of times the sequence will be invoked: + + $users = User::factory() + ->count(10) + ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) + ->create(); + + +## Factory Relationships + + +### Has Many Relationships + +Next, let's explore building Eloquent model relationships using Laravel's fluent factory methods. First, let's assume our application has an `App\Models\User` model and an `App\Models\Post` model. Also, let's assume that the `User` model defines a `hasMany` relationship with `Post`. We can create a user that has three posts using the `has` method provided by the Laravel's factories. The `has` method accepts a factory instance: + + use App\Models\Post; + use App\Models\User; + + $user = User::factory() + ->has(Post::factory()->count(3)) + ->create(); + +By convention, when passing a `Post` model to the `has` method, Laravel will assume that the `User` model must have a `posts` method that defines the relationship. If necessary, you may explicitly specify the name of the relationship that you would like to manipulate: + + $user = User::factory() + ->has(Post::factory()->count(3), 'posts') + ->create(); + +Of course, you may perform state manipulations on the related models. In addition, you may pass a closure based state transformation if your state change requires access to the parent model: + + $user = User::factory() + ->has( + Post::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['user_type' => $user->type]; + }) + ) + ->create(); + + +#### Using Magic Methods + +For convenience, you may use Laravel's magic factory relationship methods to build relationships. For example, the following example will use convention to determine that the related models should be created via a `posts` relationship method on the `User` model: + + $user = User::factory() + ->hasPosts(3) + ->create(); + +When using magic methods to create factory relationships, you may pass an array of attributes to override on the related models: + + $user = User::factory() + ->hasPosts(3, [ + 'published' => false, + ]) + ->create(); + +You may provide a closure based state transformation if your state change requires access to the parent model: + + $user = User::factory() + ->hasPosts(3, function (array $attributes, User $user) { + return ['user_type' => $user->type]; + }) + ->create(); + + +### Belongs To Relationships + +Now that we have explored how to build "has many" relationships using factories, let's explore the inverse of the relationship. The `for` method may be used to define the parent model that factory created models belong to. For example, we can create three `App\Models\Post` model instances that belong to a single user: + + use App\Models\Post; + use App\Models\User; + + $posts = Post::factory() + ->count(3) + ->for(User::factory()->state([ + 'name' => 'Jessica Archer', + ])) + ->create(); + +If you already have a parent model instance that should be associated with the models you are creating, you may pass the model instance to the `for` method: + + $user = User::factory()->create(); + + $posts = Post::factory() + ->count(3) + ->for($user) + ->create(); + + +#### Using Magic Methods + +For convenience, you may use Laravel's magic factory relationship methods to define "belongs to" relationships. For example, the following example will use convention to determine that the three posts should belong to the `user` relationship on the `Post` model: + + $posts = Post::factory() + ->count(3) + ->forUser([ + 'name' => 'Jessica Archer', + ]) + ->create(); + + +### Many To Many Relationships + +Like [has many relationships](#has-many-relationships), "many to many" relationships may be created using the `has` method: + + use App\Models\Role; + use App\Models\User; + + $user = User::factory() + ->has(Role::factory()->count(3)) + ->create(); + + +#### Pivot Table Attributes + +If you need to define attributes that should be set on the pivot / intermediate table linking the models, you may use the `hasAttached` method. This method accepts an array of pivot table attribute names and values as its second argument: + + use App\Models\Role; + use App\Models\User; + + $user = User::factory() + ->hasAttached( + Role::factory()->count(3), + ['active' => true] + ) + ->create(); + +You may provide a closure based state transformation if your state change requires access to the related model: + + $user = User::factory() + ->hasAttached( + Role::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['name' => $user->name.' Role']; + }), + ['active' => true] + ) + ->create(); + +If you already have model instances that you would like to be attached to the models you are creating, you may pass the model instances to the `hasAttached` method. In this example, the same three roles will be attached to all three users: + + $roles = Role::factory()->count(3)->create(); + + $user = User::factory() + ->count(3) + ->hasAttached($roles, ['active' => true]) + ->create(); + + +#### Using Magic Methods + +For convenience, you may use Laravel's magic factory relationship methods to define many to many relationships. For example, the following example will use convention to determine that the related models should be created via a `roles` relationship method on the `User` model: + + $user = User::factory() + ->hasRoles(1, [ + 'name' => 'Editor' + ]) + ->create(); + + +### Polymorphic Relationships + +[Polymorphic relationships](/docs/{{version}}/eloquent-relationships#polymorphic-relationships) may also be created using factories. Polymorphic "morph many" relationships are created in the same way as typical "has many" relationships. For example, if a `App\Models\Post` model has a `morphMany` relationship with a `App\Models\Comment` model: + + use App\Models\Post; + + $post = Post::factory()->hasComments(3)->create(); + + +#### Morph To Relationships + +Magic methods may not be used to create `morphTo` relationships. Instead, the `for` method must be used directly and the name of the relationship must be explicitly provided. For example, imagine that the `Comment` model has a `commentable` method that defines a `morphTo` relationship. In this situation, we may create three comments that belong to a single post by using the `for` method directly: + + $comments = Comment::factory()->count(3)->for( + Post::factory(), 'commentable' + )->create(); + + +#### Polymorphic Many To Many Relationships + +Polymorphic "many to many" (`morphToMany` / `morphedByMany`) relationships may be created just like non-polymorphic "many to many" relationships: + + use App\Models\Tag; + use App\Models\Video; + + $videos = Video::factory() + ->hasAttached( + Tag::factory()->count(3), + ['public' => true] + ) + ->create(); + +Of course, the magic `has` method may also be used to create polymorphic "many to many" relationships: + + $videos = Video::factory() + ->hasTags(3, ['public' => true]) + ->create(); + + +### Defining Relationships Within Factories + +To define a relationship within your model factory, you will typically assign a new factory instance to the foreign key of the relationship. This is normally done for the "inverse" relationships such as `belongsTo` and `morphTo` relationships. For example, if you would like to create a new user when creating a post, you may do the following: + + use App\Models\User; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'user_id' => User::factory(), + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; + } + +If the relationship's columns depend on the factory that defines it you may assign a closure to an attribute. The closure will receive the factory's evaluated attribute array: + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'user_id' => User::factory(), + 'user_type' => function (array $attributes) { + return User::find($attributes['user_id'])->type; + }, + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; + } diff --git a/http-tests.md b/http-tests.md index e4d538c109e..f64b92ccf20 100644 --- a/http-tests.md +++ b/http-tests.md @@ -152,7 +152,7 @@ Laravel provides several helpers for interacting with the session during HTTP te } } -Laravel's session is typically used to maintain state for the currently authenticated user. Therefore, the `actingAs` helper method provides a simple way to authenticate a given user as the current user. For example, we may use a [model factory](/docs/{{version}}/database-testing#writing-factories) to generate and authenticate a user: +Laravel's session is typically used to maintain state for the currently authenticated user. Therefore, the `actingAs` helper method provides a simple way to authenticate a given user as the current user. For example, we may use a [model factory](/docs/{{version}}/eloquent-factories) to generate and authenticate a user: ### Using Model Factories -Of course, manually specifying the attributes for each model seed is cumbersome. Instead, you can use [model factories](/docs/{{version}}/database-testing#defining-model-factories) to conveniently generate large amounts of database records. First, review the [model factory documentation](/docs/{{version}}/database-testing#defining-model-factories) to learn how to define your factories. +Of course, manually specifying the attributes for each model seed is cumbersome. Instead, you can use [model factories](/docs/{{version}}/eloquent-factories) to conveniently generate large amounts of database records. First, review the [model factory documentation](/docs/{{version}}/eloquent-factories) to learn how to define your factories. For example, let's create 50 users that each has one related post: From 26ec2d1a208bd8c7fa56f2d1829cc167ad461c7c Mon Sep 17 00:00:00 2001 From: Alejandro Silva Berbesi Date: Wed, 17 Aug 2022 14:41:01 -0500 Subject: [PATCH 0340/2609] Add assertNotSoftDeleted method documentation (#8134) --- database-testing.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/database-testing.md b/database-testing.md index 2ecdfd98d38..757d5c8ead6 100644 --- a/database-testing.md +++ b/database-testing.md @@ -171,6 +171,13 @@ Assert that a table in the database does not contain records matching the given The `assertSoftDeleted` method may be used to assert a given Eloquent model has been "soft deleted": $this->assertSoftDeleted($user); + + +#### assertNotSoftDeleted + +The `assertNotSoftDeleted` method may be used to assert a given Eloquent model hasn't been "soft deleted": + + $this->assertNotSoftDeleted($user); #### assertModelExists From 184f191b999916f9758af7cef1c202c51d9a4551 Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Thu, 18 Aug 2022 14:32:51 +0100 Subject: [PATCH 0341/2609] [9.x] Update GitHub Actions sample workflow for Dusk (#8137) * Use newer version of "actions/checkout". * Replace database name that breaks command. * Use backticks in database name. --- dusk.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dusk.md b/dusk.md index 69410f99d78..fda9c25dc21 100644 --- a/dusk.md +++ b/dusk.md @@ -1894,13 +1894,13 @@ jobs: dusk-php: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Prepare The Environment run: cp .env.example .env - name: Create Database run: | sudo systemctl start mysql - mysql --user="root" --password="root" -e "CREATE DATABASE 'my-database' character set UTF8mb4 collate utf8mb4_bin;" + mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;" - name: Install Composer Dependencies run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Generate Application Key From a90b05d2f98cde50daeed64872998061e2de15ed Mon Sep 17 00:00:00 2001 From: Mohamed Ilies <35309918+medilies@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:38:21 +0100 Subject: [PATCH 0342/2609] [9.x] to_route helper in routing (#8136) * Update routing.md * Update routing.md Co-authored-by: Taylor Otwell --- routing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routing.md b/routing.md index 355498e5aa9..486819e923b 100644 --- a/routing.md +++ b/routing.md @@ -305,6 +305,8 @@ Once you have assigned a name to a given route, you may use the route's name whe // Generating Redirects... return redirect()->route('profile'); + return to_route('profile'); + If the named route defines parameters, you may pass the parameters as the second argument to the `route` function. The given parameters will automatically be inserted into the generated URL in their correct positions: Route::get('/user/{id}/profile', function ($id) { From 72ebaf8770a2c5b75a986c064ab600fecae25353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Debrauwer?= Date: Thu, 18 Aug 2022 16:04:01 +0200 Subject: [PATCH 0343/2609] [9.x] Cursorpagination: query expressions with parameters not supported (#8135) * Update pagination.md * Update pagination.md Co-authored-by: Taylor Otwell --- pagination.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pagination.md b/pagination.md index 529c34b964b..cdffe639757 100644 --- a/pagination.md +++ b/pagination.md @@ -20,7 +20,7 @@ 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. +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. #### Tailwind JIT @@ -126,7 +126,7 @@ You may create a cursor based paginator instance via the `cursorPaginate` method Once you have retrieved a cursor paginator instance, you may [display the pagination results](#displaying-pagination-results) as you typically would when using the `paginate` and `simplePaginate` methods. For more information on the instance methods offered by the cursor paginator, please consult the [cursor paginator instance method documentation](#cursor-paginator-instance-methods). -> **Warning** +> **Warning** > Your query must contain an "order by" clause in order to take advantage of cursor pagination. @@ -151,7 +151,8 @@ However, cursor pagination has the following limitations: - Like `simplePaginate`, cursor pagination can only be used to display "Next" and "Previous" links and does not support generating links with page numbers. - It requires that the ordering is based on at least one unique column or a combination of columns that are unique. Columns with `null` values are not supported. -- Query expressions in "order by" clauses are supported only if they are aliased and added to the "select" clause as well. +- Query expressions in "order by" clauses are supported only if they are aliased and added to the "select" clause as well. +- Query expressions with parameters are not supported. ### Manually Creating A Paginator @@ -162,7 +163,7 @@ The `Paginator` and `CursorPaginator` classes do not need to know the total numb In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, the `CursorPaginator` corresponds to the `cursorPaginate` method, and the `LengthAwarePaginator` corresponds to the `paginate` method. -> **Warning** +> **Warning** > 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. From 7f2043937fc9fd7f2602b5bbfa2dbf98c446fde5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 21 Aug 2022 12:11:40 -0500 Subject: [PATCH 0344/2609] wip --- notifications.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/notifications.md b/notifications.md index ef6c4a774ec..c907b7f737f 100644 --- a/notifications.md +++ b/notifications.md @@ -336,10 +336,10 @@ In this example, we register a greeting, a line of text, a call to action, and t > **Note** > 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 Mail Notification Formatting Options + +#### Error Messages -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: +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. @@ -349,12 +349,16 @@ Instead of defining the "lines" of text in the notification class, you may use t */ public function toMail($notifiable) { - return (new MailMessage)->view( - 'emails.name', ['invoice' => $this->invoice] - ); + return (new MailMessage) + ->error() + ->subject('Invoice Payment Failed') + ->line('...'); } -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: + +#### 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: /** * Get the mail representation of the notification. @@ -365,15 +369,11 @@ You may specify a plain-text view for the mail message by passing the view name public function toMail($notifiable) { return (new MailMessage)->view( - ['emails.name.html', 'emails.name.plain'], - ['invoice' => $this->invoice] + 'emails.name', ['invoice' => $this->invoice] ); } - -#### 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 black: +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. @@ -383,10 +383,10 @@ Some notifications inform users of errors, such as a failed invoice payment. You */ public function toMail($notifiable) { - return (new MailMessage) - ->error() - ->subject('Notification Subject') - ->line('...'); + return (new MailMessage)->view( + ['emails.name.html', 'emails.name.plain'], + ['invoice' => $this->invoice] + ); } From f5aa53d13c46af75825f8b450dd9ed9cad83c2c5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 21 Aug 2022 12:26:31 -0500 Subject: [PATCH 0345/2609] wip --- notifications.md | 1 - 1 file changed, 1 deletion(-) diff --git a/notifications.md b/notifications.md index c907b7f737f..fc0b4dd13dd 100644 --- a/notifications.md +++ b/notifications.md @@ -603,7 +603,6 @@ Some third-party email providers such as Mailgun and Postmark support message "t If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) and [metadata](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). If your application is using Amazon SES to send emails, you should use the `metadata` method to attach [SES "tags"](https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html) to the message. -Tags and metadata can be added to the `MailMessage` - these are used by your email service for filtering/processing: ### Customizing The Symfony Message From 75f6257785cafed1114cba9270d15c226fc92224 Mon Sep 17 00:00:00 2001 From: Daniel Schmelz Date: Mon, 22 Aug 2022 15:19:17 +0200 Subject: [PATCH 0346/2609] Fix typos (#8149) --- billing.md | 2 +- cashier-paddle.md | 2 +- database.md | 2 +- dusk.md | 4 ++-- eloquent-mutators.md | 2 +- eloquent-resources.md | 14 +++++++------- http-client.md | 4 ++-- http-tests.md | 10 +++++----- localization.md | 2 +- mocking.md | 2 +- notifications.md | 2 +- pagination.md | 2 +- queues.md | 2 +- redis.md | 2 +- sail.md | 2 +- scout.md | 2 +- starter-kits.md | 2 +- validation.md | 4 ++-- 18 files changed, 31 insertions(+), 31 deletions(-) diff --git a/billing.md b/billing.md index 17e88f40a4a..3a88cce594c 100644 --- a/billing.md +++ b/billing.md @@ -746,7 +746,7 @@ You may also create subscriptions from the Stripe dashboard itself. When doing s In addition, you may only create one type of subscription via the Stripe dashboard. If your application offers multiple subscriptions that use different names, only one type of subscription may be added through the Stripe dashboard. -Finally, you should always make sure to only add one active subscription per type of subscription offered by your application. If customer has two `default` subscriptions, only the most recently added subscription will be used by Cashier even though both would be synced with your application's database. +Finally, you should always make sure to only add one active subscription per type of subscription offered by your application. If a customer has two `default` subscriptions, only the most recently added subscription will be used by Cashier even though both would be synced with your application's database. ### Checking Subscription Status diff --git a/cashier-paddle.md b/cashier-paddle.md index 31a9f92ae74..af8d5bf757f 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -739,7 +739,7 @@ If you would like to swap plans and immediately invoice the user instead of wait #### Prorations -By default, Paddle prorates charges when swapping between plans. The `noProrate` method may be used to update the subscription's without prorating the charges: +By default, Paddle prorates charges when swapping between plans. The `noProrate` method may be used to update the subscriptions without prorating the charges: $user->subscription('default')->noProrate()->swap($premium = 34567); diff --git a/database.md b/database.md index 073f40e3ff4..85f1a76eed2 100644 --- a/database.md +++ b/database.md @@ -420,7 +420,7 @@ To get started, you should schedule the `db:monitor` command to [run every minut php artisan db:monitor --databases=mysql,pgsql --max=100 ``` -Scheduling this command alone is not enough to trigger a notification alerting you of the number of open connections. When the command encounters a database that has a open connection count that exceeds your threshold, a `DatabaseBusy` event will be dispatched. You should listen for this event within your application's `EventServiceProvider` in order to send a notification to you or your development team: +Scheduling this command alone is not enough to trigger a notification alerting you of the number of open connections. When the command encounters a database that has an open connection count that exceeds your threshold, a `DatabaseBusy` event will be dispatched. You should listen for this event within your application's `EventServiceProvider` in order to send a notification to you or your development team: ```php use App\Notifications\DatabaseApproachingMaxConnections; diff --git a/dusk.md b/dusk.md index fda9c25dc21..85b47615941 100644 --- a/dusk.md +++ b/dusk.md @@ -583,7 +583,7 @@ The `press` method may be used to click a button element on the page. The argume $browser->press('Login'); -When submitting forms, many application's disable the form's submission button after it is pressed and then re-enable the button when the form submission's HTTP request is complete. To press a button and wait for the button to be re-enabled, you may use the `pressAndWaitFor` method: +When submitting forms, many applications disable the form's submission button after it is pressed and then re-enable the button when the form submission's HTTP request is complete. To press a button and wait for the button to be re-enabled, you may use the `pressAndWaitFor` method: // Press the button and wait a maximum of 5 seconds for it to be enabled... $browser->pressAndWaitFor('Save'); @@ -1884,7 +1884,7 @@ script: ### GitHub Actions -If you are using [Github Actions](https://github.com/features/actions) to run your Dusk tests, you may use the following configuration file as a starting point. Like TravisCI, we will use the `php artisan serve` command to launch PHP's built-in web server: +If you are using [GitHub Actions](https://github.com/features/actions) to run your Dusk tests, you may use the following configuration file as a starting point. Like TravisCI, we will use the `php artisan serve` command to launch PHP's built-in web server: ```yaml name: CI diff --git a/eloquent-mutators.md b/eloquent-mutators.md index c7ad09b2d41..603befbbdc1 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -172,7 +172,7 @@ The mutator closure will receive the value that is being set on the attribute, a $user->first_name = 'Sally'; -In this example, the `set` callback will be called with the value `Sally`. The mutator will then apply the `strtolower` function to the name and set its resulting value in model's the internal `$attributes` array. +In this example, the `set` callback will be called with the value `Sally`. The mutator will then apply the `strtolower` function to the name and set its resulting value in the model's internal `$attributes` array. #### Mutating Multiple Attributes diff --git a/eloquent-resources.md b/eloquent-resources.md index 7c681f6602d..978d7fb6737 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -323,12 +323,12 @@ By default, your outermost resource is wrapped in a `data` key when the resource { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ] } @@ -425,12 +425,12 @@ When returning paginated collections via a resource response, Laravel will wrap { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ], "links":{ @@ -471,12 +471,12 @@ Paginated responses always contain `meta` and `links` keys with information abou { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ], "links":{ @@ -599,7 +599,7 @@ In addition to conditionally including relationships, you may conditionally incl new UserResource($user->loadCount('posts')); -The `whenCounted` method may be used to conditionally include a relationship's count in your resource response. This method avoids unnecessarily including the attribute if the relationships's count is not present: +The `whenCounted` method may be used to conditionally include a relationship's count in your resource response. This method avoids unnecessarily including the attribute if the relationships' count is not present: /** * Transform the resource into an array. diff --git a/http-client.md b/http-client.md index 94b73230b83..805e30d2b8e 100644 --- a/http-client.md +++ b/http-client.md @@ -177,7 +177,7 @@ You may specify the maximum number of seconds to wait while trying to connect to ### Retries -If you would like HTTP client to automatically retry the request if a client or server error occurs, you may use the `retry` method. The `retry` method accepts the maximum number of times the request should be attempted and the number of milliseconds that Laravel should wait in between attempts: +If you would like the HTTP client to automatically retry the request if a client or server error occurs, you may use the `retry` method. The `retry` method accepts the maximum number of times the request should be attempted and the number of milliseconds that Laravel should wait in between attempts: $response = Http::retry(3, 100)->post(/* ... */); @@ -187,7 +187,7 @@ If needed, you may pass a third argument to the `retry` method. The third argume return $exception instanceof ConnectionException; })->post(/* ... */); -If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: +If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying the request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: $response = Http::withToken($this->getToken())->retry(2, 0, function ($exception, $request) { if (! $exception instanceof RequestException || $exception->response->status() !== 401) { diff --git a/http-tests.md b/http-tests.md index f64b92ccf20..a8f90d5238a 100644 --- a/http-tests.md +++ b/http-tests.md @@ -698,7 +698,7 @@ Assert that the response contains the given cookie and it is not expired: #### assertCookieMissing -Assert that the response does not contains the given cookie: +Assert that the response does not contain the given cookie: $response->assertCookieMissing($cookieName); @@ -828,7 +828,7 @@ Assert that the response contains the given data at the specified path: For example, if the following JSON response is returned by your application: -```js +```json { "user": { "name": "Steve Schoger" @@ -849,7 +849,7 @@ Assert that the response does not contain the given path: For example, if the following JSON response is returned by your application: -```js +```json { "user": { "name": "Steve Schoger" @@ -870,7 +870,7 @@ Assert that the response has a given JSON structure: For example, if the JSON response returned by your application contains the following data: -```js +```json { "user": { "name": "Steve Schoger" @@ -888,7 +888,7 @@ You may assert that the JSON structure matches your expectations like so: Sometimes, JSON responses returned by your application may contain arrays of objects: -```js +```json { "user": [ { diff --git a/localization.md b/localization.md index 2b40df70e86..784b53dc382 100644 --- a/localization.md +++ b/localization.md @@ -24,7 +24,7 @@ Laravel provides two ways to manage translation strings. First, language strings /es messages.php -Or, translation strings may be defined within JSON files that are placed within the `lang` directory. When taking this approach, each language supported by your application would have a corresponding JSON file within this directory. This approach is recommended for application's that have a large number of translatable strings: +Or, translation strings may be defined within JSON files that are placed within the `lang` directory. When taking this approach, each language supported by your application would have a corresponding JSON file within this directory. This approach is recommended for applications that have a large number of translatable strings: /lang en.json diff --git a/mocking.md b/mocking.md index 46a71b74865..7088307f60c 100644 --- a/mocking.md +++ b/mocking.md @@ -178,7 +178,7 @@ You may use the `Bus` facade's `fake` method to prevent jobs from being dispatch // Assert that a job was dispatched synchronously... Bus::assertDispatchedSync(AnotherJob::class); - // Assert that a job was not dipatched synchronously... + // Assert that a job was not dispatched synchronously... Bus::assertNotDispatchedSync(AnotherJob::class); // Assert that a job was dispatched after the response was sent... diff --git a/notifications.md b/notifications.md index fc0b4dd13dd..d4b6072ab53 100644 --- a/notifications.md +++ b/notifications.md @@ -52,7 +52,7 @@ ## Introduction -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. +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 notifications 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. diff --git a/pagination.md b/pagination.md index cdffe639757..fa64df0fed7 100644 --- a/pagination.md +++ b/pagination.md @@ -212,7 +212,7 @@ If you need to append a "hash fragment" to URLs generated by the paginator, you When calling the `paginate` method, you will receive an instance of `Illuminate\Pagination\LengthAwarePaginator`, while calling the `simplePaginate` method returns an instance of `Illuminate\Pagination\Paginator`. And, finally, calling the `cursorPaginate` method returns an instance of `Illuminate\Pagination\CursorPaginator`. -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): +These objects provide several methods that describe the result set. In addition to these helper 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): ```blade
diff --git a/queues.md b/queues.md index a020965824c..b81cbe0d3e9 100644 --- a/queues.md +++ b/queues.md @@ -1465,7 +1465,7 @@ php artisan queue:work > **Note** > To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. -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. +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 significantly less efficient than the `queue:work` command: diff --git a/redis.md b/redis.md index 1c2dbf2cddd..7f46e144571 100644 --- a/redis.md +++ b/redis.md @@ -183,7 +183,7 @@ In addition to the default `scheme`, `host`, `port`, `database`, and `password` #### phpredis Serialization & Compression -The phpredis extension may also be configured to use a variety serialization and compression algorithms. These algorithms can be configured via the `options` array of your Redis configuration: +The phpredis extension may also be configured to use a variety of serialization and compression algorithms. These algorithms can be configured via the `options` array of your Redis configuration: 'redis' => [ diff --git a/sail.md b/sail.md index 8166f1e8717..cba44e2c3d1 100644 --- a/sail.md +++ b/sail.md @@ -271,7 +271,7 @@ The Sail `test` command is equivalent to running the `test` Artisan command: sail artisan test ``` -By default, Sail will create a dedicated `testing` database that your tests do not interfere with the current state of your database. In a default Laravel installation, Sail will also configure your `phpunit.xml` file to use this database when executing your tests: +By default, Sail will create a dedicated `testing` database so that your tests do not interfere with the current state of your database. In a default Laravel installation, Sail will also configure your `phpunit.xml` file to use this database when executing your tests: ```xml diff --git a/scout.md b/scout.md index 3686005d7ad..d837e468127 100644 --- a/scout.md +++ b/scout.md @@ -184,7 +184,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 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: +By default, Scout will use the primary key of the model as the 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: diff --git a/validation.md b/validation.md index d2f0e4b4940..f445e9563ec 100644 --- a/validation.md +++ b/validation.md @@ -197,7 +197,7 @@ So, in our example, the user will be redirected to our controller's `create` met #### Customizing The Error Messages -Laravel's built-in validation rules each has an error message that is located in your application's `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. +Laravel's built-in validation rules each have an error message that is located in your application's `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). @@ -723,7 +723,7 @@ The `has` method may be used to determine if any error messages exist for a give ### Specifying Custom Messages In Language Files -Laravel's built-in validation rules each has an error message that is located in your application's `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. +Laravel's built-in validation rules each have an error message that is located in your application's `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). From 1b452b96df68fb56593ac53e7c4ddae39fd0dc19 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 22 Aug 2022 15:20:06 +0200 Subject: [PATCH 0347/2609] [9.x] Clarify prohibited (#8150) * Update validation.md * Update validation.md --- validation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/validation.md b/validation.md index f445e9563ec..5f7e4ad844b 100644 --- a/validation.md +++ b/validation.md @@ -1408,12 +1408,12 @@ The field under validation must be present in the input data but can be empty. #### prohibited -The field under validation must be empty or not present. +The field under validation must be an empty string or not present. #### prohibited_if:_anotherfield_,_value_,... -The field under validation must be empty or not present if the _anotherfield_ field is equal to any _value_. +The field under validation must be an empty string or not present if the _anotherfield_ field is equal to any _value_. If complex conditional prohibition logic is required, you may utilize the `Rule::prohibitedIf` method. This method accepts a boolean or a closure. When given a closure, the closure should return `true` or `false` to indicate if the field under validation should be prohibited: @@ -1431,7 +1431,7 @@ If complex conditional prohibition logic is required, you may utilize the `Rule: #### prohibited_unless:_anotherfield_,_value_,... -The field under validation must be empty or not present unless the _anotherfield_ field is equal to any _value_. +The field under validation must be an empty string or not present unless the _anotherfield_ field is equal to any _value_. #### prohibits:_anotherfield_,... From 889ad55400cafa2c2f30e684e1a86a439946c82e Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Mon, 22 Aug 2022 22:24:29 +0900 Subject: [PATCH 0348/2609] [9.x] Add Vite Facade to Class Reference (#8147) --- facades.md | 1 + 1 file changed, 1 insertion(+) diff --git a/facades.md b/facades.md index 2b4aec683fa..2a8e9faeb8e 100644 --- a/facades.md +++ b/facades.md @@ -304,3 +304,4 @@ Validator | [Illuminate\Validation\Factory](https://laravel.com/api/{{version} Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/{{version}}/Illuminate/Validation/Validator.html) |   View | [Illuminate\View\Factory](https://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` View (Instance) | [Illuminate\View\View](https://laravel.com/api/{{version}}/Illuminate/View/View.html) |   +Vite | [Illuminate\Foundation\Vite](https://laravel.com/api/{{version}}/Illuminate/Foundation/Vite.html) |   From b1d58c845cc7a988289035a89e5d1df500157fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abdulkadir=20Cemilo=C4=9Flu?= <32229751+megasteve19@users.noreply.github.com> Date: Mon, 22 Aug 2022 18:09:01 +0300 Subject: [PATCH 0349/2609] specifying build path for vite directive (#8142) * specifying build path for directive * Update vite.md Co-authored-by: Taylor Otwell --- vite.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/vite.md b/vite.md index 10643dd3cd8..7c6a27dd3a2 100644 --- a/vite.md +++ b/vite.md @@ -170,6 +170,17 @@ If you're importing your CSS via JavaScript, you only need to include the JavaSc The `@vite` directive will automatically detect the Vite development server and inject the Vite client to enable Hot Module Replacement. In build mode, the directive will load your compiled and versioned assets, including any imported CSS. +If needed, you may also specify the build path of your compiled assets when invoking the `@vite` directive: + +```blade + + + {{-- Given build path is relative to public path. --}} + + @vite('resources/js/app.js', 'vendor/courier/build') + +``` + ## Running Vite From aa63c231ddc72e3898780ca11d26129a5567b5d5 Mon Sep 17 00:00:00 2001 From: Kennedy Soehren <56128828+Ksoehren@users.noreply.github.com> Date: Mon, 22 Aug 2022 13:55:39 -0500 Subject: [PATCH 0350/2609] [9.x] Fix component wording (#8152) - Fix component method to correct return type --- http-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index a8f90d5238a..ff4bdb96c56 100644 --- a/http-tests.md +++ b/http-tests.md @@ -589,7 +589,7 @@ If necessary, you may use the `blade` method to evaluate and render a raw [Blade $view->assertSee('Taylor'); -You may use the `component` method to evaluate and render a [Blade component](/docs/{{version}}/blade#components). Like the `view` method, the `component` method returns an instance of `Illuminate\Testing\TestView`: +You may use the `component` method to evaluate and render a [Blade component](/docs/{{version}}/blade#components). The `component` method returns an instance of `Illuminate\Testing\TestComponent`: $view = $this->component(Profile::class, ['name' => 'Taylor']); From 3798ba88e7fea1dba191faff097ad6b6ed67827f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Aug 2022 08:34:28 -0500 Subject: [PATCH 0351/2609] remove backed --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 603befbbdc1..aa1544aa092 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -428,7 +428,7 @@ If a custom format is applied to the `date` or `datetime` cast, such as `datetim > **Warning** > Enum casting is only available for PHP 8.1+. -Eloquent also allows you to cast your attribute values to PHP ["backed" Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: +Eloquent also allows you to cast your attribute values to PHP [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: use App\Enums\ServerStatus; From bd0cb8ef07493f3b550a0663fd7745ce1043ac8e Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Tue, 23 Aug 2022 14:35:11 +0100 Subject: [PATCH 0352/2609] feature: `max_length` and `min_length` validation docs (#8148) --- validation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/validation.md b/validation.md index 5f7e4ad844b..fbcef7c78c8 100644 --- a/validation.md +++ b/validation.md @@ -848,9 +848,11 @@ Below is a list of all available validation rules and their function: [Less Than Or Equal](#rule-lte) [MAC Address](#rule-mac) [Max](#rule-max) +[Max Digits](#rule-max-digits) [MIME Types](#rule-mimetypes) [MIME Type By File Extension](#rule-mimes) [Min](#rule-min) +[Min Digits](#rule-min-digits) [Multiple Of](#multiple-of) [Not In](#rule-not-in) [Not Regex](#rule-not-regex) @@ -1322,6 +1324,11 @@ The field under validation must be a MAC address. The field under validation must be less than or equal to a maximum _value_. Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. + +#### max_digits:_value_ + +The integer under validation must have a maximum length of _value_. + #### mimetypes:_text/plain_,... @@ -1350,6 +1357,11 @@ Even though you only need to specify the extensions, this rule actually validate The field under validation must have a minimum _value_. Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. + +#### min_digits:_value_ + +The integer under validation must have a minimum length of _value_. + #### multiple_of:_value_ From 7be8a1c3dc3fd89885f89a62c39e8336ad0a7d61 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 23 Aug 2022 23:35:35 +1000 Subject: [PATCH 0353/2609] [9.x] Document advanced customisations (#8146) * document advanced customisations * formatting * Update vite.md Co-authored-by: Taylor Otwell --- vite.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/vite.md b/vite.md index 7c6a27dd3a2..4fdcdb861ed 100644 --- a/vite.md +++ b/vite.md @@ -25,6 +25,7 @@ - [Content Security Policy (CSP) Nonce](#content-security-policy-csp-nonce) - [Subresource Integrity (SRI)](#subresource-integrity-sri) - [Arbitrary Attributes](#arbitrary-attributes) +- [Advanced Customization](#advanced-customization) ## Introduction @@ -668,3 +669,38 @@ Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, arr > **Warning** > The `$chunk` and `$manifest` arguments will be `null` while the Vite development server is running. + + +## Advanced Customization + +Out of the box, Laravel's Vite plugin uses sensible conventions that should work for the majority of applications; however, sometimes you may need to customize Vite's behavior. To enable additional customization options, we offer the following methods and options which can be used in place of the `@vite` Blade directive: + +```blade + + + {{-- ... --}} + + {{ + Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file... + ->useBuildDirectory('bundle') // Customize the build directory... + ->withEntryPoints(['resources/js/app.js']) // Specify the entry points... + }} + +``` + +Within the `vite.config.js` file, you should then specify the same configuration: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + hotFile: 'storage/vite.hot', // Customize the "hot" file... + buildDirectory: 'bundle', // Customize the build directory... + input: ['resources/js/app.js'], // Specify the entry points... + }), + ], +}); +``` From ac726288d09988de02912ebccbdfeee3a6b2a565 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 23 Aug 2022 16:38:13 +0200 Subject: [PATCH 0354/2609] Update passport.md (#8141) --- passport.md | 56 ++++------------------------------------------------- 1 file changed, 4 insertions(+), 52 deletions(-) diff --git a/passport.md b/passport.md index e8099c3567c..b068f3b8d23 100644 --- a/passport.md +++ b/passport.md @@ -98,42 +98,6 @@ 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 `App\Providers\AuthServiceProvider`. This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens: - - 'App\Policies\ModelPolicy', - ]; - - /** - * Register any authentication / authorization services. - * - * @return void - */ - public function boot() - { - $this->registerPolicies(); - - if (! $this->app->routesAreCached()) { - Passport::routes(); - } - } - } - Finally, in your application's `config/auth.php` configuration file, you should define an `api` authentication guard and set the `driver` option to `passport`. This will instruct your application to use Passport's `TokenGuard` when authenticating incoming API requests: 'guards' => [ @@ -177,8 +141,6 @@ If necessary, you may define the path where Passport's keys should be loaded fro { $this->registerPolicies(); - Passport::routes(); - Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); } @@ -245,8 +207,6 @@ By default, Passport issues long-lived access tokens that expire after one year. { $this->registerPolicies(); - Passport::routes(); - Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::personalAccessTokensExpireIn(now()->addMonths(6)); @@ -283,8 +243,6 @@ After defining your model, you may instruct Passport to use your custom model vi { $this->registerPolicies(); - Passport::routes(); - Passport::useTokenModel(Token::class); Passport::useClientModel(Client::class); Passport::useAuthCodeModel(AuthCode::class); @@ -419,7 +377,7 @@ Once a client has been created, developers may use their client ID and secret to }); > **Note** -> Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. +> Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. #### Approving The Request @@ -483,7 +441,7 @@ If the user approves the authorization request, they will be redirected back to 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. > **Note** -> 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. +> Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. #### JSON API @@ -689,7 +647,7 @@ php artisan passport:client --password ### Requesting Tokens -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: +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 Passport 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: use Illuminate\Support\Facades\Http; @@ -804,8 +762,6 @@ The implicit grant is similar to the authorization code grant; however, the toke { $this->registerPolicies(); - Passport::routes(); - Passport::enableImplicitGrant(); } @@ -828,7 +784,7 @@ Once the grant has been enabled, developers may use their client ID to request a }); > **Note** -> Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. +> Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. ## Client Credentials Grant Tokens @@ -1048,8 +1004,6 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` { $this->registerPolicies(); - Passport::routes(); - Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status', @@ -1195,8 +1149,6 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo { $this->registerPolicies(); - Passport::routes(); - Passport::cookie('custom_name'); } From 2f2cc7ea9465defd509f22170cc4b1c98139b6b4 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 23 Aug 2022 16:39:49 +0200 Subject: [PATCH 0355/2609] Cashier v14 changes (#8156) * Cashier v14 changes * Update billing.md * Update billing.md Co-authored-by: Taylor Otwell --- billing.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/billing.md b/billing.md index 3a88cce594c..4660eef474b 100644 --- a/billing.md +++ b/billing.md @@ -77,7 +77,7 @@ When upgrading to a new version of Cashier, it's important that you carefully review [the upgrade guide](https://github.com/laravel/cashier-stripe/blob/master/UPGRADE.md). > **Warning** -> To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 13 utilizes Stripe API version `2020-08-27`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. +> To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 14 utilizes Stripe API version `2022-08-01`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. ## Installation @@ -296,13 +296,13 @@ Stripe allows you to credit or debit a customer's "balance". Later, this balance $balance = $user->balance(); -To credit a customer's balance, you may provide a negative value to the `applyBalance` method. If you wish, you may also provide a description: +To credit a customer's balance, you may provide a value to the `creditBalance` method. If you wish, you may also provide a description: - $user->applyBalance(-500, 'Premium customer top-up.'); + $user->creditBalance(500, 'Premium customer top-up.'); -Providing a positive value to the `applyBalance` method will debit the customer's balance: +Providing a value to the `debitBalance` method will debit the customer's balance: - $user->applyBalance(300, 'Bad usage penalty.'); + $user->debitBalance(300, 'Bad usage penalty.'); The `applyBalance` method will create new customer balance transactions for the customer. You may retrieve these transaction records using the `balanceTransactions` method, which may be useful in order to provide a log of credits and debits for the customer to review: @@ -375,7 +375,7 @@ You may customize the columns used for syncing customer information to Stripe by return $this->company_name; } -Similarly, you may override the `stripeEmail`, `stripePhone`, and `stripeAddress` methods. These methods will sync information to their corresponding customer parameters when [updating the Stripe customer object](https://stripe.com/docs/api/customers/update). If you wish to take total control over the customer information sync process, you may override the `syncStripeCustomerDetails` method. +Similarly, you may override the `stripeEmail`, `stripePhone`, `stripeAddress`, and `stripePreferredLocales` methods. These methods will sync information to their corresponding customer parameters when [updating the Stripe customer object](https://stripe.com/docs/api/customers/update). If you wish to take total control over the customer information sync process, you may override the `syncStripeCustomerDetails` method. ### Billing Portal @@ -1704,18 +1704,21 @@ You may pass an array of prices to the `previewInvoice` method in order to previ ### Generating Invoice PDFs +Before generating invoice PDFs, you should use Composer to install the Dompdf library, which is the default invoice renderer for Cashier: + +```php +composer require dompdf/dompdf +``` + 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) { - return $request->user()->downloadInvoice($invoiceId, [ - 'vendor' => 'Your Company', - 'product' => 'Your Product', - ]); + return $request->user()->downloadInvoice($invoiceId); }); -By default, all data on the invoice is derived from the customer and invoice data stored in Stripe. However, you can customize some of this data by providing an array as the second argument to the `downloadInvoice` method. This array allows you to customize information such as your company and product details: +By default, all data on the invoice is derived from the customer and invoice data stored in Stripe. The filename is based on your `app.name` config value. However, you can customize some of this data by providing an array as the second argument to the `downloadInvoice` method. This array allows you to customize information such as your company and product details: return $request->user()->downloadInvoice($invoiceId, [ 'vendor' => 'Your Company', @@ -1726,7 +1729,7 @@ By default, all data on the invoice is derived from the customer and invoice dat 'email' => 'info@example.com', 'url' => 'https://example.com/', 'vendorVat' => 'BE123456789', - ], 'my-invoice'); + ]); The `downloadInvoice` method also allows for a custom filename via its third argument. This filename will automatically be suffixed with `.pdf`: From 7635641c79c76dd30c3c0987a982577da5f4ddf2 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 23 Aug 2022 16:41:39 +0200 Subject: [PATCH 0356/2609] [9.x] Dusk v7 doc adjustments (#8140) * Update dusk.md * Update dusk.md * Update dusk.md Co-authored-by: Taylor Otwell --- dusk.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dusk.md b/dusk.md index 85b47615941..3cdd87debb3 100644 --- a/dusk.md +++ b/dusk.md @@ -65,7 +65,7 @@ composer require --dev laravel/dusk > **Warning** > If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. -After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory and an example Dusk test: +After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operation system: ```shell php artisan dusk:install @@ -79,7 +79,7 @@ Next, set the `APP_URL` environment variable in your application's `.env` file. ### Managing ChromeDriver Installations -If you would like to install a different version of ChromeDriver than what is included with Laravel Dusk, you may use the `dusk:chrome-driver` command: +If you would like to install a different version of ChromeDriver than what is installed by Laravel Dusk via the `dusk:install` command, you may use the `dusk:chrome-driver` command: ```shell # Install the latest version of ChromeDriver for your OS... From c386753492f19641abfc59edac5e5cf2aa171389 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 23 Aug 2022 20:02:53 +0200 Subject: [PATCH 0357/2609] Update collections.md (#8157) --- collections.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/collections.md b/collections.md index de696d84ab0..427c5582acb 100644 --- a/collections.md +++ b/collections.md @@ -3366,7 +3366,20 @@ To create a lazy collection instance, you should pass a PHP generator function t Almost all methods available on the `Collection` class are also available on the `LazyCollection` class. Both of these classes implement the `Illuminate\Support\Enumerable` contract, which defines the following methods: -
+ + +
[all](#method-all) [average](#method-average) From 39f753e30a14879274f04bd5accf9973e71ea94f Mon Sep 17 00:00:00 2001 From: Andrew Groebe <40124525+agroebe@users.noreply.github.com> Date: Tue, 23 Aug 2022 13:09:19 -0500 Subject: [PATCH 0358/2609] Update queues.md - add explicit documentation of `sync` default queue connection (#8154) * Updates queues.md - Add explicit documentation about `sync` to 'Dispatching Jobs' section Adds more explicit documentation to the 'Dispatching Jobs' section regarding Laravel's default queue connection of `sync`. I am using queues in Laravel for the first time, and this was a footgun for me. As I was trying to dispatch jobs to my queue, I noticed that they were running synchronously and I immediately assumed I was doing something wrong, even though I followed the docs. I would expect this to be documented a little more explicitly which would have helped with my development experience. * Update queues.md * Update queues.md * Update queues.md Co-authored-by: Taylor Otwell --- queues.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/queues.md b/queues.md index b81cbe0d3e9..32db7ea9d27 100644 --- a/queues.md +++ b/queues.md @@ -647,6 +647,8 @@ If you would like to conditionally dispatch a job, you may use the `dispatchIf` ProcessPodcast::dispatchUnless($accountSuspended, $podcast); +In new Laravel applications, the `sync` driver is the default queue driver. This driver executes jobs synchronously in the foreground of the current request, which is often convenient during local development. If you would like to actually begin queueing jobs for background processing, you may specify a different queue driver within your application's `config/queue.php` configuration file. + ### Delayed Dispatching From 67e505096f38478deb39ba1affde4aee7a7eeb5b Mon Sep 17 00:00:00 2001 From: Paul Preibisch Date: Wed, 24 Aug 2022 08:16:58 -0600 Subject: [PATCH 0359/2609] $isAssoc variable name is misleading in isList() helper documenation (#8159) * The isAssoc variable name is misleading. I think it was accidently copied from the previous help topic. Changing to isList * removed accidently added brackets --- helpers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index fa7c7a1840e..d74176e2053 100644 --- a/helpers.md +++ b/helpers.md @@ -611,11 +611,11 @@ The `Arr::isList` method returns `true` if the given array's keys are sequential use Illuminate\Support\Arr; - $isAssoc = Arr::isList(['foo', 'bar', 'baz']); + $isList = Arr::isList(['foo', 'bar', 'baz']); // true - $isAssoc = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); + $isList = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); // false From c367dbf1bffc8982b372f21fba68a8d41a2c2f31 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 12:44:52 -0500 Subject: [PATCH 0360/2609] fix wording --- configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index da1726b5199..8dc01aab978 100644 --- a/configuration.md +++ b/configuration.md @@ -54,7 +54,7 @@ Your `.env` file should not be committed to your application's source control, s #### Additional Environment Files -Before loading your application's environment variables, Laravel determines if either the `APP_ENV` environment variable has been externally provided or if the `--env` CLI argument has been specified. If so, Laravel will attempt to load an `.env.[APP_ENV]` file if it exists. If it does not exist, the default `.env` file will be loaded. +Before loading your application's environment variables, Laravel determines if an `APP_ENV` environment variable has been externally provided or if the `--env` CLI argument has been specified. If so, Laravel will attempt to load an `.env.[APP_ENV]` file if it exists. If it does not exist, the default `.env` file will be loaded. ### Environment Variable Types From 21eb16f1636607a4ae3e93d3f44e0e44a0491b7c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 12:45:34 -0500 Subject: [PATCH 0361/2609] clarify --- configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index 8dc01aab978..725cd7f261b 100644 --- a/configuration.md +++ b/configuration.md @@ -81,7 +81,7 @@ APP_NAME="My Application" ### Retrieving Environment Configuration -All of the variables listed in this file will be loaded into the `$_ENV` PHP super-global when your application receives a request. However, you may use the `env` helper to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice many of the options are already using this helper: +All of the variables listed in the `.env` file will be loaded into the `$_ENV` PHP super-global when your application receives a request. However, you may use the `env` helper to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice many of the options are already using this helper: 'debug' => env('APP_DEBUG', false), From 9f4510865db7873337c058c00396e876fc88250a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 12:46:40 -0500 Subject: [PATCH 0362/2609] use function --- configuration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configuration.md b/configuration.md index 725cd7f261b..de992be2875 100644 --- a/configuration.md +++ b/configuration.md @@ -81,7 +81,7 @@ APP_NAME="My Application" ### Retrieving Environment Configuration -All of the variables listed in the `.env` file will be loaded into the `$_ENV` PHP super-global when your application receives a request. However, you may use the `env` helper to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice many of the options are already using this helper: +All of the variables listed in the `.env` file will be loaded into the `$_ENV` PHP super-global when your application receives a request. However, you may use the `env` function to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice many of the options are already using this function: 'debug' => env('APP_DEBUG', false), @@ -112,14 +112,14 @@ You may also pass arguments to the `environment` method to determine if the envi ## Accessing Configuration Values -You may easily access your configuration values using the global `config` helper function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: +You may easily access your configuration values using the global `config` function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: $value = config('app.timezone'); // Retrieve a default value if the configuration value does not exist... $value = config('app.timezone', 'Asia/Seoul'); -To set configuration values at runtime, pass an array to the `config` helper: +To set configuration values at runtime, pass an array to the `config` function: config(['app.timezone' => 'America/Chicago']); From db264522da8e92c0aa66377ed61f18af0511ae25 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 12:48:01 -0500 Subject: [PATCH 0363/2609] fix sentence --- configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index de992be2875..f1af5d1a9ef 100644 --- a/configuration.md +++ b/configuration.md @@ -166,7 +166,7 @@ php artisan down --retry=60 #### Bypassing Maintenance Mode -Even while in maintenance mode, you may use the `secret` option to specify a maintenance mode bypass token: +To allow maintenance mode to be bypassed using a secret token, you may use the `secret` option to specify a maintenance mode bypass token: ```shell php artisan down --secret="1630542a-246b-4b66-afa1-dd72a4c43515" From bbfb9acbe76dcb5a15323f19117b3076caa01a65 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 15:10:09 -0500 Subject: [PATCH 0364/2609] wip --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index 2749b7b3eb6..25581e0c034 100644 --- a/authentication.md +++ b/authentication.md @@ -110,7 +110,7 @@ If you are building a single-page application (SPA) that will be powered by a La Passport may be chosen when your application absolutely needs all of the features provided by the OAuth2 specification. -And, if you would like to get started quickly, we are pleased to recommend [Laravel Jetstream](https://jetstream.laravel.com) as a quick way to start a new Laravel application that already uses our preferred authentication stack of Laravel's built-in authentication services and Laravel Sanctum. +And, if you would like to get started quickly, we are pleased to recommend [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze) as a quick way to start a new Laravel application that already uses our preferred authentication stack of Laravel's built-in authentication services and Laravel Sanctum. ## Authentication Quickstart From 15ccaefba9d1770c9448842dcc61e7e422e4eab9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 18:38:33 -0500 Subject: [PATCH 0365/2609] remove section --- installation.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/installation.md b/installation.md index 17d90492247..ef8c2672c43 100644 --- a/installation.md +++ b/installation.md @@ -10,7 +10,6 @@ - [Choosing Your Sail Services](#choosing-your-sail-services) - [Initial Configuration](#initial-configuration) - [Environment Based Configuration](#environment-based-configuration) - - [Directory Configuration](#directory-configuration) - [Databases & Migrations](#databases-and-migrations) - [Next Steps](#next-steps) - [Laravel The Full Stack Framework](#laravel-the-fullstack-framework) @@ -205,11 +204,6 @@ Your `.env` file should not be committed to your application's source control, s > **Note** > For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). - -### Directory Configuration - -Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files that exist within your application. - ### Databases & Migrations From 0ba43a4be6d25d1dbdb26bac35f0e70d2d4b0894 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 19:50:08 -0500 Subject: [PATCH 0366/2609] change header --- documentation.md | 2 +- vite.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation.md b/documentation.md index 55cde44bf45..54cdd558ee3 100644 --- a/documentation.md +++ b/documentation.md @@ -23,7 +23,7 @@ - [Responses](/docs/{{version}}/responses) - [Views](/docs/{{version}}/views) - [Blade Templates](/docs/{{version}}/blade) - - [Bundling Assets](/docs/{{version}}/vite) + - [Asset Bundling](/docs/{{version}}/vite) - [URL Generation](/docs/{{version}}/urls) - [Session](/docs/{{version}}/session) - [Validation](/docs/{{version}}/validation) diff --git a/vite.md b/vite.md index 4fdcdb861ed..31fc0398376 100644 --- a/vite.md +++ b/vite.md @@ -1,4 +1,4 @@ -# Bundling Assets (Vite) +# Asset Bundling (Vite) - [Introduction](#introduction) - [Installation & Setup](#installation) From a0ceec9a7c5cdab1edb6918d7f16b1700feb7f7b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Aug 2022 19:54:20 -0500 Subject: [PATCH 0367/2609] wip --- requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests.md b/requests.md index f2c52ebf9bc..b7b424feb0e 100644 --- a/requests.md +++ b/requests.md @@ -288,7 +288,7 @@ You may call the `query` method without any arguments in order to retrieve all o #### 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 retrieve values that are nested within 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 / objects: $name = $request->input('user.name'); From ddc4f7a606df4b2e5d6984b51f076aeb126e1493 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 25 Aug 2022 22:20:59 +0900 Subject: [PATCH 0368/2609] [9.x] Update implicit rules description to a new invokable way (#8160) * [9.x] Update implicit rules description to a new invokable way * Update validation.md Co-authored-by: Taylor Otwell --- validation.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/validation.md b/validation.md index fbcef7c78c8..e6668537203 100644 --- a/validation.md +++ b/validation.md @@ -2105,9 +2105,7 @@ By default, when an attribute being validated is not present or contains an empt Validator::make($input, $rules)->passes(); // true -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. - -To generate a new implicit rule object, you may use the `make:rule` Artisan command with the `--implicit` option : +For a custom rule to run even when an attribute is empty, the rule must imply that the attribute is required. To quickly generate a new implicit rule object, you may use the `make:rule` Artisan command with the `--implicit` option: ```shell php artisan make:rule Uppercase --invokable --implicit From dd27ba40b31b2a2637cb4c31303dfa0f89678b74 Mon Sep 17 00:00:00 2001 From: devnll <47151094+devnll@users.noreply.github.com> Date: Thu, 25 Aug 2022 09:34:25 -0600 Subject: [PATCH 0369/2609] Fix typo (#8162) Removes "the" before "Fortify's" in fortify.md --- fortify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortify.md b/fortify.md index 2cc45d0250e..ce150fba3b3 100644 --- a/fortify.md +++ b/fortify.md @@ -363,7 +363,7 @@ To disable two factor authentication, your application should make a DELETE requ To begin implementing our application's registration functionality, we need to instruct Fortify how to return our "register" view. Remember, Fortify is a headless authentication library. If you would like a frontend implementation of Laravel's authentication features that are already completed for you, you should use an [application starter kit](/docs/{{version}}/starter-kits). -All of the Fortify's view rendering logic may be customized using the appropriate methods available via the `Laravel\Fortify\Fortify` class. Typically, you should call this method from the `boot` method of your `App\Providers\FortifyServiceProvider` class: +All of Fortify's view rendering logic may be customized using the appropriate methods available via the `Laravel\Fortify\Fortify` class. Typically, you should call this method from the `boot` method of your `App\Providers\FortifyServiceProvider` class: ```php use Laravel\Fortify\Fortify; From 899bbc2215faef25c89429e41d28aca29cda7c03 Mon Sep 17 00:00:00 2001 From: Vladislav Osmianskij Date: Fri, 26 Aug 2022 18:37:46 +0300 Subject: [PATCH 0370/2609] Return installation using `laravel new` to docs (#8163) * Return installation using `laravel new` to docs * Update installation.md Co-authored-by: Taylor Otwell --- installation.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/installation.md b/installation.md index ef8c2672c43..b5fc846d199 100644 --- a/installation.md +++ b/installation.md @@ -56,6 +56,14 @@ After you have installed PHP and Composer, you may create a new Laravel project composer create-project laravel/laravel example-app ``` +Or, you may create new Laravel projects by globally installing the Laravel installer via Composer: + +```nothing +composer global require laravel/installer + +laravel new example-app +``` + After the project has been created, start Laravel's local development server using the Laravel's Artisan CLI `serve` command: ```nothing From 5b28e3efe2c2c497ee3127d996c53e1740dd28bb Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 29 Aug 2022 15:42:37 +0200 Subject: [PATCH 0371/2609] Update pagination.md --- pagination.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pagination.md b/pagination.md index fa64df0fed7..a8c231b217f 100644 --- a/pagination.md +++ b/pagination.md @@ -126,7 +126,7 @@ You may create a cursor based paginator instance via the `cursorPaginate` method Once you have retrieved a cursor paginator instance, you may [display the pagination results](#displaying-pagination-results) as you typically would when using the `paginate` and `simplePaginate` methods. For more information on the instance methods offered by the cursor paginator, please consult the [cursor paginator instance method documentation](#cursor-paginator-instance-methods). -> **Warning** +> **Warning** > Your query must contain an "order by" clause in order to take advantage of cursor pagination. @@ -163,7 +163,7 @@ The `Paginator` and `CursorPaginator` classes do not need to know the total numb In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, the `CursorPaginator` corresponds to the `cursorPaginate` method, and the `LengthAwarePaginator` corresponds to the `paginate` method. -> **Warning** +> **Warning** > 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. From 90976b449ec8728c977c6b7388619c0e6025f9d8 Mon Sep 17 00:00:00 2001 From: Phyo Thiha Date: Mon, 29 Aug 2022 20:24:05 +0630 Subject: [PATCH 0372/2609] fix sentence in fortify.md (#8168) --- fortify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortify.md b/fortify.md index ce150fba3b3..f811d79bc97 100644 --- a/fortify.md +++ b/fortify.md @@ -387,7 +387,7 @@ Fortify will take care of defining the `/register` route that returns this view. The `/register` endpoint expects a string `name`, string email address / username, `password`, and `password_confirmation` fields. The name of the email / username field should match the `username` configuration value defined within your application's `fortify` configuration file. -If the registration attempt is successful, Fortify will redirect the user to the URI configured via the `home` configuration option within your application's `fortify` configuration file. If the login request was an XHR request, a 200 HTTP response will be returned. +If the registration attempt is successful, Fortify will redirect the user to the URI configured via the `home` configuration option within your application's `fortify` configuration file. If the request was an XHR request, a 201 HTTP response will be returned. If the request was not successful, the user will be redirected back to the registration screen and the validation errors will be available to you via the shared `$errors` [Blade template variable](/docs/{{version}}/validation#quick-displaying-the-validation-errors). Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response. From eb6c3edcd073f30513777f4c71f4e2da4e182e67 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 30 Aug 2022 00:06:06 +1000 Subject: [PATCH 0373/2609] make invokable classes the default (#8165) --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index e6668537203..07944790ebb 100644 --- a/validation.md +++ b/validation.md @@ -1952,7 +1952,7 @@ Occasionally, you may want to attach additional validation rules to your default 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 -php artisan make:rule Uppercase --invokable +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: `__invoke`. This method receives the attribute name, its value, and a callback that should be invoked on failure with the validation error message: @@ -2108,7 +2108,7 @@ By default, when an attribute being validated is not present or contains an empt For a custom rule to run even when an attribute is empty, the rule must imply that the attribute is required. To quickly generate a new implicit rule object, you may use the `make:rule` Artisan command with the `--implicit` option: ```shell -php artisan make:rule Uppercase --invokable --implicit +php artisan make:rule Uppercase --implicit ``` > **Warning** From 646969bea1253054ef61249737f3ab0ae4a51334 Mon Sep 17 00:00:00 2001 From: Jesse Kramer Date: Tue, 30 Aug 2022 15:18:00 +0200 Subject: [PATCH 0374/2609] Note that the range specified in the 'between' validation rule in inclusive. (#8172) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index e6668537203..5b328a88e54 100644 --- a/validation.md +++ b/validation.md @@ -978,7 +978,7 @@ The field under validation must be a value preceding or equal to the given date. #### between:_min_,_max_ -The field under validation must have a size between the given _min_ and _max_. Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. +The field under validation must have a size between the given _min_ and _max_ (inclusive). Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. #### boolean From 00f86a4b86f9879ad29a0bf8550c487e67b9dfa7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 30 Aug 2022 08:38:12 -0500 Subject: [PATCH 0375/2609] document on demand broadcast --- notifications.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/notifications.md b/notifications.md index d4b6072ab53..c674575b599 100644 --- a/notifications.md +++ b/notifications.md @@ -287,9 +287,12 @@ However, if you would like to make the final determination on whether the queued 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: + use Illuminate\Broadcasting\Channel; + Notification::route('mail', 'taylor@example.com') ->route('vonage', '5555555555') ->route('slack', 'https://hooks.slack.com/services/...') + ->route('broadcast', [new Channel('channel-name')]) ->notify(new InvoicePaid($invoice)); If you would like to provide the recipient's name when sending an on-demand notification to the `mail` route, you may provide an array that contains the email address as the key and the name as the value of the first element in the array: From effe1a1328a316102621716b93860bb4ad9e992e Mon Sep 17 00:00:00 2001 From: simonminton <44101544+simonminton@users.noreply.github.com> Date: Wed, 31 Aug 2022 17:16:22 +0100 Subject: [PATCH 0376/2609] amending one off to be one-off to achieve consistency of phrasing, to improve search/find in page (#8174) Co-authored-by: Simon Minton --- billing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index 4660eef474b..ea147eadeee 100644 --- a/billing.md +++ b/billing.md @@ -406,7 +406,7 @@ If you would like to generate the URL to the billing portal without generating a ### 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 this 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 @@ -1215,7 +1215,7 @@ If you're offering subscriptions with multiple products, you may define differen } > **Warning** -> The `taxRates` method only applies to subscription charges. If you use Cashier to make "one off" charges, you will need to manually specify the tax rate at that time. +> The `taxRates` method only applies to subscription charges. If you use Cashier to make "one-off" charges, you will need to manually specify the tax rate at that time. #### Syncing Tax Rates From cd8f9c8e851ca331740db55053fe45421caf2a13 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 1 Sep 2022 15:36:22 +0200 Subject: [PATCH 0377/2609] Update eloquent-mutators.md (#8177) --- eloquent-mutators.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index aa1544aa092..d23b1f4c59a 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -561,7 +561,7 @@ As an example, we will define a custom cast class that casts multiple model valu namespace App\Casts; - use App\Models\Address as AddressModel; + use App\ValueObjects\Address as AddressValueObject; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use InvalidArgumentException; @@ -574,11 +574,11 @@ As an example, we will define a custom cast class that casts multiple model valu * @param string $key * @param mixed $value * @param array $attributes - * @return \App\Models\Address + * @return \App\ValueObjects\Address */ public function get($model, $key, $value, $attributes) { - return new AddressModel( + return new AddressValueObject( $attributes['address_line_one'], $attributes['address_line_two'] ); @@ -589,13 +589,13 @@ As an example, we will define a custom cast class that casts multiple model valu * * @param \Illuminate\Database\Eloquent\Model $model * @param string $key - * @param \App\Models\Address $value + * @param \App\ValueObjects\Address $value * @param array $attributes * @return array */ public function set($model, $key, $value, $attributes) { - if (! $value instanceof AddressModel) { + if (! $value instanceof AddressValueObject) { throw new InvalidArgumentException('The given value is not an Address instance.'); } From f22ecfb9ab5687b4b1a20d8a2ef73011fa2aa5cb Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 1 Sep 2022 15:36:39 +0200 Subject: [PATCH 0378/2609] Update requests.md (#8176) --- requests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requests.md b/requests.md index b7b424feb0e..003f04dbdcf 100644 --- a/requests.md +++ b/requests.md @@ -388,13 +388,13 @@ The `hasAny` method returns `true` if any of the specified values are present: // } -If you would like to determine if a value is present on the request and is not empty, you may use the `filled` method: +If you would like to determine if a value is present on the request and is not an empty string, you may use the `filled` method: if ($request->filled('name')) { // } -The `whenFilled` method will execute the given closure 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 an empty string: $request->whenFilled('name', function ($input) { // From 339038313f10b75f030f084983d3c67338fb1692 Mon Sep 17 00:00:00 2001 From: Brandon Ferens Date: Thu, 1 Sep 2022 11:39:54 -0700 Subject: [PATCH 0379/2609] Decimal cast update (#8179) The current docs have two things that need to be adjusted to give better clarity into the `decimal` cast. First, there is a space between "decimal:" and "" that is created because of putting two code blocks next to each other. It could easily be conceived that the cast should be written as `decimal: 2`, for example with a space between the colon and the precision value. This PR corrects that by wrapping the entire cast example in a single code block. Secondly, the "digits" parameter has been changed to be "precision" as that gives better understanding as to what that value is intended to do/be. While the `asDecimal` method in `HasAttributes` and the `number_format` function call this parameter "decimals", both "decimals" and "digits" are vague as to their intended purpose in the usage of this cast. Let me know if you need me to adjust or change anything or you have any questions. Thanks! --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index d23b1f4c59a..c9413f15cda 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -220,7 +220,7 @@ The `$casts` property should be an array where the key is the name of the attrib - `datetime` - `immutable_date` - `immutable_datetime` -- `decimal:`<digits> +- decimal:<precision> - `double` - `encrypted` - `encrypted:array` From 39317562939f90c7303ab37930c3c978fbeddefe Mon Sep 17 00:00:00 2001 From: Andrew Nagy Date: Fri, 2 Sep 2022 07:12:18 -0700 Subject: [PATCH 0380/2609] [9.x] SQLite 3.38.0+ Supports whereJsonContains (#8181) --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 51da3fffd21..8e9e7917824 100644 --- a/queries.md +++ b/queries.md @@ -491,7 +491,7 @@ Laravel also supports querying JSON column types on databases that provide suppo ->where('preferences->dining->meal', 'salad') ->get(); -You may use `whereJsonContains` to query JSON arrays. This feature is not supported by the SQLite database: +You may use `whereJsonContains` to query JSON arrays. This feature is not supported by SQLite database versions less than 3.38.0: $users = DB::table('users') ->whereJsonContains('options->languages', 'en') From 82fda2bd2de1381c953269f581bd4a420cb77983 Mon Sep 17 00:00:00 2001 From: Mohamed Magdi <91500450+magdicom@users.noreply.github.com> Date: Mon, 5 Sep 2022 07:23:55 -0700 Subject: [PATCH 0381/2609] Change to Xdebug v3.* configuration (#8182) https://xdebug.org/docs/upgrade_guide#changed-xdebug.remote_host https://xdebug.org/docs/upgrade_guide#changed-xdebug.remote_autostart --- homestead.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homestead.md b/homestead.md index c116c301be0..5c21eebdf0c 100644 --- a/homestead.md +++ b/homestead.md @@ -733,8 +733,9 @@ When debugging functional tests that make requests to the web server, it is easi ```ini ; If Homestead.yaml contains a different subnet for the IP address, this address may be different... -xdebug.remote_host = 192.168.10.1 -xdebug.remote_autostart = 1 +xdebug.client_host = 192.168.10.1 +xdebug.mode = debug +xdebug.start_with_request = yes ``` From 61711a6769c65be254f196026bbeccc8be763d82 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 5 Sep 2022 16:31:19 +0200 Subject: [PATCH 0382/2609] [9.x] Clarify `etc` behavior on nested attributes (#8185) * Update http-tests.md * Update http-tests.md Co-authored-by: Taylor Otwell --- http-tests.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http-tests.md b/http-tests.md index ff4bdb96c56..2607a78fc9f 100644 --- a/http-tests.md +++ b/http-tests.md @@ -376,6 +376,8 @@ In the example above, you may have noticed we invoked the `etc` method at the en The intention behind this behavior is to protect you from unintentionally exposing sensitive information in your JSON responses by forcing you to either explicitly make an assertion against the attribute or explicitly allow additional attributes via the `etc` method. +However, you should be aware that not including the `etc` method in your assertion chain does not ensure that additional attributes are not being added to arrays that are nested within your JSON object. The `etc` method only ensures that no additional attributes exist at the nesting level in which the `etc` method is invoked. + #### Asserting Attribute Presence / Absence From 1f7884b9a2e78b93253c1fc5451e1642202083d4 Mon Sep 17 00:00:00 2001 From: Samuel Date: Mon, 5 Sep 2022 21:25:51 +0200 Subject: [PATCH 0383/2609] HTML5 selected attribute (#8186) Insted of selected="selected", that sounds very old and to prevent horizzontal scroll on desktop --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index 8378e727858..a54edff42d7 100644 --- a/blade.md +++ b/blade.md @@ -819,7 +819,7 @@ In addition to public variables being available to your component template, any You may execute this method from your component template by invoking the variable matching the name of the method: ```blade - ``` From a91fe972756f5548a79b2fbf8ae690e55b13df04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0lker=20Erg=C3=BCn?= Date: Wed, 7 Sep 2022 16:07:16 +0300 Subject: [PATCH 0384/2609] Fixed the missing bracket in the Eloquent Relationships (#8189) --- eloquent-relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 6df7a27fb5a..45584935c7c 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1669,7 +1669,7 @@ You may sometimes find yourself needing to check for the existence of a relation $users = User::withWhereHas('posts', function ($query) { $query->where('featured', true); - )->get(); + })->get(); ### Lazy Eager Loading From e3007d8db74a33b961178f691032acdf62449397 Mon Sep 17 00:00:00 2001 From: KenFai Date: Wed, 7 Sep 2022 23:50:24 +0800 Subject: [PATCH 0385/2609] [9.x] Fixed the Session `missing()` method description (#8191) --- session.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.md b/session.md index 66b19b40459..086ef9f070d 100644 --- a/session.md +++ b/session.md @@ -156,7 +156,7 @@ To determine if an item is present in the session, even if its value is `null`, // } -To determine if an item is not present in the session, you may use the `missing` method. The `missing` method returns `true` if the item is `null` or if the item is not present: +To determine if an item is not present in the session, you may use the `missing` method. The `missing` method returns `true` if the item is not present: if ($request->session()->missing('users')) { // From 73c27e7ef7afc7aee89a1734363794545451086f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 Sep 2022 16:42:08 -0500 Subject: [PATCH 0386/2609] add subject call --- mail.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mail.md b/mail.md index 42afed52d6f..3bfe9b24515 100644 --- a/mail.md +++ b/mail.md @@ -191,6 +191,7 @@ First, let's explore configuring the sender of the email. Or, in other words, wh public function build() { return $this->from('example@example.com', 'Example') + ->subject('Order Shipped') ->view('emails.orders.shipped'); } From fffe5a5bcbc299fc4e65f55215026674dbd70ce5 Mon Sep 17 00:00:00 2001 From: devnll <47151094+devnll@users.noreply.github.com> Date: Thu, 8 Sep 2022 07:44:48 -0600 Subject: [PATCH 0387/2609] [9.x] Fix typo in vite.md (#8194) Replaces "public directly" with "public directory" in vite.md --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 31fc0398376..848a128ef32 100644 --- a/vite.md +++ b/vite.md @@ -255,7 +255,7 @@ export default defineConfig({ // The Vue plugin will parse absolute URLs and treat them // as absolute paths to files on disk. Setting this to // `false` will leave absolute URLs un-touched so they can - // reference assets in the public directly as expected. + // reference assets in the public directory as expected. includeAbsolute: false, }, }, From 8ce480eab83680f3b60d9950bc39bf5241a6bdce Mon Sep 17 00:00:00 2001 From: Salman S Date: Fri, 9 Sep 2022 20:53:58 +0700 Subject: [PATCH 0388/2609] Update cache.md (#8196) Update code example for cache events, so it can be more align with the style of event listener mappings at the Laravel's Events documentation --- cache.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/cache.md b/cache.md index 0c06d04859a..72313c96ac6 100644 --- a/cache.md +++ b/cache.md @@ -468,26 +468,35 @@ Once your extension is registered, update your `config/cache.php` configuration ## 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 application's `App\Providers\EventServiceProvider` class: - + + use App\Listeners\LogCacheHit; + use App\Listeners\LogCacheMissed; + use App\Listeners\LogKeyForgotten; + use App\Listeners\LogKeyWritten; + use Illuminate\Cache\Events\CacheHit; + use Illuminate\Cache\Events\CacheMissed; + use Illuminate\Cache\Events\KeyForgotten; + use Illuminate\Cache\Events\KeyWritten; + /** * The event listener mappings for the application. * * @var array */ protected $listen = [ - 'Illuminate\Cache\Events\CacheHit' => [ - 'App\Listeners\LogCacheHit', + CacheHit::class => [ + LogCacheHit::class, ], - 'Illuminate\Cache\Events\CacheMissed' => [ - 'App\Listeners\LogCacheMissed', + CacheMissed::class => [ + LogCacheMissed::class, ], - 'Illuminate\Cache\Events\KeyForgotten' => [ - 'App\Listeners\LogKeyForgotten', + KeyForgotten::class => [ + LogKeyForgotten::class, ], - 'Illuminate\Cache\Events\KeyWritten' => [ - 'App\Listeners\LogKeyWritten', + KeyWritten::class => [ + LogKeyWritten::class, ], ]; From 484fa4ba4ffcb0ad133cb516d05729f632240fd1 Mon Sep 17 00:00:00 2001 From: Salman S Date: Mon, 12 Sep 2022 20:49:02 +0700 Subject: [PATCH 0389/2609] Update Mail Events example (#8203) * Update Mail Events example Update code example for Mail events, so it will be more align with the style of event listener mappings at the Laravel's Events documentation * Update mail.md Co-authored-by: Taylor Otwell --- mail.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mail.md b/mail.md index 3bfe9b24515..064d4eba63a 100644 --- a/mail.md +++ b/mail.md @@ -969,17 +969,23 @@ Finally, you may specify a global "to" address by invoking the `alwaysTo` method Laravel fires two events during the process of sending mail messages. The `MessageSending` event is fired prior to a message being sent, while the `MessageSent` event is fired after a message has been sent. Remember, these events are fired when the mail is being *sent*, not when it is queued. You may register event listeners for this event in your `App\Providers\EventServiceProvider` service provider: + use App\Listeners\LogSendingMessage; + use App\Listeners\LogSentMessage; + use Illuminate\Mail\Events\MessageSending; + use Illuminate\Mail\Events\MessageSent; + /** * The event listener mappings for the application. * * @var array */ protected $listen = [ - 'Illuminate\Mail\Events\MessageSending' => [ - 'App\Listeners\LogSendingMessage', + MessageSending::class => [ + LogSendingMessage::class, ], - 'Illuminate\Mail\Events\MessageSent' => [ - 'App\Listeners\LogSentMessage', + + MessageSent::class => [ + LogSentMessage::class, ], ]; From e35a26684c4b4550389dc1d1e0e081aa1dc4704f Mon Sep 17 00:00:00 2001 From: Salman S Date: Mon, 12 Sep 2022 20:49:13 +0700 Subject: [PATCH 0390/2609] Update Email Verification Events example (#8202) Update code example for Email Verification events, so it will be more align with the style of event listener mappings at the Laravel's Events documentation --- verification.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/verification.md b/verification.md index f56f8ece89d..3b489041d27 100644 --- a/verification.md +++ b/verification.md @@ -158,13 +158,16 @@ To get started, pass a closure to the `toMailUsing` method provided by the `Illu 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`: + use App\Listeners\LogVerifiedUser; + use Illuminate\Auth\Events\Verified; + /** * The event listener mappings for the application. * * @var array */ protected $listen = [ - 'Illuminate\Auth\Events\Verified' => [ - 'App\Listeners\LogVerifiedUser', + Verified::class => [ + LogVerifiedUser::class, ], ]; From ffb62b42f78decf58b6396ac0148cf23ce39ec8b Mon Sep 17 00:00:00 2001 From: Salman S Date: Mon, 12 Sep 2022 20:49:37 +0700 Subject: [PATCH 0391/2609] Update Notification Events example (#8200) Update code example for Notification events, so it will be more align with the style of event listener mappings at the Laravel's Events documentation --- notifications.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/notifications.md b/notifications.md index c674575b599..6359ca37dc2 100644 --- a/notifications.md +++ b/notifications.md @@ -1285,14 +1285,17 @@ Once you have implemented the interface, Laravel will automatically use the pref When a notification is sending, the `Illuminate\Notifications\Events\NotificationSending` [event](/docs/{{version}}/events) is dispatched by the notification system. This contains the "notifiable" entity and the notification instance itself. You may register listeners for this event in your application's `EventServiceProvider`: + use App\Listeners\CheckNotificationStatus; + use Illuminate\Notifications\Events\NotificationSending; + /** * The event listener mappings for the application. * * @var array */ protected $listen = [ - 'Illuminate\Notifications\Events\NotificationSending' => [ - 'App\Listeners\CheckNotificationStatus', + NotificationSending::class => [ + CheckNotificationStatus::class, ], ]; @@ -1331,14 +1334,17 @@ Within an event listener, you may access the `notifiable`, `notification`, and ` When a notification is sent, the `Illuminate\Notifications\Events\NotificationSent` [event](/docs/{{version}}/events) is dispatched by the notification system. This contains the "notifiable" entity and the notification instance itself. You may register listeners for this event in your `EventServiceProvider`: + use App\Listeners\LogNotification; + use Illuminate\Notifications\Events\NotificationSent; + /** * The event listener mappings for the application. * * @var array */ protected $listen = [ - 'Illuminate\Notifications\Events\NotificationSent' => [ - 'App\Listeners\LogNotification', + NotificationSent::class => [ + LogNotification::class, ], ]; From 2e6ffef46e640867cab4bac3f7bf03803ef8f29c Mon Sep 17 00:00:00 2001 From: Kevin Foster <45804913+PDXfoster@users.noreply.github.com> Date: Mon, 12 Sep 2022 10:48:33 -0700 Subject: [PATCH 0392/2609] Update logging.md (#8206) * Update logging.md The RFC 5424 spec includes the numerical code for severity level, but without that context, it's not intrinsically apparent what the ordering of severity is without clicking through to the spec. Simply adds a bit of clarifying language. * Update logging.md Co-authored-by: Anjorin Damilare * Update logging.md Co-authored-by: Anjorin Damilare Co-authored-by: Taylor Otwell --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index 691deb6ed87..6986755e2b5 100644 --- a/logging.md +++ b/logging.md @@ -144,7 +144,7 @@ Let's dissect this configuration. First, notice our `stack` channel aggregates t #### Log Levels -Take note of the `level` configuration option present on the `syslog` and `slack` channel configurations in the example above. This option determines the minimum "level" a message must be in order to be logged by the channel. Monolog, which powers Laravel's logging services, offers all of the log levels defined in the [RFC 5424 specification](https://tools.ietf.org/html/rfc5424): **emergency**, **alert**, **critical**, **error**, **warning**, **notice**, **info**, and **debug**. +Take note of the `level` configuration option present on the `syslog` and `slack` channel configurations in the example above. This option determines the minimum "level" a message must be in order to be logged by the channel. Monolog, which powers Laravel's logging services, offers all of the log levels defined in the [RFC 5424 specification](https://tools.ietf.org/html/rfc5424). In descending order of severity, these log levels are: **emergency**, **alert**, **critical**, **error**, **warning**, **notice**, **info**, and **debug**. So, imagine we log a message using the `debug` method: From 5b0f8c34072aa374f11fa85e968087846920d1f9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 13 Sep 2022 15:04:57 +0100 Subject: [PATCH 0393/2609] =?UTF-8?q?[9.x]=20Adds=20documentation=20about?= =?UTF-8?q?=20Signal=20Traps=20=F0=9F=9A=A6=20(#8178)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds documentation about Signal Traps * formatting Co-authored-by: Taylor Otwell --- artisan.md | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/artisan.md b/artisan.md index 683c4b023cd..5537983f829 100644 --- a/artisan.md +++ b/artisan.md @@ -615,47 +615,29 @@ If you would like to call another console command and suppress all of its output ## Signal Handling -The Symfony Console component, which powers the Artisan console, allows you to indicate which process signals (if any) your command handles. For example, you may indicate that your command handles the `SIGINT` and `SIGTERM` signals. - -To get started, you should implement the `Symfony\Component\Console\Command\SignalableCommandInterface` interface on your Artisan command class. This interface requires you to define two methods: `getSubscribedSignals` and `handleSignal`: - -```php -stopServer(); + $this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false); - return; + while ($this->shouldKeepRunning) { + // ... } } -} -``` -As you might expect, the `getSubscribedSignals` method should return an array of the signals that your command can handle, while the `handleSignal` method receives the signal and can respond accordingly. +To listen for multiple signals at once, you may provide an array of signals to the `trap` method: + + $this->trap([SIGTERM, SIGQUIT], function ($signal) { + $this->shouldKeepRunning = false; + + dump($signal); // SIGTERM / SIGQUIT + }); ## Stub Customization From 4c0c08958ef7b104a7005a8623c6ada064307f58 Mon Sep 17 00:00:00 2001 From: Mohammed Attya Date: Tue, 13 Sep 2022 17:39:17 +0200 Subject: [PATCH 0394/2609] Update eloquent.md (#8208) use the Notifiable; --- eloquent.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent.md b/eloquent.md index b0630d6a1c2..0d73cfbb176 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1299,6 +1299,7 @@ To start listening to model events, define a `$dispatchesEvents` property on you use App\Events\UserDeleted; use App\Events\UserSaved; use Illuminate\Foundation\Auth\User as Authenticatable; + use Illuminate\Notifications\Notifiable; class User extends Authenticatable { From 4543f86f6e322523968798fe7e23b0dcbab33e32 Mon Sep 17 00:00:00 2001 From: Andrew Minion Date: Tue, 13 Sep 2022 14:52:51 -0500 Subject: [PATCH 0395/2609] add mailable attachment test documentation (#8195) * document the new mailable attachment assertions * document the hasAttach* methods * tweak grammar to be in line with next paragraph Co-authored-by: Anjorin Damilare * formatting Co-authored-by: Anjorin Damilare Co-authored-by: Taylor Otwell --- mail.md | 8 +++++++- mocking.md | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 064d4eba63a..71c6c19f816 100644 --- a/mail.md +++ b/mail.md @@ -902,7 +902,7 @@ Once you have implemented the interface, Laravel will automatically use the pref ## Testing Mailables -Laravel provides several convenient methods for testing that your mailables contain the content that you expect. These methods are: `assertSeeInHtml`, `assertDontSeeInHtml`, `assertSeeInOrderInHtml`, `assertSeeInText`, `assertDontSeeInText`, and `assertSeeInOrderInText`. +Laravel provides several convenient methods for testing that your mailable contains the content that you expect. These methods are: `assertSeeInHtml`, `assertDontSeeInHtml`, `assertSeeInOrderInHtml`, `assertSeeInText`, `assertDontSeeInText`, `assertSeeInOrderInText`, `assertHasAttachment`, `assertHasAttachedData`, `assertHasAttachmentFromStorage`, and `assertHasAttachmentFromStorageDisk`. As you might expect, the "HTML" assertions assert that the HTML version of your mailable contains a given string, while the "text" assertions assert that the plain-text version of your mailable contains a given string: @@ -921,6 +921,12 @@ As you might expect, the "HTML" assertions assert that the HTML version of your $mailable->assertSeeInText($user->email); $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']); + + $mailable->assertHasAttachment('/path/to/file'); + $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file')); + $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachementFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachementFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); } diff --git a/mocking.md b/mocking.md index 7088307f60c..e25660bb8b5 100644 --- a/mocking.md +++ b/mocking.md @@ -425,6 +425,29 @@ When calling the `Mail` facade's assertion methods, the mailable instance accept $mail->hasSubject('...'); }); +The mailable instance also includes several helpful methods for examining the attachments on a mailable: + + Mail::assertSent(OrderShipped::class, function ($mail) { + return $mail->hasAttachment('/path/to/file'); + }); + + Mail::assertSent(OrderShipped::class, function ($mail) { + return $mail->hasAttachment('/path/to/file', [ + 'as' => 'name.pdf', + 'mime' => 'application/pdf', + ]); + }); + + Mail::assertSent(OrderShipped::class, function ($mail) { + return $mail->hasAttachmentFromStorageDisk('s3', '/path/to/file'); + }); + + Mail::assertSent(OrderShipped::class, function ($mail) use ($pdfData) { + return $mail->hasAttachedData($pdfData, 'name.pdf', [ + 'mime' => 'application/pdf' + ]); + }); + You may have noticed that there are two methods for asserting that mail was not sent: `assertNotSent` and `assertNotQueued`. Sometimes you may wish to assert that no mail was sent **or** queued. To accomplish this, you may use the `assertNothingOutgoing` and `assertNotOutgoing` methods: Mail::assertNothingOutgoing(); From e6ebe04292c57da0ba345d956eed2bba1ee1ae9d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 13 Sep 2022 16:19:35 -0500 Subject: [PATCH 0396/2609] add bootcamp callouts --- deployment.md | 3 +++ eloquent.md | 4 ++++ installation.md | 6 ++++++ starter-kits.md | 4 ++++ structure.md | 3 +++ 5 files changed, 20 insertions(+) diff --git a/deployment.md b/deployment.md index fd4ff5a9c76..965ebbe48d3 100644 --- a/deployment.md +++ b/deployment.md @@ -153,6 +153,9 @@ If you aren't quite ready to manage your own server configuration or aren't comf Laravel Forge can create servers on various infrastructure providers such as DigitalOcean, Linode, AWS, and more. In addition, Forge installs and manages all of the tools needed to build robust Laravel applications, such as Nginx, MySQL, Redis, Memcached, Beanstalk, and more. +> **Note** +> Want a full guide to deploying with Laravel Forge? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com/deploying) and the Forge [video series available on Laracasts](https://laracasts.com/series/learn-laravel-forge-2022-edition). + #### Laravel Vapor diff --git a/eloquent.md b/eloquent.md index 0d73cfbb176..f9f527c00c1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -44,6 +44,10 @@ Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoy > **Note** > Before getting started, be sure to configure a database connection in your application's `config/database.php` configuration file. For more information on configuring your database, check out [the database configuration documentation](/docs/{{version}}/database#configuration). +#### Laravel Bootcamp + +If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Eloquent. It's a great way to get a tour of everything the Laravel and Eloquent have to offer. + ## Generating Model Classes diff --git a/installation.md b/installation.md index b5fc846d199..5f8402012a4 100644 --- a/installation.md +++ b/installation.md @@ -24,6 +24,9 @@ Laravel strives to provide an amazing developer experience while providing power Whether you are new to PHP web frameworks or have years of experience, Laravel is a framework that can grow with you. We'll help you take your first steps as a web developer or give you a boost as you take your expertise to the next level. We can't wait to see what you build. +> **Note** +> New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. + ### Why Laravel? @@ -259,6 +262,9 @@ Now that you have created your Laravel project, you may be wondering what to lea How you want to use Laravel will also dictate the next steps on your journey. There are a variety of ways to use Laravel, and we'll explore two primary use cases for the framework below. +> **Note** +> New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. + ### Laravel The Full Stack Framework diff --git a/starter-kits.md b/starter-kits.md index 6ce0ab48a72..642811913c8 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -24,6 +24,10 @@ Breeze provides a wonderful starting point for beginning a fresh Laravel applica +#### Laravel Bootcamp + +If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Breeze. It's a great way to get a tour of everything the Laravel and Breeze have to offer. + ### Installation diff --git a/structure.md b/structure.md index a7cf692f50b..22063a71627 100644 --- a/structure.md +++ b/structure.md @@ -33,6 +33,9 @@ The default Laravel application structure is intended to provide a great starting point for both large and small applications. But you are free to organize your application however you like. Laravel imposes almost no restrictions on where any given class is located - as long as Composer can autoload the class. +> **Note** +> New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. + ## The Root Directory From ef06a017a40191fdc399482e2d6e84c0f6274d21 Mon Sep 17 00:00:00 2001 From: Salman S Date: Thu, 15 Sep 2022 20:07:08 +0700 Subject: [PATCH 0397/2609] Update Controllers example (#8214) Removing unnecessary imports from the same namespace --- controllers.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/controllers.md b/controllers.md index 50db5b3e8d1..7ddc2ec71d6 100644 --- a/controllers.md +++ b/controllers.md @@ -31,8 +31,7 @@ Let's take a look at an example of a basic controller. Note that the controller Date: Thu, 15 Sep 2022 15:08:27 +0200 Subject: [PATCH 0398/2609] Update channel authorization signature (#8213) The signature can be found here: https://github.com/pusher/pusher-js/blob/v7.4.0/src/core/auth/options.ts#L12 --- broadcasting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 15eb33e2323..7a5c7d5292a 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -562,10 +562,10 @@ window.Echo = new Echo({ channel_name: channel.name }) .then(response => { - callback(false, response.data); + callback(null, response.data); }) .catch(error => { - callback(true, error); + callback(error); }); } }; From c8a19f8d47e2f21fe8ba0b621c7509f4a5cc765d Mon Sep 17 00:00:00 2001 From: Salman S Date: Thu, 15 Sep 2022 20:08:55 +0700 Subject: [PATCH 0399/2609] Update binding primitives example (#8212) Update the example of binding primitives so it can be align with the style of other Service Container bindings. --- container.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/container.md b/container.md index ee41ac5bbff..be397e86d47 100644 --- a/container.md +++ b/container.md @@ -229,7 +229,9 @@ Sometimes you may have two classes that utilize the same interface, but you wish Sometimes you may have a class that receives some injected classes, but also needs an injected primitive value such as an integer. You may easily use contextual binding to inject any value your class may need: - $this->app->when('App\Http\Controllers\UserController') + use App\Http\Controllers\UserController; + + $this->app->when(UserController::class) ->needs('$variableName') ->give($value); From 0e2b7714fb346a050d24a16b4eaa4f4204e2c753 Mon Sep 17 00:00:00 2001 From: axeloz <1597611+axeloz@users.noreply.github.com> Date: Fri, 16 Sep 2022 20:40:41 +0200 Subject: [PATCH 0400/2609] Fixing new subscription via invoice example (#8219) * Fixing new subscription via invoice example * Update billing.md Co-authored-by: Taylor Otwell --- billing.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index ea147eadeee..de0c1ebe37b 100644 --- a/billing.md +++ b/billing.md @@ -647,9 +647,11 @@ The `create` method, which accepts [a Stripe payment method identifier](#storing Instead of collecting a customer's recurring payments automatically, you may instruct Stripe to email an invoice to the customer each time their recurring payment is due. Then, the customer may manually pay the invoice once they receive it. The customer does not need to provide a payment method up front when collecting recurring payments via invoices: - $user->newSubscription('default', 'price_monthly')->createAndSendInvoice(); + $user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [ + 'days_until_due' => 30 + ]); -The amount of time a customer has to pay their invoice before their subscription is canceled is determined by your subscription and invoice settings within the [Stripe dashboard](https://dashboard.stripe.com/settings/billing/automatic). +The amount of time a customer has to pay their invoice before their subscription is cancelled is determined by the `days_until_due` option. #### Quantities From 5abc89033bacc88053a9f754dbe4a44fcee33d23 Mon Sep 17 00:00:00 2001 From: ZedoX <75579178+Z3d0X@users.noreply.github.com> Date: Sat, 17 Sep 2022 22:21:25 +0500 Subject: [PATCH 0401/2609] Fix: inconsistent link to `multiple-of` rule (#8224) All the other rules seems to have the prefix `rule-` --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 5b328a88e54..ac7cea22881 100644 --- a/validation.md +++ b/validation.md @@ -853,7 +853,7 @@ Below is a list of all available validation rules and their function: [MIME Type By File Extension](#rule-mimes) [Min](#rule-min) [Min Digits](#rule-min-digits) -[Multiple Of](#multiple-of) +[Multiple Of](#rule-multiple-of) [Not In](#rule-not-in) [Not Regex](#rule-not-regex) [Nullable](#rule-nullable) @@ -1362,7 +1362,7 @@ The field under validation must have a minimum _value_. Strings, numerics, array The integer under validation must have a minimum length of _value_. - + #### multiple_of:_value_ The field under validation must be a multiple of _value_. From dd01d58713db279bc0d21e4c73b4625f19a89a2f Mon Sep 17 00:00:00 2001 From: Navi Date: Sat, 17 Sep 2022 22:53:02 +0530 Subject: [PATCH 0402/2609] Add default behaviour (#8223) --- queues.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index 32db7ea9d27..2f0df7f8ab8 100644 --- a/queues.md +++ b/queues.md @@ -1842,13 +1842,13 @@ For convenience, you may choose to automatically delete jobs with missing models ### Pruning Failed Jobs -You may delete all of the records in your application's `failed_jobs` table by invoking the `queue:prune-failed` Artisan command: +You may prune the records in your application's `failed_jobs` table by invoking the `queue:prune-failed` Artisan command: ```shell php artisan queue:prune-failed ``` -If you provide the `--hours` option to the command, only the failed job records that were inserted within the last N number of hours will be retained. For example, the following command will delete all of the failed job records that were inserted more than 48 hours ago: +By default, all the failed job records that are more than 24 hours old will be pruned. If you provide the `--hours` option to the command, only the failed job records that were inserted within the last N number of hours will be retained. For example, the following command will delete all the failed job records that were inserted more than 48 hours ago: ```shell php artisan queue:prune-failed --hours=48 From 061c7d1961617b11dee32e4838f1d9b9ae5aadd8 Mon Sep 17 00:00:00 2001 From: Ankur Kumar Date: Mon, 19 Sep 2022 20:01:37 +0530 Subject: [PATCH 0403/2609] [9.x] filter_unicode email validation (#8231) --- validation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/validation.md b/validation.md index ac7cea22881..00129ea224f 100644 --- a/validation.md +++ b/validation.md @@ -1103,6 +1103,7 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida - `dns`: `DNSCheckValidation` - `spoof`: `SpoofCheckValidation` - `filter`: `FilterEmailValidation` +- `filter_unicode`: `FilterEmailValidation::unicode()`
From 0b30ece7cf0636bf1c8f66aa1431ae41ba67f526 Mon Sep 17 00:00:00 2001 From: esolo Date: Mon, 19 Sep 2022 17:34:05 +0300 Subject: [PATCH 0404/2609] Update requests.md (#8227) * Update requests.md * Update requests.md Co-authored-by: Taylor Otwell --- requests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requests.md b/requests.md index 003f04dbdcf..9da8979d9f3 100644 --- a/requests.md +++ b/requests.md @@ -480,7 +480,7 @@ All cookies created by the Laravel framework are encrypted and signed with an au ## Input Trimming & Normalization -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. +By default, Laravel includes the `App\Http\Middleware\TrimStrings` and `Illuminate\Foundation\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. #### Disabling Input Normalization @@ -489,8 +489,8 @@ If you would like to disable this behavior for all requests, you may remove the If you would like to disable string trimming and empty string conversion for a subset of requests to your application, you may use the `skipWhen` method offered by both middleware. This method accepts a closure which should return `true` or `false` to indicate if input normalization should be skipped. Typically, the `skipWhen` method should be invoked in the `boot` method of your application's `AppServiceProvider`. ```php -use App\Http\Middleware\ConvertEmptyStringsToNull; use App\Http\Middleware\TrimStrings; +use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; /** * Bootstrap any application services. From 94cf5f8ff1b7eae0c1c2094dcee40a82c4f997a7 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 19 Sep 2022 16:41:48 +0200 Subject: [PATCH 0405/2609] [9.x] Reword createAndSendInvoice docs (#8233) * Update billing.md * Update billing.md Co-authored-by: Taylor Otwell --- billing.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index de0c1ebe37b..0b749a23067 100644 --- a/billing.md +++ b/billing.md @@ -647,12 +647,14 @@ The `create` method, which accepts [a Stripe payment method identifier](#storing Instead of collecting a customer's recurring payments automatically, you may instruct Stripe to email an invoice to the customer each time their recurring payment is due. Then, the customer may manually pay the invoice once they receive it. The customer does not need to provide a payment method up front when collecting recurring payments via invoices: + $user->newSubscription('default', 'price_monthly')->createAndSendInvoice(); + +The amount of time a customer has to pay their invoice before their subscription is cancelled is determined by the `days_until_due` option. By default, this is 30 days; however, you may provide a specific value for this option if you wish: + $user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [ 'days_until_due' => 30 ]); -The amount of time a customer has to pay their invoice before their subscription is cancelled is determined by the `days_until_due` option. - #### Quantities From 1d519c797d80a9bc196125648e07ba52602d58d8 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 20 Sep 2022 13:16:02 +0000 Subject: [PATCH 0406/2609] add `double` and `tinyText` to list of modifiable columns (#8235) --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index 8120e92fc27..f928181fd56 100644 --- a/migrations.md +++ b/migrations.md @@ -999,7 +999,7 @@ We could also modify a column to be nullable: }); > **Warning** -> The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). +> The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `double`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `tinyText`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). #### Renaming Columns From 5152aa4679fe0c66095212ee6e1d6e71eea3e870 Mon Sep 17 00:00:00 2001 From: ijpatricio Date: Tue, 20 Sep 2022 17:05:47 +0100 Subject: [PATCH 0407/2609] Notice `dispatchAfterResponse` needs webserver with FastCGI (#8237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like in [terminable middleware](https://laravel.com/docs/6.x/middleware#terminable-middleware), the user should be noticed that the web server should implement FastCGI. I ran through this issue myself on a dead simple app. I decided to go with a database driver queue, but it would be a better experience if I had known faster, it took me a while and a chat with a friend to realize this :) I hope I made the docs better! Thanks for Laravel ❤️ --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 2f0df7f8ab8..4cda844d2c8 100644 --- a/queues.md +++ b/queues.md @@ -688,7 +688,7 @@ If you would like to specify that a job should not be immediately available for #### Dispatching After The Response Is Sent To Browser -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: +Alternatively, the `dispatchAfterResponse` method delays dispatching a job until after the HTTP response is sent to the user's browser if your web server is using FastCGI. 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; From e07cb24fb27a55e0e071bec669f75cc0d87c264f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Sep 2022 13:29:11 -0500 Subject: [PATCH 0408/2609] document fillable exceptions --- eloquent.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/eloquent.md b/eloquent.md index f9f527c00c1..78d0b7ab3b9 100644 --- a/eloquent.md +++ b/eloquent.md @@ -764,6 +764,25 @@ If you would like to make all of your attributes mass assignable, you may define */ protected $guarded = []; + +#### Mass Assignment Exceptions + +By default, attributes that are not included in the `$fillable` array are silently discarded when performing mass-assignment operations. In production, this is expected behavior; however, during local development it can lead to confusion as to why model changes are not taking effect. + +If you wish, you may instruct Laravel to throw an exception when attempting to fill an unfillable attribute by invoking the `preventSilentlyDiscardingAttributes` method. Typically, this method should be invoked within the `boot` method of one of your application's service providers: + + use Illuminate\Database\Eloquent\Model; + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Model::preventSilentlyDiscardingAttributes($this->app->isLocal()); + } + ### Upserts From 0265253e51621490a9fb08103160d5c808e13b30 Mon Sep 17 00:00:00 2001 From: Italo Date: Tue, 20 Sep 2022 15:31:52 -0300 Subject: [PATCH 0409/2609] Adds ULID method to migration section (#8221) --- migrations.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/migrations.md b/migrations.md index f928181fd56..db5a6dc9c9c 100644 --- a/migrations.md +++ b/migrations.md @@ -375,6 +375,7 @@ The schema builder blueprint offers a variety of methods that correspond to the [float](#column-method-float) [foreignId](#column-method-foreignId) [foreignIdFor](#column-method-foreignIdFor) +[foreignUlid](#column-method-foreignUlid) [foreignUuid](#column-method-foreignUuid) [geometryCollection](#column-method-geometryCollection) [geometry](#column-method-geometry) @@ -423,6 +424,7 @@ The schema builder blueprint offers a variety of methods that correspond to the [unsignedSmallInteger](#column-method-unsignedSmallInteger) [unsignedTinyInteger](#column-method-unsignedTinyInteger) [uuidMorphs](#column-method-uuidMorphs) +[ulid](#column-method-ulid) [uuid](#column-method-uuid) [year](#column-method-year) @@ -526,6 +528,13 @@ The `foreignIdFor` method adds a `{column}_id UNSIGNED BIGINT` equivalent column $table->foreignIdFor(User::class); + +#### `foreignUlid()` {.collection-method} + +The `foreignUlid` method creates a `ULID` equivalent column: + + $table->foreignUlid('user_id'); + #### `foreignUuid()` {.collection-method} @@ -866,6 +875,13 @@ This method is intended to be used when defining the columns necessary for a pol $table->uuidMorphs('taggable'); + +#### `ulid()` {.collection-method} + +The `ulid` method creates a `ULID` equivalent column: + + $table->ulid('id'); + #### `uuid()` {.collection-method} From a98b6ec138f35ad9e4b804625d08c9ef3d478b1d Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 21 Sep 2022 05:20:31 +1000 Subject: [PATCH 0410/2609] [9.x] Document Factory `recycle` method (#8209) * Document Factory `recycle` method * Re-word `recycle` docs * formatting Co-authored-by: Taylor Otwell --- eloquent-factories.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/eloquent-factories.md b/eloquent-factories.md index 02c9e4973c0..acfc34499dd 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -15,6 +15,7 @@ - [Many To Many Relationships](#many-to-many-relationships) - [Polymorphic Relationships](#polymorphic-relationships) - [Defining Relationships Within Factories](#defining-relationships-within-factories) + - [Recycling An Existing Model For Relationships](#recycling-an-existing-model-for-relationships) ## Introduction @@ -487,3 +488,16 @@ If the relationship's columns depend on the factory that defines it you may assi 'content' => fake()->paragraph(), ]; } + + +### Recycling An Existing Model For Relationships + +If you have models that share a common relationship with another model, you may use the `recycle` method to ensure a single instance of the related model is recycled for all of the relationships. + +For example, imagine you have `Airline`, `Flight`, and `Ticket` models, where the ticket belongs to an airline and a flight, and the flight also belongs to an airline. When creating tickets, you will probably want the same airline for both the ticket and the flight, so you may pass an airline instance to the `recycle` method: + + Ticket::factory() + ->recycle(Airline::factory()->create()) + ->create(); + +You may find the `recycle` method particularly useful if you have models belonging to a common user or team. From aeb04150b0f26e1d06059ad44608b18a84665249 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Sep 2022 14:27:21 -0500 Subject: [PATCH 0411/2609] scope and read only filesystems --- filesystem.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/filesystem.md b/filesystem.md index f1304477a2d..b426656aae9 100644 --- a/filesystem.md +++ b/filesystem.md @@ -5,6 +5,7 @@ - [The Local Driver](#the-local-driver) - [The Public Disk](#the-public-disk) - [Driver Prerequisites](#driver-prerequisites) + - [Scoped & Read-Only Filesystems](#scoped-and-read-only-filesystems) - [Amazon S3 Compatible Filesystems](#amazon-s3-compatible-filesystems) - [Obtaining Disk Instances](#obtaining-disk-instances) - [On-Demand Disks](#on-demand-disks) @@ -34,7 +35,7 @@ Laravel's filesystem configuration file is located at `config/filesystems.php`. The `local` driver interacts with files stored locally on the server running the Laravel application while the `s3` driver is used to write to Amazon's S3 cloud storage service. -> **Note** +> **Note** > You may configure as many disks as you like and may even have multiple disks that use the same driver. @@ -123,7 +124,7 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu 'sftp' => [ 'driver' => 'sftp', 'host' => env('SFTP_HOST'), - + // Settings for basic authentication... 'username' => env('SFTP_USERNAME'), 'password' => env('SFTP_PASSWORD'), @@ -142,6 +143,29 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu // 'useAgent' => true, ], + +### Scoped & Read-Only Filesystems + +You may create a path scoped instance of any existing filesystem disk by defining a disk that utilizes the `scoped` driver. Scoped disks allow you to define a filesystem where all paths are automatically prefixed with a given path prefix. 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 +'s3-videos' => [ + 'driver' => 'scoped', + 'disk' => 's3', + 'prefix' => 'path/to/videos', +], +``` + +If you would like to specify that any filesystem disk should be "read-only", you may include the `read-only` configuration option in the disk's configuration array: + +```php +'s3-videos' => [ + 'driver' => 's3', + // ... + 'read-only' => true, +], +``` + ### Amazon S3 Compatible Filesystems From b77e32701449f5447182027897aa5fb062c78811 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Sep 2022 14:31:58 -0500 Subject: [PATCH 0412/2609] document bus except --- mocking.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/mocking.md b/mocking.md index e25660bb8b5..dbfa207ecab 100644 --- a/mocking.md +++ b/mocking.md @@ -198,6 +198,29 @@ You may pass a closure to the available methods in order to assert that a job wa return $job->order->id === $order->id; }); + +#### Faking A Subset Of Jobs + +If you only want to prevent certain jobs from being dispatched, you may pass the jobs that should be faked to the `fake` method: + + /** + * Test order process. + */ + public function test_orders_can_be_shipped() + { + Bus::fake([ + ShipOrder::class, + ]); + + // ... + } + +You may fake all jobs except for a set of specified jobs using the `except` method: + + Bus::fake()->except([ + ShipOrder::class, + ]); + ### Job Chains @@ -314,9 +337,9 @@ If you only want to fake event listeners for a specific set of events, you may p $order->update([...]); } -You may fake all events except for a set of specified events using the `fakeExcept` method: +You may fake all events except for a set of specified events using the `except` method: - Event::fakeExcept([ + Event::fake()->except([ OrderCreated::class, ]); From a5e94b693494a477178db95844b0923397cc4bca Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Sep 2022 15:07:06 -0500 Subject: [PATCH 0413/2609] docs on uuids --- eloquent.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/eloquent.md b/eloquent.md index 78d0b7ab3b9..2d5289235db 100644 --- a/eloquent.md +++ b/eloquent.md @@ -5,6 +5,7 @@ - [Eloquent Model Conventions](#eloquent-model-conventions) - [Table Names](#table-names) - [Primary Keys](#primary-keys) + - [UUID & ULID Keys](#uuid-and-ulid-keys) - [Timestamps](#timestamps) - [Database Connections](#database-connections) - [Default Attribute Values](#default-attribute-values) @@ -197,6 +198,69 @@ If your model's primary key is not an integer, you should define a protected `$k Eloquent requires each model to have at least one uniquely identifying "ID" that can serve as its primary key. "Composite" primary keys are not supported by Eloquent models. However, you are free to add additional multi-column, unique indexes to your database tables in addition to the table's uniquely identifying primary key. + +### UUID & ULID Keys + +Instead of using auto-incrementing integers as your Eloquent model's primary keys, you may choose to use UUIDs instead. UUIDs are universally unique alpha-numeric identifiers that are 36 characters long. + +If you would like a model to use a UUID key instead of an auto-incrementing integer key, you may use the `Illuminate\Database\Eloquent\Concerns\HasUuids` trait on the model. Of course, you should ensure that the model has a [UUID equivalent primary key column](/docs/{{version}}/migrations#column-method-uuid): + + use Illuminate\Database\Eloquent\Concerns\HasUuids; + use Illuminate\Database\Eloquent\Model; + + class Article extends Model + { + use HasUuids; + + // ... + } + + $article = Article::create(['title' => 'Traveling to Europe']); + + $article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5" + +By default, The `HasUuids` trait will generate ["ordered" UUIDs](/docs/{{version}}/helpers#method-str-ordered-uuid) for your models. These UUIDs are more efficient for indexed database storage because they can be sorted lexicographically. + +You can override the UUID generation process for a given model by defining a `newUniqueId` method on the model. In addition, you may specify which columns should receive UUIDs by defining a `uniqueIds` method on the model: + + use Ramsey\Uuid\Uuid; + + /** + * Generate a new UUID for the model. + * + * @return string + */ + public function newUniqueId() + { + return (string) Uuid::uuid4(); + } + + /** + * Get the columns that should receive a unique identifier. + * + * @return array + */ + public function uniqueIds() + { + return ['id', 'discount_code']; + } + +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: + + use Illuminate\Database\Eloquent\Concerns\HasUlids; + use Illuminate\Database\Eloquent\Model; + + class Article extends Model + { + use HasUlids; + + // ... + } + + $article = Article::create(['title' => 'Traveling to Asia']); + + $article->id; // "01gd4d3tgrrfqeda94gdbtdk5c" + ### Timestamps From 57e8175837030d8abb5495495fa164fdb64792f7 Mon Sep 17 00:00:00 2001 From: Italo Date: Tue, 20 Sep 2022 17:15:20 -0300 Subject: [PATCH 0414/2609] [9.x] Adds ULID helper section (#8226) * Adds ULID helper section Draft until release date. * Fixes anchor * Update helpers.md * Lowercased ULID which is what it returns. --- helpers.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/helpers.md b/helpers.md index d74176e2053..044e7ad45ee 100644 --- a/helpers.md +++ b/helpers.md @@ -153,6 +153,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::ucfirst](#method-str-ucfirst) [Str::ucsplit](#method-str-ucsplit) [Str::upper](#method-str-upper) +[Str::ulid](#method-str-ulid) [Str::uuid](#method-str-uuid) [Str::wordCount](#method-str-word-count) [Str::words](#method-str-words) @@ -2010,6 +2011,17 @@ The `Str::upper` method converts the given string to uppercase: // LARAVEL + +#### `Str::ulid()` {.collection-method} + +The `Str::ulid` method generates a ULID: + + use Illuminate\Support\Str; + + return (string) Str::ulid(); + + // 01gd6r360bp37zj17nxb55yv40 + #### `Str::uuid()` {.collection-method} From 2cbeedfcb881336ba57b6aa678bcda12ea3319bd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Sep 2022 15:55:27 -0500 Subject: [PATCH 0415/2609] document withFakeBatch --- mocking.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mocking.md b/mocking.md index dbfa207ecab..76f0d6c4e3b 100644 --- a/mocking.md +++ b/mocking.md @@ -258,6 +258,15 @@ The `Bus` facade's `assertBatched` method may be used to assert that a [batch of $batch->jobs->count() === 10; }); +In addition, you may occasionally need to test an individual job's interaction with its underlying batch. For example, you may need to test if a job cancelled further processing for its batch. To accomplish this, you need to assign a fake batch to the job via the `withFakeBatch` method. The `withFakeBatch` method returns a tuple containing the job instance and the fake batch: + + [$job, $batch] = (new ShipOrder)->withFakeBatch(); + + $job->handle(); + + $this->assertTrue($batch->cancelled()); + $this->assertEmpty($batch->added); + ## Event Fake From 09436a446e8ec890ae979c77dcdf87083f84149c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Sep 2022 15:56:24 -0500 Subject: [PATCH 0416/2609] wip --- mocking.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mocking.md b/mocking.md index 76f0d6c4e3b..bffed33b3d3 100644 --- a/mocking.md +++ b/mocking.md @@ -258,6 +258,9 @@ The `Bus` facade's `assertBatched` method may be used to assert that a [batch of $batch->jobs->count() === 10; }); + +#### Testing Job / Batch Interaction + In addition, you may occasionally need to test an individual job's interaction with its underlying batch. For example, you may need to test if a job cancelled further processing for its batch. To accomplish this, you need to assign a fake batch to the job via the `withFakeBatch` method. The `withFakeBatch` method returns a tuple containing the job instance and the fake batch: [$job, $batch] = (new ShipOrder)->withFakeBatch(); From d9f9c3bc56d569b2297249014c13232ab739af33 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 22 Sep 2022 00:01:10 +1000 Subject: [PATCH 0417/2609] [9.x] Document withoutTimestamps (#8238) * document withoutTimestamps * Update eloquent.md Co-authored-by: Taylor Otwell --- eloquent.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eloquent.md b/eloquent.md index 2d5289235db..d2dfbaa7c0f 100644 --- a/eloquent.md +++ b/eloquent.md @@ -310,6 +310,10 @@ If you need to customize the names of the columns used to store the timestamps, const UPDATED_AT = 'updated_date'; } +If you would like to perform model operations without the model having its `updated_at` timestamp modified, you may operate on the model within a closure given to the `withoutTimestamps` method: + + Model::withoutTimestamps(fn () => $post->incrememt(['reads'])); + ### Database Connections From 549fd9fded569e7458048b019f89cc181decf39c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 22 Sep 2022 00:04:28 +1000 Subject: [PATCH 0418/2609] [9.x] Document blade alises (#8232) * document blade alises * Update vite.md Co-authored-by: Taylor Otwell --- vite.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/vite.md b/vite.md index 848a128ef32..5d01c20487b 100644 --- a/vite.md +++ b/vite.md @@ -17,6 +17,7 @@ - [Working With Blade & Routes](#working-with-blade-and-routes) - [Processing Static Assets With Vite](#blade-processing-static-assets) - [Refreshing On Save](#blade-refreshing-on-save) + - [Aliases](#blade-aliases) - [Custom Base URLs](#custom-base-urls) - [Environment Variables](#environment-variables) - [Disabling Vite In Tests](#disabling-vite-in-tests) @@ -430,6 +431,27 @@ export default defineConfig({ }); ``` + +### Aliases + +It is common in JavaScript applications to [create aliases](#aliases) to regularly referenced directories. But, you may also create aliases to use in Blade by using the `macro` method on the `Illuminate\Support\Vite` class. Typically, "macros" should be defined within the `boot` method of a [service provider](/docs/{{version}}/providers): + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Vite::macro('image', fn ($asset) => $this->asset("/resources/images/{$asset}")); + } + +Once a macro has been defined, it can be invoked within your templates. For example, we can use the `image` macro defined above to reference an asset located at `resources/images/logo.png`: + +```blade +Laravel Logo +``` + ## Custom Base URLs From ee5d18e8ebe33f38a402f18f2d43a3520489d724 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 22 Sep 2022 00:15:05 +1000 Subject: [PATCH 0419/2609] [9.x] Update Vite plugin refresh paths (#8217) * document new refresh paths * formatting Co-authored-by: Taylor Otwell --- vite.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 5d01c20487b..3e26569a72a 100644 --- a/vite.md +++ b/vite.md @@ -392,7 +392,13 @@ export default defineConfig({ }); ``` -When the `refresh` option is `true`, saving files in `resources/views/**`, `app/View/Components/**`, and `routes/**` will trigger the browser to perform a full page refresh while you are running `npm run dev`. +When the `refresh` option is `true`, saving files in the following directories will trigger the browser to perform a full page refresh while you are running `npm run dev`: + +- `app/View/Components/**` +- `lang/**` +- `resources/lang/**` +- `resources/views/**` +- `routes/**` Watching the `routes/**` directory is useful if you are utilizing [Ziggy](https://github.com/tighten/ziggy) to generate route links within your application's frontend. From d872a4782ce494cfdef86f4ab10f25b50b78f434 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 22 Sep 2022 00:32:27 +1000 Subject: [PATCH 0420/2609] [9.x] Update TLS configuration options (#8164) * document TLS configuration * document vite plugin * formatting Co-authored-by: Taylor Otwell --- vite.md | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/vite.md b/vite.md index 3e26569a72a..cad8ef1acfa 100644 --- a/vite.md +++ b/vite.md @@ -131,19 +131,46 @@ The Laravel plugin also supports multiple entry points and advanced configuratio #### Working With A Secure Development Server -If your development web server is running on HTTPS, including Valet's [secure command](/docs/{{version}}/valet#securing-sites), you may run into issues connecting to the Vite development server. You may configure Vite to also run on HTTPS by adding the following to your `vite.config.js` configuration file: +If your local development web server is serving your application via HTTPS, you may run into issues connecting to the Vite development server. + +If you are using [Laravel Valet](/docs/{{version}}/valet) for local development and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may configure the Vite development server to automatically use Valet's generated TLS certificates: ```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + valetTls: 'my-app.test', // [!tl add] + }), + ], +}); +``` + +When using another web server, you should generate a trusted certificate and manually configure Vite to use the generated certificates: + +```js +// ... +import fs from 'fs'; // [tl! add] + +const host = 'my-app.test'; // [tl! add] + export default defineConfig({ // ... - server: { // [tl! add] - https: true, // [tl! add] - host: 'localhost', // [tl! add] - }, // [tl! add] + server: { // [!tl add] + host, // [!tl add] + hmr: { host }, // [!tl add] + https: { // [!tl add] + key: fs.readFileSync(`/path/to/${host}.key`), // [!tl add] + cert: fs.readFileSync(`/path/to/${host}.crt`), // [!tl add] + }, // [!tl add] + }, // [!tl add] }); ``` -You will also need to accept the certificate warning for Vite's development server in your browser by following the "Local" link in your console when running the `npm run dev` command. +If you are unable to generate a trusted certificate for your system, you may install and configure the [`@vitejs/plugin-basic-ssl` plugin](https://github.com/vitejs/vite-plugin-basic-ssl). When using untrusted certificates, you will need to accept the certificate warning for Vite's development server in your browser by following the "Local" link in your console when running the `npm run dev` command. ### Loading Your Scripts And Styles From 65882618d8ef4432d3c51c5f0563c42642a85cf4 Mon Sep 17 00:00:00 2001 From: Italo Date: Wed, 21 Sep 2022 11:47:08 -0300 Subject: [PATCH 0421/2609] [9.x] Adds support for callbacks on auth attempt (#8239) * Adds support for callbacks on the auth attempt * Update authentication.md Co-authored-by: Taylor Otwell --- authentication.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/authentication.md b/authentication.md index 25581e0c034..d50f5b4db17 100644 --- a/authentication.md +++ b/authentication.md @@ -276,6 +276,16 @@ If you wish, you may also add extra query conditions to the authentication query // Authentication was successful... } +For complex query conditions, you may provide a closure in your array of credentials. This closure will be invoked with the query instance, allowing you to customize the query based on your application's needs: + + if (Auth::attempt([ + 'email' => $email, + 'password' => $password, + fn ($query) => $query->has('activeSubscription'), + ]) { + // Authentication was successful... + } + > **Warning** > 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. From d1cf1dfb0cf155522f323fb51d944151afd07199 Mon Sep 17 00:00:00 2001 From: Kevin Bui Date: Sat, 24 Sep 2022 00:10:10 +1000 Subject: [PATCH 0422/2609] [9.x] Add the whereBetweenColumns method and relevant ones. (#8249) * Add the whereBetweenColumns method and relevant ones. * Update queries.md * Update queries.md Co-authored-by: Taylor Otwell --- queries.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/queries.md b/queries.md index 8e9e7917824..d6739a652ba 100644 --- a/queries.md +++ b/queries.md @@ -532,6 +532,20 @@ The `whereNotBetween` method verifies that a column's value lies outside of two ->whereNotBetween('votes', [1, 100]) ->get(); +**whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns** + +The `whereBetweenColumns` method verifies that a column's value is between the two values of two columns in the same table row: + + $patients = DB::table('patients') + ->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); + +The `whereNotBetweenColumns` method verifies that a column's value lies outside the two values of two columns in the same table row: + + $patients = DB::table('patients') + ->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); + **whereIn / whereNotIn / orWhereIn / orWhereNotIn** The `whereIn` method verifies that a given column's value is contained within the given array: From 1cf96e09fde4871abe47ed73d71d3b9d4160e042 Mon Sep 17 00:00:00 2001 From: Kevin Bui Date: Sat, 24 Sep 2022 00:12:01 +1000 Subject: [PATCH 0423/2609] [9.x] Add the orderByPivot method. (#8248) * Add the orderByPivot method. * Update eloquent-relationships.md Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 45584935c7c..bf72df17880 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -11,6 +11,7 @@ - [Many To Many Relationships](#many-to-many) - [Retrieving Intermediate Table Columns](#retrieving-intermediate-table-columns) - [Filtering Queries Via Intermediate Table Columns](#filtering-queries-via-intermediate-table-columns) + - [Ordering Queries Via Intermediate Table Columns](#ordering-queries-via-intermediate-table-columns) - [Defining Custom Intermediate Table Models](#defining-custom-intermediate-table-models) - [Polymorphic Relationships](#polymorphic-relationships) - [One To One](#one-to-one-polymorphic-relations) @@ -662,6 +663,15 @@ You can also filter the results returned by `belongsToMany` relationship queries ->as('subscriptions') ->wherePivotNotNull('expired_at'); + +### Ordering Queries Via Intermediate Table Columns + +You can order the results returned by `belongsToMany` relationship queries using the `orderByPivot` method. In the following example, we will retrieve all of the latest badges for the user: + + return $this->belongsToMany(Badge::class) + ->where('rank', 'gold') + ->orderByPivot('created_at', 'desc'); + ### Defining Custom Intermediate Table Models From 8eb73d9a6f7f3057626025b57834778b16dfe738 Mon Sep 17 00:00:00 2001 From: Adam Lee <53559175+adam-code-labx@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:56:20 +0100 Subject: [PATCH 0424/2609] Corrected access level for TestCase setUp method when using Pest (#8251) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index cad8ef1acfa..d89bf640b08 100644 --- a/vite.md +++ b/vite.md @@ -551,7 +551,7 @@ abstract class TestCase extends BaseTestCase { use CreatesApplication; - public function setUp(): void// [tl! add:start] + protected function setUp(): void// [tl! add:start] { parent::setUp(); From 6d0ba2424183afa7a7b005a1acf001e86d45f486 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Tue, 27 Sep 2022 14:46:46 +0100 Subject: [PATCH 0425/2609] [9.x] Adds documentation for `env:encrypt` and `env:decrypt` (#8253) * Add environment encryption * formatting Co-authored-by: Taylor Otwell --- configuration.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/configuration.md b/configuration.md index f1af5d1a9ef..aeccc1177c6 100644 --- a/configuration.md +++ b/configuration.md @@ -5,6 +5,7 @@ - [Environment Variable Types](#environment-variable-types) - [Retrieving Environment Configuration](#retrieving-environment-configuration) - [Determining The Current Environment](#determining-the-current-environment) + - [Encrypting Environment Files](#encrypting-environment-files) - [Accessing Configuration Values](#accessing-configuration-values) - [Configuration Caching](#configuration-caching) - [Debug Mode](#debug-mode) @@ -109,6 +110,70 @@ You may also pass arguments to the `environment` method to determine if the envi > **Note** > The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. + +### Encrypting Environment Files + +Unencrypted environment files should never be stored in source control. However, Laravel allows you to encrypt your environment files so that they may be safely be added to source control with the rest of your application. + + +#### Encryption + +To encrypt an environment file, you may use the `env:encrypt` command: + +```shell +php artisan env:encrypt +``` + +Running the `env:encrypt` command will encrypt your `.env` file and place the encrypted contents in an `.env.encrypted` file. The decryption key is presented in the output of the command and should be stored in a secure password manager. If you would like to provide your own encryption key you may use the `--key` option when invoking the command: + +```shell +php artisan env:encrypt --key=3UVsEgGVK36XN82KKeyLFMhvosbZN1aF +``` + +> **Note** +> The length of the key provided should match the key length required by the encryption cipher being used. By default, Laravel will use the `AES-256-CBC` cipher which requires a 32 character key. You are free to use any cipher supported by Laravel's [encrypter](/docs/{{version}}/encryption) by passing the `--cipher` option when invoking the command. + +If your application has multiple environment files, such as `.env` and `.env.staging`, you may specify the environment file that should be encrypted by providing the environment name via the `--env` option: + +```shell +php artisan env:encrypt --env=staging +``` + + +#### Decryption + +To decrypt an environment file, you may use the `env:decrypt` command. This command requires a decryption key, which Laravel will retrieve from the `LARAVEL_ENV_ENCRYPTION_KEY` environment variable: + +```shell +php artisan env:decrypt +``` + +Or, the key may be provided directly to the command via the `--key` option: + +```shell +php artisan env:decrypt --key=3UVsEgGVK36XN82KKeyLFMhvosbZN1aF +``` + +When the `env:decrypt` command is invoked, Laravel will decrypt the contents of the `.env.encrypted` file and place the decrypted contents in the `.env` file. + +The `--cipher` option may be provided to the `env:decrypt` command in order to use a custom encryption cipher: + +```shell +php artisan env:decrypt --key=qUWuNRdfuImXcKxZ --cipher=AES-128-CBC +``` + +If your application has multiple environment files, such as `.env` and `.env.staging`, you may specify the environment file that should be decrypted by providing the environment name via the `--env` option: + +```shell +php artisan env:decrypt --env=staging +``` + +In order to overwrite an existing environment file, you may provide the `--force` option to the `env:decrypt` command: + +```shell +php artisan env:decrypt --force +``` + ## Accessing Configuration Values From 7db02e6549ce87df8936a66285ea6d6314d8652e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Sep 2022 08:47:21 -0500 Subject: [PATCH 0426/2609] wip --- scheduling.md | 1 + 1 file changed, 1 insertion(+) diff --git a/scheduling.md b/scheduling.md index a01caff9c00..8ffb19fa780 100644 --- a/scheduling.md +++ b/scheduling.md @@ -117,6 +117,7 @@ Method | Description `->everyThirtyMinutes();` | Run the task every thirty minutes `->hourly();` | Run the task every hour `->hourlyAt(17);` | Run the task every hour at 17 minutes past the hour +`->everyOddHour();` | Run the task every odd hour `->everyTwoHours();` | Run the task every two hours `->everyThreeHours();` | Run the task every three hours `->everyFourHours();` | Run the task every four hours From d0ee784797ecb08553d0b59a178a6e5cba72663a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Sep 2022 08:55:20 -0500 Subject: [PATCH 0427/2609] document short attribute syntax --- blade.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/blade.md b/blade.md index a54edff42d7..189382f86d5 100644 --- a/blade.md +++ b/blade.md @@ -781,6 +781,19 @@ The `$alertType` argument may be provided to the component like so: ``` + +#### Short Attribute Syntax + +When passing attributes to components, you may also use a "short attribute" syntax. This is often convenient since attribute names frequently match the variable names they correspond to: + +```blade +{-- Short attribute syntax... --} + + +{-- Is equivalent to... --} + +``` + #### Escaping Attribute Rendering From be53be9d5f62d92b3b640ea0be93dceda8ff9cdf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Sep 2022 09:01:49 -0500 Subject: [PATCH 0428/2609] document shared lock keys --- queues.md | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index 4cda844d2c8..2bb58aa0ad6 100644 --- a/queues.md +++ b/queues.md @@ -508,7 +508,7 @@ For example, let's imagine you have a queued job that updates a user's credit sc 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 released job will be attempted again: +Any overlapping jobs of the same type 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. @@ -544,9 +544,44 @@ The `WithoutOverlapping` middleware is powered by Laravel's atomic lock feature. return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; } -> **Warning** +> **Warning** > 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. + +#### Sharing Lock Keys Across Job Classes + +By default, the `WithoutOverlapping` middleware will only prevent overlapping jobs of the same class. So, although two different job classes may use the same lock key, they will not be prevented from overlapping. However, you can instruct Laravel to apply the key across job classes using the `shared` method: + +```php +use Illuminate\Queue\Middleware\WithoutOverlapping; + +class ProviderIsDown +{ + // ... + + + public function middleware() + { + return [ + (new WithoutOverlapping("status:{$this->provider}"))->shared(), + ]; + } +} + +class ProviderIsUp +{ + // ... + + + public function middleware() + { + return [ + (new WithoutOverlapping("status:{$this->provider}"))->shared(), + ]; + } +} +``` + ### Throttling Exceptions From 7944ff37d67d8d62c5f5ae567a6331b9bd15c2aa Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Tue, 27 Sep 2022 15:02:50 +0100 Subject: [PATCH 0429/2609] Document `pauseIf()` / `pauseUnless()` (#8254) * Document `pauseIf()` * Document `pauseUnless()` --- dusk.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/dusk.md b/dusk.md index 3cdd87debb3..9767ef82412 100644 --- a/dusk.md +++ b/dusk.md @@ -62,7 +62,7 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome composer require --dev laravel/dusk ``` -> **Warning** +> **Warning** > If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operation system: @@ -73,7 +73,7 @@ php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. -> **Note** +> **Note** > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -95,7 +95,7 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> **Warning** +> **Warning** > Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. @@ -161,7 +161,7 @@ Most of the tests you write will interact with pages that retrieve data from you use DatabaseMigrations; } -> **Warning** +> **Warning** > SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. @@ -185,7 +185,7 @@ The `dusk` command accepts any argument that is normally accepted by the PHPUnit php artisan dusk --group=foo ``` -> **Note** +> **Note** > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -382,7 +382,7 @@ Often, you will be testing pages that require authentication. You can use Dusk's ->visit('/home'); }); -> **Warning** +> **Warning** > After using the `loginAs` method, the user session will be maintained for all tests within the file. @@ -573,7 +573,7 @@ The `attach` method may be used to attach a file to a `file` input element. Like $browser->attach('photo', __DIR__.'/photos/mountains.png'); -> **Warning** +> **Warning** > The attach function requires the `Zip` PHP extension to be installed and enabled on your server. @@ -604,7 +604,7 @@ You may use the `seeLink` method to determine if a link with the given display t // ... } -> **Warning** +> **Warning** > These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. @@ -618,7 +618,7 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" $browser->keys('.app', ['{command}', 'j']); -> **Note** +> **Note** > All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). @@ -741,6 +741,14 @@ If you just need to pause the test for a given number of milliseconds, use the ` $browser->pause(1000); +If you need to pause the test only if a given condition is `true`, use the `pauseIf` method: + + $browser->pauseIf(App::isProduction(), 1000); + +If you need to pause the test unless a given condition is `true`, use the `pauseUnless` method: + + $browser->pauseUnless(App::environment('testing'), 1000); + #### Waiting For Selectors @@ -1830,7 +1838,7 @@ Once the component has been defined, we can easily select a date within the date ## Continuous Integration -> **Warning** +> **Warning** > Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. From e244d630fda135c5ca3b0b1269c4d67f7a1d3de3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Sep 2022 09:03:26 -0500 Subject: [PATCH 0430/2609] wip --- dusk.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dusk.md b/dusk.md index 9767ef82412..fccfea7be09 100644 --- a/dusk.md +++ b/dusk.md @@ -743,9 +743,9 @@ If you just need to pause the test for a given number of milliseconds, use the ` If you need to pause the test only if a given condition is `true`, use the `pauseIf` method: - $browser->pauseIf(App::isProduction(), 1000); + $browser->pauseIf(App::environment('production'), 1000); -If you need to pause the test unless a given condition is `true`, use the `pauseUnless` method: +Likewise, if you need to pause the test unless a given condition is `true`, you may use the `pauseUnless` method: $browser->pauseUnless(App::environment('testing'), 1000); From 5d5a9c0c7c60ee1aaebcafce42af1cbc526fed47 Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Tue, 27 Sep 2022 18:43:33 +0100 Subject: [PATCH 0431/2609] Document `assertIndeterminate()` and `waitForInput()` (#8255) * Document `assertIndeterminate()` * Document `waitForInput()` * Update dusk.md Co-authored-by: Taylor Otwell --- dusk.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dusk.md b/dusk.md index fccfea7be09..27058082b61 100644 --- a/dusk.md +++ b/dusk.md @@ -830,6 +830,17 @@ The `waitForLink` method may be used to wait until the given link text is displa // Wait a maximum of one second for the link... $browser->waitForLink('Create', 1); + +#### Waiting For Inputs + +The `waitForInput` method may be used to wait until the given input field is visible on the page: + + // Wait a maximum of five seconds for the input... + $browser->waitForInput($field); + + // Wait a maximum of one second for the input... + $browser->waitForInput($field, 1); + #### Waiting On The Page Location @@ -986,6 +997,7 @@ Dusk provides a variety of assertions that you may make against your application [assertInputValueIsNot](#assert-input-value-is-not) [assertChecked](#assert-checked) [assertNotChecked](#assert-not-checked) +[assertIndeterminate](#assert-indeterminate) [assertRadioSelected](#assert-radio-selected) [assertRadioNotSelected](#assert-radio-not-selected) [assertSelected](#assert-selected) @@ -1301,6 +1313,13 @@ Assert that the given checkbox is not checked: $browser->assertNotChecked($field); + +#### assertIndeterminate + +Assert that the given checkbox is in an indeterminate state: + + $browser->assertIndeterminate($field); + #### assertRadioSelected From e6f5c5e4a13c775b777a9a3efc67e227a6ef101e Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 27 Sep 2022 18:58:13 +0100 Subject: [PATCH 0432/2609] [9.x] Adds `Benchmark` facade documentation (#8250) * Adds `Benchmark` documentation * Updates wording * Minor changes * Adjusts documentation * formatting Co-authored-by: Taylor Otwell --- helpers.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/helpers.md b/helpers.md index 044e7ad45ee..3098be210b5 100644 --- a/helpers.md +++ b/helpers.md @@ -2,6 +2,8 @@ - [Introduction](#introduction) - [Available Methods](#available-methods) +- [Other Utilities](#other-utilities) + - [Benchmarking](#benchmarking) ## Introduction @@ -3948,3 +3950,29 @@ The `with` function returns the value it is given. If a closure is passed as the $result = with(5, null); // 5 + + +## Other Utilities + + +### Benchmarking + +Sometimes you may wish to quickly test the performance of certain parts of your application. On those occasions, you may utilize the `Benchmark` support class to measure the number of milliseconds it takes for the given callbacks to complete: + + User::find(1)); // 0.1 ms + + Benchmark::dd([ + 'Scenario 1' => fn () => User::count(), // 0.5 ms + 'Scenario 2' => fn () => User::all()->count(), // 20.0 ms + ]); + +By default, the given callbacks will be executed once (one iteration), and their duration will be displayed in the browser / console. + +To invoke a callback more than once, you may specify the number of iterations that the callback should be invoked as the second argument to the method. When executing a callback more than once, the `Benchmark` class will return the average amount of milliseconds it took to execute the callback across all iterations: + + Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms From 27bbe18e1fa0ba3c8c4370102900d64ce502faaa Mon Sep 17 00:00:00 2001 From: Majkel Date: Wed, 28 Sep 2022 15:12:31 +0200 Subject: [PATCH 0433/2609] [blade] Single statement `@php` (#8257) * adds single-statement @ php * fixes word order * Update blade.md Co-authored-by: Taylor Otwell --- blade.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blade.md b/blade.md index 189382f86d5..c6008f8df11 100644 --- a/blade.md +++ b/blade.md @@ -593,6 +593,12 @@ In some situations, it's useful to embed PHP code into your views. You can use t @endphp ``` +If you only need to write a single PHP statement, you can include the statement within the `@php` directive: + +```blade +@php($counter = 1) +``` + ### Comments From 4ac7dea489321235738d59612137c8837f7498e6 Mon Sep 17 00:00:00 2001 From: Jordan Edmondson <51299280+jordmondson@users.noreply.github.com> Date: Wed, 28 Sep 2022 17:41:04 +0100 Subject: [PATCH 0434/2609] Add reference to --seeder option (#8258) * Add reference to --seeder option * Update seeding.md Co-authored-by: Taylor Otwell --- seeding.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/seeding.md b/seeding.md index 0d1eec442e3..aa3cee553dd 100644 --- a/seeding.md +++ b/seeding.md @@ -138,10 +138,12 @@ php artisan db:seed php artisan db:seed --class=UserSeeder ``` -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: +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. The `--seeder` option may be used to specify a specific seeder to run: ```shell php artisan migrate:fresh --seed + +php artisan migrate:fresh --seed --seeder=UserSeeder ``` From de67e86f3e02d74e5a1c5c28347b71e67562aa43 Mon Sep 17 00:00:00 2001 From: Perry <11609290+PerryvanderMeer@users.noreply.github.com> Date: Wed, 28 Sep 2022 20:58:17 +0200 Subject: [PATCH 0435/2609] Fix typo in blade comment (#8259) {-- > {{-- --- blade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade.md b/blade.md index c6008f8df11..419403f70e7 100644 --- a/blade.md +++ b/blade.md @@ -793,10 +793,10 @@ The `$alertType` argument may be provided to the component like so: When passing attributes to components, you may also use a "short attribute" syntax. This is often convenient since attribute names frequently match the variable names they correspond to: ```blade -{-- Short attribute syntax... --} +{{-- Short attribute syntax... --}} -{-- Is equivalent to... --} +{{-- Is equivalent to... --}} ``` From f2d35d27b533394c87877aee9df25fee0de964c6 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Thu, 29 Sep 2022 14:17:42 +0100 Subject: [PATCH 0436/2609] [9.x] Adds link to encryption documentation (#8263) * Add link to encryption * Update configuration.md Co-authored-by: Taylor Otwell --- configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configuration.md b/configuration.md index aeccc1177c6..2b2c0811721 100644 --- a/configuration.md +++ b/configuration.md @@ -52,6 +52,8 @@ If you are developing with a team, you may wish to continue including a `.env.ex Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. +However, it is possible to encrypt your environment file using Laravel's built-in [environment encryption](#encrypting-environment-files). Encrypted environment files may be placed in source control safely. + #### Additional Environment Files From 737a69d5aa62193ff00ac066597e9ea9de2870bc Mon Sep 17 00:00:00 2001 From: gpanos Date: Thu, 29 Sep 2022 15:20:18 +0200 Subject: [PATCH 0437/2609] Removes unused use from withoutEvents callback (#8262) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index d2dfbaa7c0f..93928f0a015 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1595,7 +1595,7 @@ You may occasionally need to temporarily "mute" all events fired by a model. You use App\Models\User; - $user = User::withoutEvents(function () use () { + $user = User::withoutEvents(function () { User::findOrFail(1)->delete(); return User::find(2); From b7059b1eea1949b6f2d6d8a580db67aba0213104 Mon Sep 17 00:00:00 2001 From: Evan Goss Date: Thu, 29 Sep 2022 06:44:45 -0700 Subject: [PATCH 0438/2609] [9.x] Add valet list command (#8260) * Add valet list command * Update valet.md Co-authored-by: Taylor Otwell --- valet.md | 1 + 1 file changed, 1 insertion(+) diff --git a/valet.md b/valet.md index f49bd01e3a3..0250baf146e 100644 --- a/valet.md +++ b/valet.md @@ -458,6 +458,7 @@ If you would like to define a custom Valet driver for a single application, crea Command | Description ------------- | ------------- +`valet list` | Display a list of all Valet commands. `valet forget` | Run this command from a "parked" directory to remove it from the parked directory list. `valet log` | View a list of logs which are written by Valet's services. `valet paths` | View all of your "parked" paths. From 77ed07a4a6185b9ff37def76bffe8a980e7d4387 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 29 Sep 2022 10:51:47 -0500 Subject: [PATCH 0439/2609] move env vars to the "job" level (#8264) Github Actions allows you to place environment variables at either the "workflow", "job", or "step" level. This commit moves them from the "step" level to the "job" level. https://docs.github.com/en/actions/learn-github-actions/environment-variables Dusk is a unique situation because running `php artisan dusk` starts the tests in one process, but hitting `$this->browse()` within the test spawns a **new** process, that **DOES NOT** inherit the environment variables set at the "step" level. Therefore, for Dusk CI on Github, it's better to set ENV variables at the "job" level so both the "test process" and "browser process" inherit them. Added the `DB_USERNAME`, `DB_PASSWORD`, and `MAIL_MAILER` env variables. The current value in the default `.env.example` for the password is empty, so we'll set it here to match the default Github Action MySQL password. The default `MAIL_MAILER` value is "smtp" and points to Mailhog, which **is not** running on Github Actions, and will throw an Exception if an email is attempted to be sent, so we'll use "log" to avoid that. --- dusk.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dusk.md b/dusk.md index 27058082b61..cc073dbd204 100644 --- a/dusk.md +++ b/dusk.md @@ -1920,6 +1920,11 @@ jobs: dusk-php: runs-on: ubuntu-latest + env: + APP_URL: "http://127.0.0.1:8000/" + DB_USERNAME: root + DB_PASSWORD: root + MAIL_MAILER: log steps: - uses: actions/checkout@v3 - name: Prepare The Environment @@ -1939,8 +1944,6 @@ jobs: - name: Run Laravel Server run: php artisan serve --no-reload & - name: Run Dusk Tests - env: - APP_URL: "http://127.0.0.1:8000/" run: php artisan dusk - name: Upload Screenshots if: failure() From d2a4f13ce572845923264ca43911015d8b83b958 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 29 Sep 2022 10:53:05 -0500 Subject: [PATCH 0440/2609] use chrome driver `--detect` option (#8265) the `--detect` flag was added to Dusk in https://github.com/laravel/dusk/pull/816 which automatically downloads the correct driver based on the version of Chrome currently installed on the system. --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index cc073dbd204..366ab0d2848 100644 --- a/dusk.md +++ b/dusk.md @@ -1938,7 +1938,7 @@ jobs: - name: Generate Application Key run: php artisan key:generate - name: Upgrade Chrome Driver - run: php artisan dusk:chrome-driver `/opt/google/chrome/chrome --version | cut -d " " -f3 | cut -d "." -f1` + run: php artisan dusk:chrome-driver --detect - name: Start Chrome Driver run: ./vendor/laravel/dusk/bin/chromedriver-linux & - name: Run Laravel Server From a33d32df460cc578404728f2227d389c37273349 Mon Sep 17 00:00:00 2001 From: ibrainventures Date: Thu, 29 Sep 2022 19:04:31 +0200 Subject: [PATCH 0441/2609] Missing information to toggle with intermediate table values (#8266) * Missing information to toggle with intermediate table values * Update eloquent-relationships.md * Update eloquent-relationships.md Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index bf72df17880..95d66119eac 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1929,6 +1929,13 @@ The many-to-many relationship also provides a `toggle` method which "toggles" th $user->roles()->toggle([1, 2, 3]); +You may also pass additional intermediate table values with the IDs: + + $user->roles()->toggle([ + 1 => ['expires' => true], + 2 => ['expires' => true], + ]); + #### Updating A Record On The Intermediate Table From abe48be7c4c25a6966c86aceac7c5ca3edfd2d90 Mon Sep 17 00:00:00 2001 From: Harvey Christian Pacleb Date: Sat, 1 Oct 2022 03:20:48 +0800 Subject: [PATCH 0442/2609] [9.x] Update outdated PHPUnit link and add simple documentation about phpunit.dusk.xml (#8268) * Use operating system instead of operation system * Add XML Configration file docs * Clean up * Update dusk.md Co-authored-by: Taylor Otwell --- dusk.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dusk.md b/dusk.md index 366ab0d2848..55db79d4d66 100644 --- a/dusk.md +++ b/dusk.md @@ -65,7 +65,7 @@ composer require --dev laravel/dusk > **Warning** > If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. -After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operation system: +After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operating system: ```shell php artisan dusk:install @@ -179,7 +179,7 @@ If you had test failures the last time you ran the `dusk` command, you may save php artisan dusk:fails ``` -The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group): +The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/9.5/annotations.html#group): ```shell php artisan dusk --group=foo From 24dadff8336e81d2601b400ab73b1f3d46fdaa3f Mon Sep 17 00:00:00 2001 From: Maarten Paauw Date: Mon, 3 Oct 2022 15:22:58 +0200 Subject: [PATCH 0443/2609] Change global scope namespace to `App\Models\Scopes` (#8271) Co-authored-by: Maarten Paauw --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 93928f0a015..cbba013618d 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1162,7 +1162,7 @@ The `Scope` interface requires you to implement one method: `apply`. The `apply` Date: Mon, 3 Oct 2022 22:35:32 +0200 Subject: [PATCH 0444/2609] [9.x] add guest checkout docs (#8252) * add guest checkout docs * remove idea directory from repo * add guest checkout docs * add promotion code example * remove idea dir * Update billing.md * Update billing.md * improve guest checkout docs * improve guest checkout docs * Update billing.md * foramtting Co-authored-by: Alexander Gaal Co-authored-by: Taylor Otwell Co-authored-by: Dries Vints --- billing.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/billing.md b/billing.md index 0b749a23067..481c645c524 100644 --- a/billing.md +++ b/billing.md @@ -54,6 +54,7 @@ - [Single Charge Checkouts](#single-charge-checkouts) - [Subscription Checkouts](#subscription-checkouts) - [Collecting Tax IDs](#collecting-tax-ids) + - [Guest Checkouts](#guest-checkouts) - [Invoices](#invoices) - [Retrieving Invoices](#retrieving-invoices) - [Upcoming Invoices](#upcoming-invoices) @@ -746,7 +747,7 @@ If you would like to add a subscription to a customer who already has a default #### Creating Subscriptions From The Stripe Dashboard -You may also create subscriptions from the Stripe dashboard itself. When doing so, Cashier will sync newly added subscriptions and assign them a name of `default`. To customize the subscription name that is assigned to dashboard created subscriptions, [extend the `WebhookController`](/docs/{{version}}/billing#defining-webhook-event-handlers) and overwrite the `newSubscriptionName` method. +You may also create subscriptions from the Stripe dashboard itself. When doing so, Cashier will sync newly added subscriptions and assign them a name of `default`. To customize the subscription name that is assigned to dashboard created subscriptions, [extend the `WebhookController`](#defining-webhook-event-handlers) and overwrite the `newSubscriptionName` method. In addition, you may only create one type of subscription via the Stripe dashboard. If your application offers multiple subscriptions that use different names, only one type of subscription may be added through the Stripe dashboard. @@ -1922,6 +1923,37 @@ When this method is invoked, a new checkbox will be available to the customer th > **Warning** > If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. + +### Guest Checkouts + +Using the `Checkout::guest` method, you may initiate checkout sessions for guests of your application that do not have an "account": + + use Illuminate\Http\Request; + use Laravel\Cashier\Checkout; + + Route::get('/product-checkout', function (Request $request) { + return Checkout::guest()->create('price_tshirt', [ + 'success_url' => route('your-success-route'), + 'cancel_url' => route('your-cancel-route'), + ]); + }); + +Similarly to when creating checkout sessions for existing users, you may utilize additional methods available on the `Laravel\Cashier\CheckoutBuilder` instance to customize the guest checkout session: + + use Illuminate\Http\Request; + use Laravel\Cashier\Checkout; + + Route::get('/product-checkout', function (Request $request) { + return Checkout::guest() + ->withPromotionCode('promo-code') + ->create('price_tshirt', [ + 'success_url' => route('your-success-route'), + 'cancel_url' => route('your-cancel-route'), + ]); + }); + +After a guest checkout has been completed, Stripe can dispatch a `checkout.session.completed` webhook event, so make sure to [configure your Stripe webhook](https://dashboard.stripe.com/webhooks) to actually send this event to your application. Once the webhook has been enabled within the Stripe dashboard, you may [handle the webhook with Cashier](#handling-stripe-webhooks). The object contained in the webhook payload will be a [`checkout` object](https://stripe.com/docs/api/checkout/sessions/object) that you may inspect in order to fulfill your customer's order. + ## Handling Failed Payments From 9230d95a181c5de6f49253b2df052fe2113a3ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E5=94=81?= <52o@qq52o.cn> Date: Tue, 4 Oct 2022 22:05:58 +0800 Subject: [PATCH 0445/2609] Use `,` instead of `;` (#8273) --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index 6a084e86106..b4fda1f1b57 100644 --- a/octane.md +++ b/octane.md @@ -138,7 +138,7 @@ Swoole supports a few additional configuration options that you may add to your 'log_file' => storage_path('logs/swoole_http.log'), 'package_max_length' => 10 * 1024 * 1024, ], -]; +], ``` From d435aacb26cb968384daa641e766e235184571c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A0=20L=C3=AA?= Date: Thu, 6 Oct 2022 20:59:56 +0700 Subject: [PATCH 0446/2609] Fix error wrong locate (#8279) Remove first punctuation in file path. --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index d89bf640b08..511832efbf1 100644 --- a/vite.md +++ b/vite.md @@ -476,7 +476,7 @@ It is common in JavaScript applications to [create aliases](#aliases) to regular */ public function boot() { - Vite::macro('image', fn ($asset) => $this->asset("/resources/images/{$asset}")); + Vite::macro('image', fn ($asset) => $this->asset("resources/images/{$asset}")); } Once a macro has been defined, it can be invoked within your templates. For example, we can use the `image` macro defined above to reference an asset located at `resources/images/logo.png`: From 7c98ae8d1912ea320d28d516cd3d66b4a1277ebe Mon Sep 17 00:00:00 2001 From: New Bing Date: Thu, 6 Oct 2022 22:06:36 +0800 Subject: [PATCH 0447/2609] [9.x] Add method at and twiceDailyAt description (#8278) * Add method at and twiceDailyAt description Add frequency method at and twiceDailyAt document. * Update scheduling.md Co-authored-by: Taylor Otwell --- scheduling.md | 1 + 1 file changed, 1 insertion(+) diff --git a/scheduling.md b/scheduling.md index 8ffb19fa780..22725a2bbc9 100644 --- a/scheduling.md +++ b/scheduling.md @@ -125,6 +125,7 @@ Method | Description `->daily();` | Run the task every day at midnight `->dailyAt('13:00');` | Run the task every day at 13:00 `->twiceDaily(1, 13);` | Run the task daily at 1:00 & 13:00 +`->twiceDailyAt(1, 13, 15);` | Run the task daily at 1:15 & 13:15 `->weekly();` | Run the task every Sunday at 00:00 `->weeklyOn(1, '8:00');` | Run the task every week on Monday at 8:00 `->monthly();` | Run the task on the first day of every month at 00:00 From f965e5fe866af9816497635a8866a1cad51eefd1 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 7 Oct 2022 18:00:23 +0330 Subject: [PATCH 0448/2609] [9.x] Document prompt parameter when redirecting for authorization (#8193) * document prompt parameter when redirecting for authorization * formatting Co-authored-by: Taylor Otwell --- passport.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/passport.md b/passport.md index b068f3b8d23..8b638dfa596 100644 --- a/passport.md +++ b/passport.md @@ -371,18 +371,25 @@ Once a client has been created, developers may use their client ID and secret to 'response_type' => 'code', 'scope' => '', 'state' => $state, + // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); }); +The `prompt` parameter may be used to specify the authentication behavior of the Passport application. + +If the value is `none`, Passport will always throw an authentication error if the user is not already authenticated with the Passport application. If the value is `consent`, Passport will always display the authorization approval screen, even if all scopes were previously granted to the consuming application. When the value is `login`, the Passport application will always prompt the user to re-login to the application, even if they already have an existing session. + +If no `prompt` value is provided, the user will be prompted for authorization only if they have not previously authorized access to the consuming application for the requested scopes. + > **Note** > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. #### Approving The Request -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. +When receiving authorization requests, Passport will automatically respond based on the value of `prompt` parameter (if present) and may 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 the `resources/views/vendor/passport` directory: @@ -390,7 +397,7 @@ If you would like to customize the authorization approval screen, you may publis php artisan vendor:publish --tag=passport-views ``` -Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately: +Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately, unless the consuming application has explicitly set the `prompt` parameter when redirecting for authorization: $state, 'code_challenge' => $codeChallenge, 'code_challenge_method' => 'S256', + // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); @@ -778,6 +786,7 @@ Once the grant has been enabled, developers may use their client ID to request a 'response_type' => 'token', 'scope' => '', 'state' => $state, + // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); From d988f5a9fae61b0754a34cb2293194b2840be322 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 7 Oct 2022 09:30:54 -0500 Subject: [PATCH 0449/2609] wip --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 8b638dfa596..ae07c14f345 100644 --- a/passport.md +++ b/passport.md @@ -379,7 +379,7 @@ Once a client has been created, developers may use their client ID and secret to The `prompt` parameter may be used to specify the authentication behavior of the Passport application. -If the value is `none`, Passport will always throw an authentication error if the user is not already authenticated with the Passport application. If the value is `consent`, Passport will always display the authorization approval screen, even if all scopes were previously granted to the consuming application. When the value is `login`, the Passport application will always prompt the user to re-login to the application, even if they already have an existing session. +If the `prompt` value is `none`, Passport will always throw an authentication error if the user is not already authenticated with the Passport application. If the value is `consent`, Passport will always display the authorization approval screen, even if all scopes were previously granted to the consuming application. When the value is `login`, the Passport application will always prompt the user to re-login to the application, even if they already have an existing session. If no `prompt` value is provided, the user will be prompted for authorization only if they have not previously authorized access to the consuming application for the requested scopes. From c010619f4d634e161ab9a4af9edcc7a0bd2b9134 Mon Sep 17 00:00:00 2001 From: Oliver Matla Date: Sun, 9 Oct 2022 03:40:23 +0200 Subject: [PATCH 0450/2609] make clear that only string-backed enums are allowed in route model binding (#8283) --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 486819e923b..18a07f38bf2 100644 --- a/routing.md +++ b/routing.md @@ -535,7 +535,7 @@ Typically, a 404 HTTP response will be generated if an implicitly bound model is ### Implicit Enum Binding -PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To compliment this feature, Laravel allows you to type-hint a [backed Enum](https://www.php.net/manual/en/language.enumerations.backed.php) on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: +PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To compliment this feature, Laravel allows you to type-hint a [string-backed Enum](https://www.php.net/manual/en/language.enumerations.backed.php) on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: ```php Date: Tue, 11 Oct 2022 08:49:50 -0500 Subject: [PATCH 0451/2609] New Mailable Syntax Docs (#8282) * initial pass at mailable doc updates * add headers and customization --- mail.md | 293 ++++++++++++++++++++++++++++++++--------------------- mocking.md | 23 +++-- 2 files changed, 190 insertions(+), 126 deletions(-) diff --git a/mail.md b/mail.md index 71c6c19f816..aa4ddf897c0 100644 --- a/mail.md +++ b/mail.md @@ -12,6 +12,7 @@ - [Attachments](#attachments) - [Inline Attachments](#inline-attachments) - [Attachable Objects](#attachable-objects) + - [Headers](#headers) - [Tags & Metadata](#tags-and-metadata) - [Customizing The Symfony Message](#customizing-the-symfony-message) - [Markdown Mailables](#markdown-mailables) @@ -170,29 +171,32 @@ php artisan make:mail OrderShipped ## Writing Mailables -Once you have generated a mailable class, open it up so we can explore its contents. First, note that all of a mailable class' configuration is done in the `build` method. Within this method, you may call various methods such as `from`, `subject`, `view`, and `attach` to configure the email's presentation and delivery. +Once you have generated a mailable class, open it up so we can explore its contents. Mailable class configuration is done in several methods, including the `envelope`, `content`, and `attachments` methods. -> **Note** -> You may type-hint dependencies on the mailable's `build` method. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies. +The `envelope` method returns an `Illuminate\Mail\Mailables\Envelope` object that defines the subject and, sometimes, the recipients of the message. The `content` method returns an `Illuminate\Mail\Mailables\Content` object that defines the [Blade template](/docs/{{version}}/blade) that will be used to generate the message content. ### Configuring The Sender - -#### Using The `from` Method + +#### Using The Envelope + +First, let's explore configuring the sender of the email. Or, in other words, who the email is going to be "from". There are two ways to configure the sender. First, you may specify the "from" address on your message's envelope: -First, let's explore configuring the sender of the email. Or, in other words, who the email is going to be "from". There are two ways to configure the sender. First, you may use the `from` method within your mailable class' `build` method: + use Illuminate\Mail\Mailables\Address; + use Illuminate\Mail\Mailables\Envelope; /** - * Build the message. + * Get the message envelope. * - * @return $this + * @return \Illuminate\Mail\Mailables\Envelope */ - public function build() + public function envelope() { - return $this->from('example@example.com', 'Example') - ->subject('Order Shipped') - ->view('emails.orders.shipped'); + return new Envelope( + from: new Address('jeffrey@example.com', 'Jeffrey Way'), + subject: 'Order Shipped', + ); } @@ -209,16 +213,18 @@ In addition, you may define a global "reply_to" address within your `config/mail ### Configuring The View -Within a mailable class' `build` method, you may use the `view` method to specify which template should be used when rendering the email's contents. Since each email typically uses a [Blade template](/docs/{{version}}/blade) to render its contents, you have the full power and convenience of the Blade templating engine when building your email's HTML: +Within a mailable class' `content` method, you may define the `view`, or which template should be used when rendering the email's contents. Since each email typically uses a [Blade template](/docs/{{version}}/blade) to render its contents, you have the full power and convenience of the Blade templating engine when building your email's HTML: /** - * Build the message. + * Get the message content definition. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->view('emails.orders.shipped'); + return new Content( + view: 'emails.orders.shipped', + ); } > **Note** @@ -227,19 +233,28 @@ Within a mailable class' `build` method, you may use the `view` method to specif #### Plain Text Emails -If you would like to define a plain-text version of your email, you may use the `text` method. Like the `view` method, the `text` method accepts a template name which will be used to render the contents of the email. You are free to define both an HTML and plain-text version of your message: +If you would like to define a plain-text version of your email, you may specify the plain-text template when creating the message's `Content` definition. Like the `view` parameter, the `text` parameter should be a template name which will be used to render the contents of the email. You are free to define both an HTML and plain-text version of your message: /** - * Build the message. + * Get the message content definition. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->view('emails.orders.shipped') - ->text('emails.orders.shipped_plain'); + return new Content( + view: 'emails.orders.shipped', + text: 'emails.orders.shipped-text' + ); } +For clarity, the `html` parameter may be used as an alias of the `view` parameter: + + return new Content( + html: 'emails.orders.shipped', + text: 'emails.orders.shipped-text' + ); + ### View Data @@ -255,6 +270,7 @@ Typically, you will want to pass some data to your view that you can utilize whe use App\Models\Order; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; + use Illuminate\Mail\Mailables\Content; use Illuminate\Queue\SerializesModels; class OrderShipped extends Mailable @@ -280,13 +296,15 @@ Typically, you will want to pass some data to your view that you can utilize whe } /** - * Build the message. + * Get the message content definition. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->view('emails.orders.shipped'); + return new Content( + view: 'emails.orders.shipped', + ); } } @@ -296,10 +314,10 @@ Once the data has been set to a public property, it will automatically be availa Price: {{ $order->price }}
- -#### Via The `with` Method: + +#### Via The `with` Parameter: -If you would like to customize the format of your email's data before it is sent to the template, you may manually pass your data to the view via the `with` method. Typically, you will still pass data via the mailable class' constructor; however, you should set this data to `protected` or `private` properties so the data is not automatically made available to the template. Then, when calling the `with` method, pass an array of data that you wish to make available to the template: +If you would like to customize the format of your email's data before it is sent to the template, you may manually pass your data to the view via the `Content` definition's `with` parameter. Typically, you will still pass data via the mailable class' constructor; however, you should set this data to `protected` or `private` properties so the data is not automatically made available to the template: view('emails.orders.shipped') - ->with([ - 'orderName' => $this->order->name, - 'orderPrice' => $this->order->price, - ]); + return new Content( + view: 'emails.orders.shipped', + with: [ + 'orderName' => $this->order->name, + 'orderPrice' => $this->order->price, + ], + ); } } @@ -356,95 +377,103 @@ Once the data has been passed to the `with` method, it will automatically be ava ### Attachments -To add attachments to an email, use the `attach` method within the mailable class' `build` method. The `attach` method accepts the full path to the file as its first argument: +To add attachments to an email, you will add attachments to the array returned by the message's `attachments` method. First, you may add an attachment by providing a file path to the `fromPath` method provided by the `Attachment` class: + + use Illuminate\Mail\Mailables\Attachment; /** - * Build the message. + * Get the attachments for the message. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attach('/path/to/file'); + return [ + Attachment::fromPath('/path/to/file'), + ]; } -When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: +When attaching files to a message, you may also specify the display name and / or MIME type for the attachment using the `as` and `withMime` methods: /** - * Build the message. + * Get the attachments for the message. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attach('/path/to/file', [ - 'as' => 'name.pdf', - 'mime' => 'application/pdf', - ]); + return [ + Attachment::fromPath('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; } #### Attaching Files From Disk -If you have stored a file on one of your [filesystem disks](/docs/{{version}}/filesystem), you may attach it to the email using the `attachFromStorage` method: +If you have stored a file on one of your [filesystem disks](/docs/{{version}}/filesystem), you may attach it to the email using the `fromStorage` attachment method: /** - * Build the message. + * Get the attachments for the message. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachFromStorage('/path/to/file'); + return [ + Attachment::fromStorage('/path/to/file'), + ]; } -If necessary, you may specify the file's attachment name and additional options using the second and third arguments to the `attachFromStorage` method: +Of course, you may also specify the attachment's name and MIME type: /** - * Build the message. + * Get the attachments for the message. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachFromStorage('/path/to/file', 'name.pdf', [ - 'mime' => 'application/pdf' - ]); + return [ + Attachment::fromStorage('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; } -The `attachFromStorageDisk` method may be used if you need to specify a storage disk other than your default disk: +The `fromStorageDisk` method may be used if you need to specify a storage disk other than your default disk: /** - * Build the message. + * Get the attachments for the message. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachFromStorageDisk('s3', '/path/to/file'); + return [ + Attachment::fromStorageDisk('s3', '/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; } #### Raw Data Attachments -The `attachData` method may be used to attach a raw string of bytes as an attachment. For example, you might use this method if you have generated a PDF in memory and want to attach it to the email without writing it to disk. The `attachData` method accepts the raw data bytes as its first argument, the name of the file as its second argument, and an array of options as its third argument: +The `fromData` attachment method may be used to attach a raw string of bytes as an attachment. For example, you might use this method if you have generated a PDF in memory and want to attach it to the email without writing it to disk. The `fromData` method accepts a closure which resolves the raw data bytes as well as the name that the attachment should be assigned: /** - * Build the message. + * Get the attachments for the message. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachData($this->pdf, 'name.pdf', [ - 'mime' => 'application/pdf', - ]); + return [ + Attachment::fromData(fn () => $this->pdf, 'Report.pdf') + ->withMime('application/pdf'), + ]; } @@ -504,17 +533,16 @@ To get started, implement the `Illuminate\Contracts\Mail\Attachable` interface o } } -Once you have defined your attachable object, you may simply pass an instance of that object to the `attach` method when building an email message: +Once you have defined your attachable object, you may return an instance of that object from the `attachments` method when building an email message: /** - * Build the message. + * Get the attachments for the message. * - * @return $this + * @return array */ - public function build() + public function attachments() { - return $this->view('photos.resized') - ->attach($this->photo); + return [$this->photo]; } Of course, attachment data may be stored on a remote file storage service such as Amazon S3. So, Laravel also allows you to generate attachment instances from data that is stored on one of your application's [filesystem disks](/docs/{{version}}/filesystem): @@ -535,21 +563,52 @@ Laravel also provides additional methods that you may use to customize your atta ->as('Photo Name') ->withMime('image/jpeg'); + +### Headers + +Sometimes you may need to attach additional headers to the outgoing message. For instance, you may need to set a custom `Message-Id` or other arbitrary text headers. + +To accomplish this, define a `headers` method on your mailable. The `headers` method should return an `Illuminate\Mail\Mailables\Headers` instance. This class accepts `messageId`, `references`, and `text` parameters. Of course, you may provide only the parameters you need for your particular message: + + use Illuminate\Mail\Mailables\Headers; + + /** + * Get the message headers. + * + * @return \Illuminate\Mail\Mailables\Headers + */ + public function headers() + { + return new Headers( + messageId: 'custom-message-id@example.com', + references: ['previous-message@example.com'], + text: [ + 'X-Custom-Header' => 'Custom Value', + ], + ); + } + ### Tags & Metadata -Some third-party email providers such as Mailgun and Postmark support message "tags" and "metadata", which may be used to group and track emails sent by your application. You may add tags and metadata to an email message via the `tag` and `metadata` methods: +Some third-party email providers such as Mailgun and Postmark support message "tags" and "metadata", which may be used to group and track emails sent by your application. You may add tags and metadata to an email message via your `Envelope` definition: + + use Illuminate\Mail\Mailables\Envelope; /** - * Build the message. + * Get the message envelope. * - * @return $this + * @return \Illuminate\Mail\Mailables\Envelope */ - public function build() + public function envelope() { - return $this->view('emails.orders.shipped') - ->tag('shipment') - ->metadata('order_id', $this->order->id); + return new Envelope( + subject: 'Order Shipped', + tags: ['shipment'], + metadata: [ + 'order_id' => $this->order->id, + ], + ); } If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) and [metadata](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). @@ -559,26 +618,26 @@ If your application is using Amazon SES to send emails, you should use the `meta ### Customizing The Symfony Message -The `withSymfonyMessage` method of the `Mailable` base class allows you to register a closure which will be invoked with the Symfony Message instance before sending the message. This gives you an opportunity to deeply customize the message before it is delivered: +Laravel's mail capabilities are powered by Symfony Mailer. Laravel allows you to register custom callbacks that will be invoked with the Symfony Message instance before sending the message. This gives you an opportunity to deeply customize the message before it is sent. To accomplish this, define a `using` parameter on your `Envelope` definition: + use Illuminate\Mail\Mailables\Envelope; use Symfony\Component\Mime\Email; /** - * Build the message. + * Get the message envelope. * - * @return $this + * @return \Illuminate\Mail\Mailables\Envelope */ - public function build() + public function envelope() { - $this->view('emails.orders.shipped'); - - $this->withSymfonyMessage(function (Email $message) { - $message->getHeaders()->addTextHeader( - 'Custom-Header', 'Header Value' - ); - }); - - return $this; + return new Envelope( + subject: 'Order Shipped', + using: [ + function (Email $message) { + // ... + }, + ] + ); } @@ -595,19 +654,23 @@ To generate a mailable with a corresponding Markdown template, you may use the ` php artisan make:mail OrderShipped --markdown=emails.orders.shipped ``` -Then, when configuring the mailable within its `build` method, call the `markdown` method instead of the `view` method. The `markdown` method accepts the name of the Markdown template and an optional array of data to make available to the template: +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; /** - * Build the message. + * Get the message content definition. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->from('example@example.com') - ->markdown('emails.orders.shipped', [ - 'url' => $this->orderUrl, - ]); + return new Content( + markdown: 'emails.orders.shipped', + with: [ + 'url' => $this->orderUrl, + ], + ); } diff --git a/mocking.md b/mocking.md index bffed33b3d3..70f1c3510b3 100644 --- a/mocking.md +++ b/mocking.md @@ -462,25 +462,26 @@ When calling the `Mail` facade's assertion methods, the mailable instance accept The mailable instance also includes several helpful methods for examining the attachments on a mailable: - Mail::assertSent(OrderShipped::class, function ($mail) { - return $mail->hasAttachment('/path/to/file'); - }); + use Illuminate\Mail\Mailables\Attachment; Mail::assertSent(OrderShipped::class, function ($mail) { - return $mail->hasAttachment('/path/to/file', [ - 'as' => 'name.pdf', - 'mime' => 'application/pdf', - ]); + return $mail->hasAttachment( + Attachment::fromPath('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf') + ); }); Mail::assertSent(OrderShipped::class, function ($mail) { - return $mail->hasAttachmentFromStorageDisk('s3', '/path/to/file'); + return $mail->hasAttachment( + Attachment::fromStorageDisk('s3', '/path/to/file') + ); }); Mail::assertSent(OrderShipped::class, function ($mail) use ($pdfData) { - return $mail->hasAttachedData($pdfData, 'name.pdf', [ - 'mime' => 'application/pdf' - ]); + return $mail->hasAttachment( + Attachment::fromData(fn () => $pdfData, 'name.pdf') + ); }); You may have noticed that there are two methods for asserting that mail was not sent: `assertNotSent` and `assertNotQueued`. Sometimes you may wish to assert that no mail was sent **or** queued. To accomplish this, you may use the `assertNothingOutgoing` and `assertNotOutgoing` methods: From 44b7825035033badafb2e58b6fe5ab5dfccb6a20 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 12 Oct 2022 00:50:13 +1100 Subject: [PATCH 0452/2609] [9.x] Update mail syntax (#8284) * update mail syntax * update notifications * fix attribute syntax * fix closing tag --- mail.md | 20 ++++++++++---------- notifications.md | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/mail.md b/mail.md index aa4ddf897c0..1936c28dba5 100644 --- a/mail.md +++ b/mail.md @@ -679,18 +679,18 @@ Then, when configuring the mailable `Content` definition within its `content` me Markdown mailables use a combination of Blade components and Markdown syntax which allow you to easily construct mail messages while leveraging Laravel's pre-built email UI components: ```blade -@component('mail::message') + # Order Shipped Your order has been shipped! -@component('mail::button', ['url' => $url]) + View Order -@endcomponent + Thanks,
{{ config('app.name') }} -@endcomponent +
``` > **Note** @@ -702,9 +702,9 @@ Thanks,
The button component renders a centered button link. The component accepts two arguments, a `url` and an optional `color`. Supported colors are `primary`, `success`, and `error`. You may add as many button components to a message as you wish: ```blade -@component('mail::button', ['url' => $url, 'color' => 'success']) + View Order -@endcomponent + ``` @@ -713,9 +713,9 @@ View Order The panel component renders the given block of text in a panel that has a slightly different background color than the rest of the message. This allows you to draw attention to a given block of text: ```blade -@component('mail::panel') + This is the panel content. -@endcomponent + ``` @@ -724,12 +724,12 @@ This is the panel content. The table component allows you to transform a Markdown table into an HTML table. The component accepts the Markdown table as its content. Table column alignment is supported using the default Markdown table alignment syntax: ```blade -@component('mail::table') + | Laravel | Table | Example | | ------------- |:-------------:| --------:| | Col 2 is | Centered | $10 | | Col 3 is | Right-Aligned | $20 | -@endcomponent + ``` diff --git a/notifications.md b/notifications.md index 6359ca37dc2..16f958dda51 100644 --- a/notifications.md +++ b/notifications.md @@ -725,18 +725,18 @@ Like all other mail notifications, notifications that use Markdown templates sho Markdown mail notifications use a combination of Blade components and Markdown syntax which allow you to easily construct notifications while leveraging Laravel's pre-crafted notification components: ```blade -@component('mail::message') + # Invoice Paid Your invoice has been paid! -@component('mail::button', ['url' => $url]) + View Invoice -@endcomponent + Thanks,
{{ config('app.name') }} -@endcomponent +
``` @@ -745,9 +745,9 @@ Thanks,
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: ```blade -@component('mail::button', ['url' => $url, 'color' => 'green']) + View Invoice -@endcomponent + ``` @@ -756,9 +756,9 @@ View Invoice The panel component renders the given block of text in a panel that has a slightly different background color than the rest of the notification. This allows you to draw attention to a given block of text: ```blade -@component('mail::panel') + This is the panel content. -@endcomponent + ``` @@ -767,12 +767,12 @@ This is the panel content. The table component allows you to transform a Markdown table into an HTML table. The component accepts the Markdown table as its content. Table column alignment is supported using the default Markdown table alignment syntax: ```blade -@component('mail::table') + | Laravel | Table | Example | | ------------- |:-------------:| --------:| | Col 2 is | Centered | $10 | | Col 3 is | Right-Aligned | $20 | -@endcomponent + ``` From 685470e226de889649d181a9f87967c47c24a9a1 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 11 Oct 2022 08:55:12 -0500 Subject: [PATCH 0453/2609] [9.x] loading soft deleted models on resource routes (#8277) * loading soft deleted models on resource routes * specify the new correct default routes * Update controllers.md * Update controllers.md Co-authored-by: Taylor Otwell --- controllers.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/controllers.md b/controllers.md index 7ddc2ec71d6..0c8bcc90518 100644 --- a/controllers.md +++ b/controllers.md @@ -183,6 +183,19 @@ Typically, a 404 HTTP response will be generated if an implicitly bound resource return Redirect::route('photos.index'); }); + +#### Soft Deleted Models + +Typically, implicit model binding will not retrieve models that have been [soft deleted](/docs/{{version}}/eloquent#soft-deleting), and will instead return a 404 HTTP response. However, you can instruct the framework to allow soft deleted models by invoking the `withTrashed` method when defining your resource route: + + use App\Http\Controllers\PhotoController; + + Route::resource('photos', PhotoController::class)->withTrashed(); + +Calling `withTrashed` with no arguments will allow soft deleted models for the `show`, `edit`, and `update` resource routes. You may specify a subset of these routes by passing an array to the `withTrashed` method: + + Route::resource('photos', PhotoController::class)->withTrashed(['show']); + #### Specifying The Resource Model From 3d5ae89755c640b645d5fc3938868e11a47c0608 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Oct 2022 09:09:11 -0500 Subject: [PATCH 0454/2609] document strict mode --- eloquent.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/eloquent.md b/eloquent.md index cbba013618d..314907d27e5 100644 --- a/eloquent.md +++ b/eloquent.md @@ -9,6 +9,7 @@ - [Timestamps](#timestamps) - [Database Connections](#database-connections) - [Default Attribute Values](#default-attribute-values) + - [Configuring Eloquent Strictness](#configuring-eloquent-strictness) - [Retrieving Models](#retrieving-models) - [Collections](#collections) - [Chunking Results](#chunking-results) @@ -358,6 +359,48 @@ By default, a newly instantiated model instance will not contain any attribute v ]; } + +### Configuring Eloquent Strictness + +Laravel offers several methods that allow you to configure Eloquent's behavior and "strictness" in a variety of situations. + +First, the `preventLazyLoading` method accepts an optional boolean argument that indicates if lazy loading should be prevented. For example, you may wish to only disable lazy loading in non-production environments so that your production environment will continue to function normally even if a lazy loaded relationship is accidentally present in production code. Typically, this method should be invoked in the `boot` method of your application's `AppServiceProvider`: + +```php +use Illuminate\Database\Eloquent\Model; + +/** + * Bootstrap any application services. + * + * @return void + */ +public function boot() +{ + Model::preventLazyLoading(! $this->app->isProduction()); +} +``` + +Also, you may instruct Laravel to throw an exception when attempting to fill an unfillable attribute by invoking the `preventSilentlyDiscardingAttributes` method. This can help prevent unexpected errors during local development when attempting to set an attribute that has not been added to the model's `fillable` array: + +```php +Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction()); +``` + +Finally, you may instruct Eloquent to throw an exception if you attempt to access an attribute on a model when that attribute was not actually retrieved from the database or when the attribute does not exist. For example, this may occur when you forget to add an attribute to the `select` clause of an Eloquent query: + +```php +Model::preventAccessingMissingAttributes(! $this->app->isProduction()); +``` + + +#### Enabling Eloquent "Strict Mode" + +For convenience, you may enable all three of the methods discussed above by simply invoking the `shouldBeStrict` method: + +```php +Model::shouldBeStrict(! $this->app->isProduction()); +``` + ## Retrieving Models From faaf2c4ce29c559dcf1124360a5a1643ae75020f Mon Sep 17 00:00:00 2001 From: Nick Turrietta Date: Mon, 17 Oct 2022 07:15:22 -0700 Subject: [PATCH 0455/2609] Bug fix.. missing ")" (#8292) --- http-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index 805e30d2b8e..a3aab56fe55 100644 --- a/http-client.md +++ b/http-client.md @@ -271,7 +271,7 @@ Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guz return $request; }) - ->get('http://example.com/'); + )->get('http://example.com/'); Likewise, you can inspect the incoming HTTP response by registering a middleware via the `withMiddleware` method in combination with Guzzle's `mapResponse` middleware factory: From 877a25e1ee65d6d09a0f75eec4894846547a21d5 Mon Sep 17 00:00:00 2001 From: Joep de Jong Date: Mon, 17 Oct 2022 16:21:04 +0200 Subject: [PATCH 0456/2609] Update SFTP documentation to use passphrase (#8291) Update SFTP documentation to use passphrase --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index b426656aae9..e6aee696de9 100644 --- a/filesystem.md +++ b/filesystem.md @@ -131,7 +131,7 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu // Settings for SSH key based authentication with encryption password... 'privateKey' => env('SFTP_PRIVATE_KEY'), - 'password' => env('SFTP_PASSWORD'), + 'passphrase' => env('SFTP_PASSPHRASE'), // Optional SFTP Settings... // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'), From e6d3839248b23f5c400c3896fdc1ef8a497feb60 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Oct 2022 08:44:15 -0500 Subject: [PATCH 0457/2609] wip --- mail.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 1936c28dba5..9054df96cdf 100644 --- a/mail.md +++ b/mail.md @@ -965,7 +965,7 @@ Once you have implemented the interface, Laravel will automatically use the pref ## Testing Mailables -Laravel provides several convenient methods for testing that your mailable contains the content that you expect. These methods are: `assertSeeInHtml`, `assertDontSeeInHtml`, `assertSeeInOrderInHtml`, `assertSeeInText`, `assertDontSeeInText`, `assertSeeInOrderInText`, `assertHasAttachment`, `assertHasAttachedData`, `assertHasAttachmentFromStorage`, and `assertHasAttachmentFromStorageDisk`. +Laravel provides a variety of methods for inspecting your mailable's structure. In addition, Laravel provides several convenient methods for testing that your mailable contains the content that you expect. These methods are: `assertSeeInHtml`, `assertDontSeeInHtml`, `assertSeeInOrderInHtml`, `assertSeeInText`, `assertDontSeeInText`, `assertSeeInOrderInText`, `assertHasAttachment`, `assertHasAttachedData`, `assertHasAttachmentFromStorage`, and `assertHasAttachmentFromStorageDisk`. As you might expect, the "HTML" assertions assert that the HTML version of your mailable contains a given string, while the "text" assertions assert that the plain-text version of your mailable contains a given string: @@ -978,6 +978,14 @@ As you might expect, the "HTML" assertions assert that the HTML version of your $mailable = new InvoicePaid($user); + $mailable->assertFrom('jeffrey@example.com'); + $mailable->assertTo('taylor@example.com'); + $mailable->assertHasCc('abigail@example.com'); + $mailable->assertHasBcc('victoria@example.com'); + $mailable->assertHasReplyTo('tyler@example.com'); + $mailable->hasTag('example-tag'); + $mailable->hasMetadata('key', 'value'); + $mailable->assertSeeInHtml($user->email); $mailable->assertSeeInHtml('Invoice Paid'); $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']); From d5bb9322f0338b8f7ae50dd59010dcf6c9a4ea63 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Oct 2022 08:44:59 -0500 Subject: [PATCH 0458/2609] wip --- mail.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mail.md b/mail.md index 9054df96cdf..544d95271d0 100644 --- a/mail.md +++ b/mail.md @@ -983,8 +983,8 @@ As you might expect, the "HTML" assertions assert that the HTML version of your $mailable->assertHasCc('abigail@example.com'); $mailable->assertHasBcc('victoria@example.com'); $mailable->assertHasReplyTo('tyler@example.com'); - $mailable->hasTag('example-tag'); - $mailable->hasMetadata('key', 'value'); + $mailable->assertHasTag('example-tag'); + $mailable->assertHasMetadata('key', 'value'); $mailable->assertSeeInHtml($user->email); $mailable->assertSeeInHtml('Invoice Paid'); From 055f6a74fb68e4882dbd0068fcaed96b30317c4e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Oct 2022 08:47:08 -0500 Subject: [PATCH 0459/2609] wip --- routing.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/routing.md b/routing.md index 18a07f38bf2..8379951863e 100644 --- a/routing.md +++ b/routing.md @@ -517,6 +517,12 @@ Or, you may instruct an entire group of route definitions to use scoped bindings }); }); +Similarly, you may explicitly instruct Laravel to not scope bindings by invoking the `withoutScopedBindings` method: + + Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { + return $post; + })->withoutScopedBindings(); + #### Customizing Missing Model Behavior From 067e5ad54467d88b8b884b996791b802b444619e Mon Sep 17 00:00:00 2001 From: Manash Sonowal Date: Wed, 19 Oct 2022 19:24:04 +0530 Subject: [PATCH 0460/2609] Update eloquent-factories.md (#8296) * Update eloquent-factories.md letting know the recycle method also accepts collections * Update eloquent-factories.md Co-authored-by: Taylor Otwell --- eloquent-factories.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/eloquent-factories.md b/eloquent-factories.md index acfc34499dd..d2d88fd67bd 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -492,7 +492,7 @@ If the relationship's columns depend on the factory that defines it you may assi ### Recycling An Existing Model For Relationships -If you have models that share a common relationship with another model, you may use the `recycle` method to ensure a single instance of the related model is recycled for all of the relationships. +If you have models that share a common relationship with another model, you may use the `recycle` method to ensure a single instance of the related model is recycled for all of the relationships created by the factory. For example, imagine you have `Airline`, `Flight`, and `Ticket` models, where the ticket belongs to an airline and a flight, and the flight also belongs to an airline. When creating tickets, you will probably want the same airline for both the ticket and the flight, so you may pass an airline instance to the `recycle` method: @@ -501,3 +501,9 @@ For example, imagine you have `Airline`, `Flight`, and `Ticket` models, where th ->create(); You may find the `recycle` method particularly useful if you have models belonging to a common user or team. + +The `recycle` method also accepts a collection of existing models. When a collection is provided to the `recycle` method, a random model from the collection will be chosen when the factory needs a model of that type: + + Ticket::factory() + ->recycle($airlines) + ->create(); From 906f046c3bf384d845725fb288183da8d5e0e878 Mon Sep 17 00:00:00 2001 From: Philip <17368112+vHeemstra@users.noreply.github.com> Date: Wed, 19 Oct 2022 23:03:14 +0200 Subject: [PATCH 0461/2609] Fixed typo in eloquent.md (#8297) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 314907d27e5..533cfef025e 100644 --- a/eloquent.md +++ b/eloquent.md @@ -313,7 +313,7 @@ If you need to customize the names of the columns used to store the timestamps, If you would like to perform model operations without the model having its `updated_at` timestamp modified, you may operate on the model within a closure given to the `withoutTimestamps` method: - Model::withoutTimestamps(fn () => $post->incrememt(['reads'])); + Model::withoutTimestamps(fn () => $post->increment(['reads'])); ### Database Connections From d52be4332352f8c476ca3a8d171a5e7ce80fdc47 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 20 Oct 2022 22:36:39 +0900 Subject: [PATCH 0462/2609] [9.x] Add assertHasSubject to mail.md (#8298) * [9.x] Add assertHasSubject to mail.md * Update mail.md Co-authored-by: Taylor Otwell --- mail.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mail.md b/mail.md index 544d95271d0..70eab44c84c 100644 --- a/mail.md +++ b/mail.md @@ -983,6 +983,7 @@ As you might expect, the "HTML" assertions assert that the HTML version of your $mailable->assertHasCc('abigail@example.com'); $mailable->assertHasBcc('victoria@example.com'); $mailable->assertHasReplyTo('tyler@example.com'); + $mailable->assertHasSubject('Invoice Paid'); $mailable->assertHasTag('example-tag'); $mailable->assertHasMetadata('key', 'value'); From 1f58e585825a1f12af35b7031e64fab894c64aa4 Mon Sep 17 00:00:00 2001 From: Andrew Dawes Date: Sat, 22 Oct 2022 18:36:19 +0200 Subject: [PATCH 0463/2609] Fixed grammar errors (#8302) --- collections.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collections.md b/collections.md index 427c5582acb..10758e24ffe 100644 --- a/collections.md +++ b/collections.md @@ -1484,7 +1484,7 @@ The `median` method returns the [median value](https://en.wikipedia.org/wiki/Med #### `merge()` {.collection-method} -The `merge` method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection: +The `merge` method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given item's value will overwrite the value in the original collection: $collection = collect(['product_id' => 1, 'price' => 100]); @@ -1494,7 +1494,7 @@ The `merge` method merges the given array or collection with the original collec // ['product_id' => 1, 'price' => 200, 'discount' => false] -If the given items's keys are numeric, the values will be appended to the end of the collection: +If the given item's keys are numeric, the values will be appended to the end of the collection: $collection = collect(['Desk', 'Chair']); From c9f8ee7d94dd27a054cb32ee18a8bfb5cad3674d Mon Sep 17 00:00:00 2001 From: emargareten <46111162+emargareten@users.noreply.github.com> Date: Mon, 24 Oct 2022 01:03:38 +0300 Subject: [PATCH 0464/2609] Update helpers.md (#8304) Use named parameters instead of assigning to unused variable. --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 3098be210b5..c41350b1f55 100644 --- a/helpers.md +++ b/helpers.md @@ -1103,7 +1103,7 @@ By default, any existing values are overwritten. If you wish to only set a value $data = ['products' => ['desk' => ['price' => 100]]]; - data_set($data, 'products.desk.price', 200, $overwrite = false); + data_set($data, 'products.desk.price', 200, overwrite: false); // ['products' => ['desk' => ['price' => 100]]] From 51ff6eea6d1f6943972e28c6567c6444772a9221 Mon Sep 17 00:00:00 2001 From: Bikram Tuladhar Date: Mon, 24 Oct 2022 19:08:07 +0545 Subject: [PATCH 0465/2609] [9.x]: Fixed blackfire.io documentation link (#8307) --- homestead.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homestead.md b/homestead.md index 5c21eebdf0c..ad9dcf22efd 100644 --- a/homestead.md +++ b/homestead.md @@ -763,7 +763,7 @@ features: client_token: "client_value" ``` -Blackfire server credentials and client credentials [require a Blackfire account](https://blackfire.io/signup). Blackfire offers various options to profile an application, including a CLI tool and browser extension. Please [review the Blackfire documentation for more details](https://blackfire.io/docs/cookbooks/index). +Blackfire server credentials and client credentials [require a Blackfire account](https://blackfire.io/signup). Blackfire offers various options to profile an application, including a CLI tool and browser extension. Please [review the Blackfire documentation for more details](https://blackfire.io/docs/php/integrations/laravel/index). ## Network Interfaces From b2e070eb21960754afaf00eebb76751830c36147 Mon Sep 17 00:00:00 2001 From: Anjorin Damilare Date: Mon, 24 Oct 2022 18:29:12 +0100 Subject: [PATCH 0466/2609] [9.x]: Add closure examples for `load` and `loadMissing` (#8303) --- eloquent-collections.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eloquent-collections.md b/eloquent-collections.md index 00adcc34ab6..76ef8113e64 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -149,6 +149,8 @@ The `load` method eager loads the given relationships for all models in the coll $users->load(['comments', 'posts']); $users->load('comments.author'); + + $users->load(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); #### `loadMissing($relations)` {.collection-method} @@ -158,6 +160,8 @@ The `loadMissing` method eager loads the given relationships for all models in t $users->loadMissing(['comments', 'posts']); $users->loadMissing('comments.author'); + + $users->loadMissing(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); #### `modelKeys()` {.collection-method} From 695d61d06b1eb650d181f0f62878e5b63af4f277 Mon Sep 17 00:00:00 2001 From: Anjorin Damilare Date: Mon, 24 Oct 2022 23:17:30 +0100 Subject: [PATCH 0467/2609] [9.x]: Add Second Argument to `whenQueryingForLongerThan` example (#8306) --- database.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database.md b/database.md index 85f1a76eed2..34b1579d1c2 100644 --- a/database.md +++ b/database.md @@ -292,6 +292,7 @@ A common performance bottleneck of modern web applications is the amount of time use Illuminate\Database\Connection; use Illuminate\Support\Facades\DB; use Illuminate\Support\ServiceProvider; + use Illuminate\Database\Events\QueryExecuted; class AppServiceProvider extends ServiceProvider { @@ -312,7 +313,7 @@ A common performance bottleneck of modern web applications is the amount of time */ public function boot() { - DB::whenQueryingForLongerThan(500, function (Connection $connection) { + DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) { // Notify development team... }); } From c46e484c91276b4d133257ae80cc38d677b54404 Mon Sep 17 00:00:00 2001 From: mohsen abrishami <31363832+MohsenAbrishami@users.noreply.github.com> Date: Tue, 25 Oct 2022 16:49:22 +0330 Subject: [PATCH 0468/2609] Add `expectsOutputToContain` and `doesntExpectOutputToContain` to output expectations (#8309) --- console-tests.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/console-tests.md b/console-tests.md index 871ab7fb204..2ad53ff7644 100644 --- a/console-tests.md +++ b/console-tests.md @@ -51,7 +51,7 @@ Laravel allows you to easily "mock" user input for your console commands using t $this->line('Your name is '.$name.' and you prefer '.$language.'.'); }); -You may test this command with the following test which utilizes the `expectsQuestion`, `expectsOutput`, `doesntExpectOutput`, and `assertExitCode` methods: +You may test this command with the following test which utilizes the `expectsQuestion`, `expectsOutput`, `doesntExpectOutput`, `expectsOutputToContain`, `doesntExpectOutputToContain`, and `assertExitCode` methods: /** * Test a console command. @@ -65,6 +65,8 @@ You may test this command with the following test which utilizes the `expectsQue ->expectsQuestion('Which language do you prefer?', 'PHP') ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') + ->expectsOutputToContain('Taylor Otwell') + ->doesntExpectOutputToContain('you prefer Ruby') ->assertExitCode(0); } From 6e967ecc8edb7a3fdfdb97a22c866e0bfbc42c2f Mon Sep 17 00:00:00 2001 From: mohsen abrishami <31363832+MohsenAbrishami@users.noreply.github.com> Date: Wed, 26 Oct 2022 17:16:41 +0330 Subject: [PATCH 0469/2609] Add `assertContent` (#8314) --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 2607a78fc9f..68c92c5e819 100644 --- a/http-tests.md +++ b/http-tests.md @@ -644,6 +644,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertJsonValidationErrors](#assert-json-validation-errors) [assertJsonValidationErrorFor](#assert-json-validation-error-for) [assertLocation](#assert-location) +[assertContent](#assert-content) [assertNoContent](#assert-no-content) [assertNotFound](#assert-not-found) [assertOk](#assert-ok) @@ -942,6 +943,13 @@ Assert the response has any JSON validation errors for the given key: Assert that the response has the given URI value in the `Location` header: $response->assertLocation($uri); + + +#### assertContent + +Assert that the given string matches the response content: + + $response->assertContent($value); #### assertNoContent From 6e9f2420bd4932a3174710732aa88e8e842b522e Mon Sep 17 00:00:00 2001 From: Nicolas Hedger <649677+nhedger@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:15:06 +0200 Subject: [PATCH 0470/2609] Fix Torchlight annotations (#8315) --- vite.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/vite.md b/vite.md index 511832efbf1..a82a7670392 100644 --- a/vite.md +++ b/vite.md @@ -143,7 +143,7 @@ export default defineConfig({ plugins: [ laravel({ // ... - valetTls: 'my-app.test', // [!tl add] + valetTls: 'my-app.test', // [tl! add] }), ], }); @@ -159,14 +159,14 @@ const host = 'my-app.test'; // [tl! add] export default defineConfig({ // ... - server: { // [!tl add] - host, // [!tl add] - hmr: { host }, // [!tl add] - https: { // [!tl add] - key: fs.readFileSync(`/path/to/${host}.key`), // [!tl add] - cert: fs.readFileSync(`/path/to/${host}.crt`), // [!tl add] - }, // [!tl add] - }, // [!tl add] + server: { // [tl! add] + host, // [tl! add] + hmr: { host }, // [tl! add] + https: { // [tl! add] + key: fs.readFileSync(`/path/to/${host}.key`), // [tl! add] + cert: fs.readFileSync(`/path/to/${host}.crt`), // [tl! add] + }, // [tl! add] + }, // [tl! add] }); ``` From 09d172a3d86a607e8a2a2635f174e351fc6e43e7 Mon Sep 17 00:00:00 2001 From: Justijn Depover Date: Thu, 27 Oct 2022 15:33:51 +0200 Subject: [PATCH 0471/2609] Fixed link href (#8318) --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index a2ea821fbea..9af2e0b4edb 100644 --- a/upgrade.md +++ b/upgrade.md @@ -34,7 +34,7 @@ ## Upgrading To 9.0 From 8.x - + #### Estimated Upgrade Time: 30 Minutes > **Note** From 61c7884f6cb29eec79b060dad48daaf67b7ffebf Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 27 Oct 2022 15:37:46 +0200 Subject: [PATCH 0472/2609] [9.x] Document overriding Passport routes (#8316) * Document overriding Passport routes In case someone wants to modify the throttle behavior, the middlewares, etc. * formatting Co-authored-by: Taylor Otwell --- passport.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/passport.md b/passport.md index ae07c14f345..b9548855cc8 100644 --- a/passport.md +++ b/passport.md @@ -10,6 +10,7 @@ - [Client Secret Hashing](#client-secret-hashing) - [Token Lifetimes](#token-lifetimes) - [Overriding Default Models](#overriding-default-models) + - [Overriding Routes](#overriding-routes) - [Issuing Access Tokens](#issuing-access-tokens) - [Managing Clients](#managing-clients) - [Requesting Tokens](#requesting-tokens) @@ -249,6 +250,33 @@ After defining your model, you may instruct Passport to use your custom model vi Passport::usePersonalAccessClientModel(PersonalAccessClient::class); } + +### Overriding Routes + +Sometimes you may wish to customize the routes defined by Passport. To achieve this, you first need to ignore the routes registered by Passport by adding `Passport::ignoreRoutes` to the `register` method of your application's `AppServiceProvider`: + + use Laravel\Passport\Passport; + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + Passport::ignoreRoutes(); + } + +Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/11.x/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: + + Route::group([ + 'as' => 'passport.', + 'prefix' => config('passport.path', 'oauth'), + 'namespace' => 'Laravel\Passport\Http\Controllers', + ], function () { + // Passport routes... + }); + ## Issuing Access Tokens From 7869b45d304217db9c16b82d197773194abcd394 Mon Sep 17 00:00:00 2001 From: Diogo Aguiar Date: Thu, 27 Oct 2022 18:40:08 +0100 Subject: [PATCH 0473/2609] Use consistent namespace for AncientScope (#8319) The AncientScope class is declared above with the namespace `namespace App\Models\Scopes;` but is then imported with `use App\Scopes\AncientScope;`. The namespace does not match the declaration nor the default structure when created with the `make:scope` command. --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 533cfef025e..4fec2b21034 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1238,7 +1238,7 @@ To assign a global scope to a model, you should override the model's `booted` me namespace App\Models; - use App\Scopes\AncientScope; + use App\Models\Scopes\AncientScope; use Illuminate\Database\Eloquent\Model; class User extends Model From 32a6ffa50264e021f23dae0b5c6f2d024d0705e4 Mon Sep 17 00:00:00 2001 From: Didac <63950600+didac-adria@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:33:52 +0200 Subject: [PATCH 0474/2609] [9.x] Document which dump file Laravel picks when running migrations (#8320) * Document which dump file Laravel picks when running migrations * Update migrations.md * Update migrations.md Co-authored-by: Taylor Otwell --- migrations.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index db5a6dc9c9c..9fb1e3cca12 100644 --- a/migrations.md +++ b/migrations.md @@ -58,7 +58,14 @@ php artisan schema:dump php artisan schema:dump --prune ``` -When you execute this command, Laravel will write a "schema" file to your application's `database/schema` directory. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will execute the schema file's SQL statements first. After executing the schema file's statements, Laravel will execute any remaining migrations that were not part of the schema dump. +When you execute this command, Laravel will write a "schema" file to your application's `database/schema` directory. The schema file's name will correspond to the database connection. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will execute first the SQL statements of the schema file of the database connection you are using. After executing the schema file's statements, Laravel will execute any remaining migrations that were not part of the schema dump. + +If your application's tests use a different database connection than the one you typically use during local development, you should ensure you have a dumped a schema file using that database connection so that your tests are able to build your database. You may wish to do this after dumping the database connection you typically use during local development: + +```shell +php artisan schema:dump +php artisan schema:dump --database=testing --prune +``` You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure. From 8be49a076bc19a744c1afb7ec901eab8eeca570b Mon Sep 17 00:00:00 2001 From: Seobs Date: Mon, 31 Oct 2022 22:48:50 +0900 Subject: [PATCH 0475/2609] [9.x] Add missing `quarterlyOn` (#8321) --- scheduling.md | 1 + 1 file changed, 1 insertion(+) diff --git a/scheduling.md b/scheduling.md index 22725a2bbc9..a36b71ade3b 100644 --- a/scheduling.md +++ b/scheduling.md @@ -133,6 +133,7 @@ Method | Description `->twiceMonthly(1, 16, '13:00');` | Run the task monthly on the 1st and 16th at 13:00 `->lastDayOfMonth('15:00');` | Run the task on the last day of the month at 15:00 `->quarterly();` | Run the task on the first day of every quarter at 00:00 +`->quarterlyOn(4, '14:00');` | Run the task every quarter on the 4th at 14: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 for the task From a904c5c667d94ec20f0dff8953b78cdb36b82860 Mon Sep 17 00:00:00 2001 From: JBtje Date: Mon, 31 Oct 2022 16:10:31 +0100 Subject: [PATCH 0476/2609] Typo in example (#8322) Typo in example: assertHasAttachementFromStorage and assertHasAttachementFromStorageDisk both have an 'e' too many. --- mail.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mail.md b/mail.md index 70eab44c84c..e8ac8b4f341 100644 --- a/mail.md +++ b/mail.md @@ -997,8 +997,8 @@ As you might expect, the "HTML" assertions assert that the HTML version of your $mailable->assertHasAttachment('/path/to/file'); $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file')); $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']); - $mailable->assertHasAttachementFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); - $mailable->assertHasAttachementFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); } From 9fedd1a451ab83cfc3a3d8f348da36ad6c08e2a6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Nov 2022 09:02:38 -0500 Subject: [PATCH 0477/2609] document isolatable commands --- artisan.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ migrations.md | 15 ++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index 5537983f829..8795ecb8cc9 100644 --- a/artisan.md +++ b/artisan.md @@ -6,6 +6,7 @@ - [Generating Commands](#generating-commands) - [Command Structure](#command-structure) - [Closure Commands](#closure-commands) + - [Isolatable Commands](#isolatable-commands) - [Defining Input Expectations](#defining-input-expectations) - [Arguments](#arguments) - [Options](#options) @@ -203,6 +204,55 @@ When defining a closure based command, you may use the `purpose` method to add a // ... })->purpose('Send a marketing email to a user'); + +### Isolatable Commands + +> **Warning** +> To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. + +Sometimes you may wish to ensure that only one instance of a command can run at a time. To accomplish this, you may implement the `Illuminate\Contracts\Console\Isolatable` interface on your command class: + + +#### Lock Expiration Time + +By default, isolation locks expire after the command is finished. Or, if the command is interrupted and unable to finish, the lock will expire after one hour. However, you may adjust the lock expiration time by defining a `isolationLockExpiresAt` method on your command: + +```php +/** + * Determine when an isolation lock expires for the command. + * + * @return \DateTimeInterface|\DateInterval + */ +public function isolationLockExpiresAt() +{ + return now()->addMinutes(5); +} +``` + ## Defining Input Expectations diff --git a/migrations.md b/migrations.md index 9fb1e3cca12..c34c44a8da1 100644 --- a/migrations.md +++ b/migrations.md @@ -5,7 +5,7 @@ - [Squashing Migrations](#squashing-migrations) - [Migration Structure](#migration-structure) - [Running Migrations](#running-migrations) - - [Rolling Back Migrations](#rolling-back-migrations) + - [Rolling Back Migrations](#rolling-back-migrations)] - [Tables](#tables) - [Creating Tables](#creating-tables) - [Updating Tables](#updating-tables) @@ -156,6 +156,19 @@ If you would like to see the SQL statements that will be executed by the migrati php artisan migrate --pretend ``` +#### Isolating Migration Execution + +If you are deploying your application across multiple servers and running migrations as part of your deployment process, you likely do not want two servers attempting to migrate the database at the same time. To avoid this, you may use the `isolated` option when invoking the `migrate` command. + +When the `isolated` option is provided, Laravel will acquire an atomic lock using your application's cache driver before attempting to run your migrations. All other attempts to run the `migrate` command while that lock is held will not execute; however, the command will still exit with a successful exit status code: + +```shell +php artisan migrate --isolated +``` + +> **Warning** +> To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. + #### Forcing Migrations To Run In Production From 326d175de9156b287eb28782901ed0792e259b99 Mon Sep 17 00:00:00 2001 From: Diogo Afonso Date: Tue, 1 Nov 2022 19:25:49 +0000 Subject: [PATCH 0478/2609] Fix markdown typo on migrations.md file (#8324) --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index c34c44a8da1..e3d6f8d658d 100644 --- a/migrations.md +++ b/migrations.md @@ -5,7 +5,7 @@ - [Squashing Migrations](#squashing-migrations) - [Migration Structure](#migration-structure) - [Running Migrations](#running-migrations) - - [Rolling Back Migrations](#rolling-back-migrations)] + - [Rolling Back Migrations](#rolling-back-migrations) - [Tables](#tables) - [Creating Tables](#creating-tables) - [Updating Tables](#updating-tables) From 2d7e76bc26fdff59efa52d9cd3ac8a22ae629a19 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 1 Nov 2022 20:26:40 +0100 Subject: [PATCH 0479/2609] Clarify main database requirement (#8323) * Clarify main database requirement Parallel testing will automatically create your testing databases but not your "main" database which you still need to run migrations, etc. * Update testing.md * Update testing.md Co-authored-by: Taylor Otwell --- testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing.md b/testing.md index 2545849f6b2..886cc133f5b 100644 --- a/testing.md +++ b/testing.md @@ -126,7 +126,7 @@ php artisan test --parallel --processes=4 #### Parallel Testing & Databases -Laravel automatically handles creating and migrating a test database for each parallel process that is running your tests. The test databases will be suffixed with a process token which is unique per process. For example, if you have two parallel test processes, Laravel will create and use `your_db_test_1` and `your_db_test_2` test databases. +As long as you have configured a primary database connection, Laravel automatically handles creating and migrating a test database for each parallel process that is running your tests. The test databases will be suffixed with a process token which is unique per process. For example, if you have two parallel test processes, Laravel will create and use `your_db_test_1` and `your_db_test_2` test databases. By default, test databases persist between calls to the `test` Artisan command so that they can be used again by subsequent `test` invocations. However, you may re-create them using the `--recreate-databases` option: From 9ceeb48bfd9a6005756f9d8c4d3be5af8e197f06 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 2 Nov 2022 14:35:49 +0100 Subject: [PATCH 0480/2609] Document new incomplete method (#8326) --- billing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/billing.md b/billing.md index 481c645c524..d79d7df7cdf 100644 --- a/billing.md +++ b/billing.md @@ -867,7 +867,7 @@ When a subscription has an incomplete payment, you should direct the user to Cas ``` -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`: +If you would like the subscription to still be considered active when it's in a `past_due` or `incomplete` state, you may use the `keepPastDueSubscriptionsActive` and `keepIncompleteSubscriptionsActive` methods provided by Cashier. Typically, these methods should be called in the `register` method of your `App\Providers\AppServiceProvider`: use Laravel\Cashier\Cashier; @@ -879,6 +879,7 @@ If you would like the subscription to still be considered active when it's in a public function register() { Cashier::keepPastDueSubscriptionsActive(); + Cashier::keepIncompleteSubscriptionsActive(); } > **Warning** From 4a7ff58a8ec2632c3f97b55e790af01084537c6c Mon Sep 17 00:00:00 2001 From: Diogo Aguiar Date: Wed, 2 Nov 2022 13:37:09 +0000 Subject: [PATCH 0481/2609] Add missing middleware to $middlewarePriority list (#8325) In the documentation, the `\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class` middleware class is not listed on the array `$middlewarePriority` but is present on the implementation of `Illuminate\Foundation\Http\Kernel`. --- middleware.md | 1 + 1 file changed, 1 insertion(+) diff --git a/middleware.md b/middleware.md index aa66de2de4f..cf3c112b5a5 100644 --- a/middleware.md +++ b/middleware.md @@ -232,6 +232,7 @@ Rarely, you may need your middleware to execute in a specific order but not have * @var string[] */ protected $middlewarePriority = [ + \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, From dc1688df5d5c54e8b4e1b6f1265a7e5155ab965c Mon Sep 17 00:00:00 2001 From: Micael Dias Date: Sun, 6 Nov 2022 17:42:24 +0100 Subject: [PATCH 0482/2609] Document ULID primary key (#8328) * Document ULID primary key * Update eloquent.md Co-authored-by: Taylor Otwell --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 4fec2b21034..d2fa4d2f1d1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -246,7 +246,7 @@ You can override the UUID generation process for a given model by defining a `ne return ['id', 'discount_code']; } -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: +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; From 8d756e835892bfa834a88a8e36e2a60994450fb5 Mon Sep 17 00:00:00 2001 From: Bennett Date: Mon, 7 Nov 2022 15:42:13 +0100 Subject: [PATCH 0483/2609] Add information about maximum number of Octane concurrently tasks (#8329) * Add information about maximum number of octane tasks * Improve notice about maximum tasks * Update octane.md Co-authored-by: Taylor Otwell --- octane.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/octane.md b/octane.md index b4fda1f1b57..c4086d8f539 100644 --- a/octane.md +++ b/octane.md @@ -479,6 +479,8 @@ Concurrent tasks processed by Octane utilize Swoole's "task workers", and execut php artisan octane:start --workers=4 --task-workers=6 ``` +When invoking the `concurrently` method, you should not provide more than 1024 tasks due to limitations imposed by Swoole's task system. + ## Ticks & Intervals From 06be80cec9d2fa8e15decdaecb23d11594d15fd1 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 8 Nov 2022 14:57:32 +0100 Subject: [PATCH 0484/2609] Updated pint version command typo (#8331) to show the version of pint it must be with the "v" in uppercase, used like this in the example will start Pint with applying the changes. --- pint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint.md b/pint.md index 00fdb18fb6f..77d1051f0ff 100644 --- a/pint.md +++ b/pint.md @@ -36,7 +36,7 @@ You can instruct Pint to fix code style issues by invoking the `pint` binary tha Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: ```shell -./vendor/bin/pint -v +./vendor/bin/pint -V ``` If you would like Pint to simply inspect your code for style errors without actually changing the files, you may use the `--test` option: From 0aaf29bc79957a1f181bc75160e8fb0bc3c5c951 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Nov 2022 13:31:44 -0600 Subject: [PATCH 0485/2609] document blade fragmetns --- blade.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/blade.md b/blade.md index 419403f70e7..2deec7d751f 100644 --- a/blade.md +++ b/blade.md @@ -39,6 +39,7 @@ - [Stacks](#stacks) - [Service Injection](#service-injection) - [Rendering Inline Blade Templates](#rendering-inline-blade-templates) +- [Rendering Blade Fragments](#rendering-blade-fragments) - [Extending Blade](#extending-blade) - [Custom Echo Handlers](#custom-echo-handlers) - [Custom If Statements](#custom-if-statements) @@ -1689,6 +1690,27 @@ return Blade::render( ); ``` + +## Rendering Blade Fragments + +When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and [HTMX](https://htmx.org/), you may occasionally need to return only a portion of a Blade template within your HTTP response. Blade "fragments" allow you to do just that. To get started, place a portion of your Blade template within `@fragment` and `@endfragment` directives: + +```blade +@fragment('user-list') +
  • + @foreach ($users as $user) +
      {{ $user->name }}
    + @endforeach +
  • +@endfragment +``` + +Then, when rendering the view that utilizes this template, you may invoke the `fragment` method to specify that only the specified fragment should be returned: + +```php +return view('dashboard', ['users' => $users])->fragment('user-list'); +``` + ## Extending Blade From e57ba840823173a1fd9e5d66210630912b4d4b5e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Nov 2022 13:32:30 -0600 Subject: [PATCH 0486/2609] wip --- blade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade.md b/blade.md index 2deec7d751f..3c24dde10dc 100644 --- a/blade.md +++ b/blade.md @@ -1693,7 +1693,7 @@ return Blade::render( ## Rendering Blade Fragments -When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and [HTMX](https://htmx.org/), you may occasionally need to return only a portion of a Blade template within your HTTP response. Blade "fragments" allow you to do just that. To get started, place a portion of your Blade template within `@fragment` and `@endfragment` directives: +When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and [HTMX](https://htmx.org/), you may occasionally need to only return a portion of a Blade template within your HTTP response. Blade "fragments" allow you to do just that. To get started, place a portion of your Blade template within `@fragment` and `@endfragment` directives: ```blade @fragment('user-list') @@ -1705,7 +1705,7 @@ When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and @endfragment ``` -Then, when rendering the view that utilizes this template, you may invoke the `fragment` method to specify that only the specified fragment should be returned: +Then, when rendering the view that utilizes this template, you may invoke the `fragment` method to specify that only the specified fragment should be included in the outgoing HTTP response: ```php return view('dashboard', ['users' => $users])->fragment('user-list'); From 723dd55ec35e4cdca6ca7dbe3698a9d31844e51a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Nov 2022 14:00:01 -0600 Subject: [PATCH 0487/2609] wip --- blade.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blade.md b/blade.md index 3c24dde10dc..30a1e7102fd 100644 --- a/blade.md +++ b/blade.md @@ -1697,11 +1697,11 @@ When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and ```blade @fragment('user-list') -
  • +
      @foreach ($users as $user) -
        {{ $user->name }}
      +
    • {{ $user->name }}
    • @endforeach - +
    @endfragment ``` From 25f35688559d8a30eaad7e03f8a8123226ffa0c5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 10 Nov 2022 09:04:59 -0600 Subject: [PATCH 0488/2609] wip --- pint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint.md b/pint.md index 77d1051f0ff..754432f94b5 100644 --- a/pint.md +++ b/pint.md @@ -33,7 +33,7 @@ You can instruct Pint to fix code style issues by invoking the `pint` binary tha ./vendor/bin/pint ``` -Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: +Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-V` option when invoking Pint: ```shell ./vendor/bin/pint -V From d24158c02a7d007ec5c1bfb70a1d4ac59b21e32a Mon Sep 17 00:00:00 2001 From: Arko Elsenaar Date: Thu, 10 Nov 2022 17:17:33 +0100 Subject: [PATCH 0489/2609] Changed HTMX to the correct lowercase version (#8334) --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index 30a1e7102fd..713ea5acde6 100644 --- a/blade.md +++ b/blade.md @@ -1693,7 +1693,7 @@ return Blade::render( ## Rendering Blade Fragments -When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and [HTMX](https://htmx.org/), you may occasionally need to only return a portion of a Blade template within your HTTP response. Blade "fragments" allow you to do just that. To get started, place a portion of your Blade template within `@fragment` and `@endfragment` directives: +When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and [htmx](https://htmx.org/), you may occasionally need to only return a portion of a Blade template within your HTTP response. Blade "fragments" allow you to do just that. To get started, place a portion of your Blade template within `@fragment` and `@endfragment` directives: ```blade @fragment('user-list') From a90d78afca29119dde2acdff1e0bf3c27c2835f4 Mon Sep 17 00:00:00 2001 From: shahroq Date: Mon, 14 Nov 2022 18:16:46 +0330 Subject: [PATCH 0490/2609] rename ConcreteCMS (#8340) --- valet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valet.md b/valet.md index 0250baf146e..5e6ab9b71e0 100644 --- a/valet.md +++ b/valet.md @@ -42,7 +42,7 @@ Out of the box, Valet support includes, but is not limited to: - [Laravel](https://laravel.com) - [Bedrock](https://roots.io/bedrock/) - [CakePHP 3](https://cakephp.org) -- [Concrete5](https://www.concrete5.org/) +- [ConcreteCMS](https://www.concretecms.com/) - [Contao](https://contao.org/en/) - [Craft](https://craftcms.com) - [Drupal](https://www.drupal.org/) From 44da0061b711c9057f31d27374e544ae174cdd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malinowski?= <47485431+malinowskip@users.noreply.github.com> Date: Mon, 14 Nov 2022 19:17:48 +0100 Subject: [PATCH 0491/2609] document scoped and read-only filesystem dependencies (#8341) * document scoped and read-only filesystem dependencies Scoped and read-only filesystems require additional Flysystem packages. * Update filesystem.md Co-authored-by: Taylor Otwell --- filesystem.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/filesystem.md b/filesystem.md index e6aee696de9..c82b29ffe40 100644 --- a/filesystem.md +++ b/filesystem.md @@ -146,7 +146,13 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu ### Scoped & Read-Only Filesystems -You may create a path scoped instance of any existing filesystem disk by defining a disk that utilizes the `scoped` driver. Scoped disks allow you to define a filesystem where all paths are automatically prefixed with a given path prefix. 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: +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 +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 's3-videos' => [ @@ -156,7 +162,13 @@ You may create a path scoped instance of any existing filesystem disk by definin ], ``` -If you would like to specify that any filesystem disk should be "read-only", you may include the `read-only` configuration option in the disk's configuration array: +"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: + +```shell +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 's3-videos' => [ From fc3c949119556e3510899d5c20c7728e9e2d3a33 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 16 Nov 2022 01:14:30 +1100 Subject: [PATCH 0492/2609] Adds "lowercase" validation rule documentation (#8332) --- validation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation.md b/validation.md index 00129ea224f..ec19f18787b 100644 --- a/validation.md +++ b/validation.md @@ -846,6 +846,7 @@ Below is a list of all available validation rules and their function: [JSON](#rule-json) [Less Than](#rule-lt) [Less Than Or Equal](#rule-lte) +[Lowercase](#rule-lowercase) [MAC Address](#rule-mac) [Max](#rule-max) [Max Digits](#rule-max-digits) @@ -1315,6 +1316,11 @@ The field under validation must be less than the given _field_. The two fields m The field under validation must be less than or equal to the given _field_. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the [`size`](#rule-size) rule. + +#### lowercase:_field_ + +The field under validation must be lowercase. + #### mac_address From 7feb2445ab6f135396eccd7e82ac949bff4613a2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Nov 2022 08:15:42 -0600 Subject: [PATCH 0493/2609] wip --- validation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation.md b/validation.md index ec19f18787b..3cd6a62890f 100644 --- a/validation.md +++ b/validation.md @@ -881,6 +881,7 @@ Below is a list of all available validation rules and their function: [String](#rule-string) [Timezone](#rule-timezone) [Unique (Database)](#rule-unique) +[Uppercase](#rule-uppercase) [URL](#rule-url) [UUID](#rule-uuid) @@ -1624,6 +1625,11 @@ You may specify additional query conditions by customizing the query using the ` 'email' => Rule::unique('users')->where(fn ($query) => $query->where('account_id', 1)) + +#### uppercase:_field_ + +The field under validation must be uppercase. + #### url From a10e325d7083038e60712a456f173b6c0d152a0c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Nov 2022 08:17:14 -0600 Subject: [PATCH 0494/2609] dark mode --- starter-kits.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/starter-kits.md b/starter-kits.md index 642811913c8..354b2b69f0b 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -56,6 +56,15 @@ 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. + +#### Dark Mode + +If you would like Breeze to include "dark mode" support when scaffolding your application's frontend, simply provide the `--dark` directive when executing the `breeze:install` command: + +```shell +php artisan breeze:install --dark +``` + > **Note** > To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). From d890c2876629804b6288f347cbd2d2d4b050967c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Nov 2022 08:18:19 -0600 Subject: [PATCH 0495/2609] redirect to route --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 68c92c5e819..45298bc8e6e 100644 --- a/http-tests.md +++ b/http-tests.md @@ -651,6 +651,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertPlainCookie](#assert-plain-cookie) [assertRedirect](#assert-redirect) [assertRedirectContains](#assert-redirect-contains) +[assertRedirectToRoute](#assert-redirect-to-route) [assertRedirectToSignedRoute](#assert-redirect-to-signed-route) [assertSee](#assert-see) [assertSeeInOrder](#assert-see-in-order) @@ -993,6 +994,13 @@ Assert whether the response is redirecting to a URI that contains the given stri $response->assertRedirectContains($string); + +#### assertRedirectToRoute + +Assert that the response is a redirect to the given [named route](/docs/{{version}}/routing#named-routes): + + $response->assertRedirectToRoute($name = null, $parameters = []); + #### assertRedirectToSignedRoute From ede93ca8b8eb02e4f0849cf65d5571df8bed29b2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Nov 2022 08:19:03 -0600 Subject: [PATCH 0496/2609] wip --- starter-kits.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/starter-kits.md b/starter-kits.md index 354b2b69f0b..65013958619 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -18,7 +18,9 @@ While you are welcome to use these starter kits, they are not required. You are ## Laravel Breeze -[Laravel Breeze](https://github.com/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 default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Or, Breeze can scaffold your application using Vue or React and [Inertia](https://inertiajs.com). +[Laravel Breeze](https://github.com/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. In addition, Breeze includes a simple "profile" page where the user may update their name, email address, and password. + +Laravel Breeze's default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Or, Breeze can scaffold your application using Vue or React and [Inertia](https://inertiajs.com). Breeze provides a wonderful starting point for beginning a fresh Laravel application and is also a great choice for projects that plan to take their Blade templates to the next level with [Laravel Livewire](https://laravel-livewire.com). From b73fb60a3cb8193a9f337217a1ea55ec9be7ade0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Nov 2022 10:14:21 -0600 Subject: [PATCH 0497/2609] add lottery docs --- helpers.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/helpers.md b/helpers.md index c41350b1f55..7a26c660bcd 100644 --- a/helpers.md +++ b/helpers.md @@ -4,6 +4,7 @@ - [Available Methods](#available-methods) - [Other Utilities](#other-utilities) - [Benchmarking](#benchmarking) + - [Lottery](#lottery) ## Introduction @@ -3976,3 +3977,43 @@ By default, the given callbacks will be executed once (one iteration), and their To invoke a callback more than once, you may specify the number of iterations that the callback should be invoked as the second argument to the method. When executing a callback more than once, the `Benchmark` class will return the average amount of milliseconds it took to execute the callback across all iterations: Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms + + +### Lottery + +Laravel's lottery class may be used to execute callbacks based on a set of given odds. This can be particularly useful when you only want to execute code for a percentage of your incoming requests: + + use Illuminate\Support\Lottery; + + Lottery::odds(1, 20) + ->winner(fn () => $user->won()) + ->loser(fn () => $user->lost()) + ->choose(); + +You may combine Laravel's lottery class with other Laravel features. For example, you may wish to only report a small percentage of slow queries to your exception handler. And, since the lottery class is callable, we may pass an instance of the class into any method that accepts callables: + + use Carbon\CarbonInterval; + use Illuminate\Support\Facades\DB; + use Illuminate\Support\Lottery; + + DB::whenQueryingForLongerThan( + CarbonInterval::seconds(2), + Lottery::odds(1, 100)->winner(fn () => report('Querying > 2 seconds.')), + ); + + +#### Testing Lotteries + +Laravel provides some simple methods to allow you to easily test your application's lottery invocations: + + // Lottery will always win... + Lottery::alwaysWin(); + + // Lottery will always lose... + Lottery::alwaysLose(); + + // Lottery will win then lose, and finally return to normal behavior... + Lottery::fix([true, false]); + + // Lottery will return to normal behavior... + Lottery::determineResultsNormally(); From b9181da75bd7d7a765fc99ea8f9c50cb45c62d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Tue, 15 Nov 2022 17:17:07 +0100 Subject: [PATCH 0498/2609] Added quotation marks around docker run command (#8343) --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index cba44e2c3d1..4454d468f11 100644 --- a/sail.md +++ b/sail.md @@ -164,7 +164,7 @@ You may install the application's dependencies by navigating to the application' ```shell docker run --rm \ -u "$(id -u):$(id -g)" \ - -v $(pwd):/var/www/html \ + -v "$(pwd):/var/www/html" \ -w /var/www/html \ laravelsail/php81-composer:latest \ composer install --ignore-platform-reqs From 81ffdc2f4964958d90774a4bb8d16dcc415ac372 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 16 Nov 2022 19:51:50 +0000 Subject: [PATCH 0499/2609] [9.x] Adds information about how to get the job ID from the queue worker (#8345) * Adds information about how to get the job ID from the queue worker * Update queues.md Co-authored-by: Taylor Otwell --- queues.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/queues.md b/queues.md index 2bb58aa0ad6..00ce8665b5c 100644 --- a/queues.md +++ b/queues.md @@ -1502,6 +1502,12 @@ php artisan queue:work > **Note** > To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. +You may include the `-v` flag when invoking the `queue:work` command if you would like the processed job IDs to be included in the command's output: + +```shell +php artisan queue:work -v +``` + 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 significantly less efficient than the `queue:work` command: From 97f59e0b10f16f828f51edbad492a5b1e1ddaf54 Mon Sep 17 00:00:00 2001 From: shatterproof <4100078+shatterproof@users.noreply.github.com> Date: Thu, 17 Nov 2022 06:39:50 -0800 Subject: [PATCH 0500/2609] Fix Pint Verbose Flag Typo (#8347) To show the thorough list of updated files the flag here needs to be lowercase `-v` not `-V` which instead provides the version number of Pint. --- pint.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pint.md b/pint.md index 754432f94b5..00fdb18fb6f 100644 --- a/pint.md +++ b/pint.md @@ -33,10 +33,10 @@ You can instruct Pint to fix code style issues by invoking the `pint` binary tha ./vendor/bin/pint ``` -Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-V` option when invoking Pint: +Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: ```shell -./vendor/bin/pint -V +./vendor/bin/pint -v ``` If you would like Pint to simply inspect your code for style errors without actually changing the files, you may use the `--test` option: From 85c29bcf68eff367685f968a3603756d488759b8 Mon Sep 17 00:00:00 2001 From: Chris Lloyd Date: Thu, 17 Nov 2022 20:59:31 +0000 Subject: [PATCH 0501/2609] [9.x] Add mention of the app() helper in the Container docs. (#8348) * Add mention of the app() helper in the Container docs. * Update container.md Co-authored-by: Taylor Otwell --- container.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/container.md b/container.md index be397e86d47..aaac9d05e96 100644 --- a/container.md +++ b/container.md @@ -366,13 +366,15 @@ If some of your class' dependencies are not resolvable via the container, you ma $transistor = $this->app->makeWith(Transistor::class, ['id' => 1]); -If you are outside of a service provider in a location of your code that does not have access to the `$app` variable, you may use the `App` [facade](/docs/{{version}}/facades) to resolve a class instance from the container: +If you are outside of a service provider in a location of your code that does not have access to the `$app` variable, you may use the `App` [facade](/docs/{{version}}/facades) or the `app` [helper](/docs/{{version}}/helpers#method-app) to resolve a class instance from the container: use App\Services\Transistor; use Illuminate\Support\Facades\App; $transistor = App::make(Transistor::class); + $transistor = app(Transistor::class); + If you would like to have the Laravel container instance itself injected into a class that is being resolved by the container, you may type-hint the `Illuminate\Container\Container` class on your class' constructor: use Illuminate\Container\Container; From 29d3ad59653ab7dc5725b4f493464ee6a515503c Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 21 Nov 2022 17:05:48 +0100 Subject: [PATCH 0502/2609] Clarify behavior of request merge (#8353) * Clarify behavior of request merge Right now it's not clear that existing keys will be overwritten. * Update requests.md Co-authored-by: Taylor Otwell --- requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests.md b/requests.md index 9da8979d9f3..62c5380f64e 100644 --- a/requests.md +++ b/requests.md @@ -417,7 +417,7 @@ To determine if a given key is absent from the request, you may use the `missing ### Merging Additional Input -Sometimes you may need to manually merge additional input into the request's existing input data. To accomplish this, you may use the `merge` method: +Sometimes you may need to manually merge additional input into the request's existing input data. To accomplish this, you may use the `merge` method. If a given input key already exists on the request, it will be overwritten by the data provided to the `merge` method: $request->merge(['votes' => 0]); From ea3f6fca64fdac48d1e33677f7094f3b172b367b Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 22 Nov 2022 07:47:45 +1100 Subject: [PATCH 0503/2609] Documents the need to set a name on scheduled closures running on one server (#8354) --- scheduling.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scheduling.md b/scheduling.md index a36b71ade3b..842a2d128d6 100644 --- a/scheduling.md +++ b/scheduling.md @@ -300,6 +300,16 @@ $schedule->job(new CheckUptime('https://vapor.laravel.com/')) ->onOneServer(); ``` +If you are scheduling a closure that should only run on one server, you will also need to provide a name before you call the `onOneServer` method: + +```php +$schedule->call(fn () => User::resetApiRequestCount()) + ->name('reset-api-request-count') + ->daily() + ->onOneServer(); +``` + + ### Background Tasks From 3d5434c09b3e37263c8a62183c5fcfa11d2c28fb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 21 Nov 2022 14:48:46 -0600 Subject: [PATCH 0504/2609] wip --- scheduling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index 842a2d128d6..3ff6f1bbdeb 100644 --- a/scheduling.md +++ b/scheduling.md @@ -300,7 +300,7 @@ $schedule->job(new CheckUptime('https://vapor.laravel.com/')) ->onOneServer(); ``` -If you are scheduling a closure that should only run on one server, you will also need to provide a name before you call the `onOneServer` method: +Similarly, scheduled closures must be assigned a name if they are intended to be run on one server: ```php $schedule->call(fn () => User::resetApiRequestCount()) From 780dc09ca3fb1d03709a3ffaa8244e3c2007ea62 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 22 Nov 2022 12:07:14 -0600 Subject: [PATCH 0505/2609] wip --- requests.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/requests.md b/requests.md index 62c5380f64e..191d41c1dd9 100644 --- a/requests.md +++ b/requests.md @@ -408,12 +408,18 @@ A second closure may be passed to the `whenFilled` method that will be executed // The "name" value is not filled... }); -To determine if a given key is absent from the request, you may use the `missing` method: +To determine if a given key is absent from the request, you may use the `missing` and `whenMissing` methods: if ($request->missing('name')) { // } + $request->whenMissing('name', function ($input) { + // The "name" value is missing... + }, function () { + // The "name" value is present... + }); + ### Merging Additional Input From 3a8912f94b0fc634aecf8d30711a752148d58a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krishan=20K=C3=B6nig?= Date: Wed, 23 Nov 2022 17:48:19 +0100 Subject: [PATCH 0506/2609] [9.x] Add note to 'Chain Failures' and 'Queueing Closures' section to not use '$this' in callbacks (#8357) * Update queues.md * formatting Co-authored-by: Taylor Otwell --- queues.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/queues.md b/queues.md index 00ce8665b5c..a527e0debea 100644 --- a/queues.md +++ b/queues.md @@ -858,6 +858,8 @@ When chaining jobs, you may use the `catch` method to specify a closure that sho ])->catch(function (Throwable $e) { // A job within the chain has failed... })->dispatch(); + +> {note} Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. ### Customizing The Queue & Connection @@ -1486,6 +1488,8 @@ Using the `catch` method, you may provide a closure that should be executed if t })->catch(function (Throwable $e) { // This job has failed... }); + +> {note} Since `catch` callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within `catch` callbacks. ## Running The Queue Worker From a24d8f072348e5099b86db973f984ae74ebeee4b Mon Sep 17 00:00:00 2001 From: Elijah Schrock <70502486+ElijahSchrock@users.noreply.github.com> Date: Wed, 23 Nov 2022 12:55:06 -0800 Subject: [PATCH 0507/2609] Documents the fluent string helper `headline` (#8358) Co-authored-by: elijahschrockAGT --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 7a26c660bcd..37c21bb1d65 100644 --- a/helpers.md +++ b/helpers.md @@ -190,6 +190,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [exactly](#method-fluent-str-exactly) [explode](#method-fluent-str-explode) [finish](#method-fluent-str-finish) +[headline](#method-fluent-str-headline) [inlineMarkdown](#method-fluent-str-inline-markdown) [is](#method-fluent-str-is) [isAscii](#method-fluent-str-is-ascii) @@ -2358,6 +2359,21 @@ The `finish` method adds a single instance of the given value to a string if it // this/string/ + +#### `headline` {.collection-method} + +The `headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: + + use Illuminate\Support\Str; + + $headline = Str::of('taylor_otwell')->headline(); + + // Taylor Otwell + + $headline = Str::of('EmailNotificationSent')->headline(); + + // Email Notification Sent + #### `inlineMarkdown` {.collection-method} From cce26b2c2b58faacc8f96d25b6e1454a51aa86ba Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 25 Nov 2022 01:19:32 +1100 Subject: [PATCH 0508/2609] Documents `Vite::useManifestFilename()` (#8352) --- vite.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vite.md b/vite.md index a82a7670392..3ae4b298e66 100644 --- a/vite.md +++ b/vite.md @@ -738,6 +738,7 @@ Out of the box, Laravel's Vite plugin uses sensible conventions that should work {{ Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file... ->useBuildDirectory('bundle') // Customize the build directory... + ->useManifestFilename('assets.json') // Customize the manifest filename... ->withEntryPoints(['resources/js/app.js']) // Specify the entry points... }} @@ -757,5 +758,8 @@ export default defineConfig({ input: ['resources/js/app.js'], // Specify the entry points... }), ], + build: { + manifest: 'assets.json', // Customize the manifest filename... + }, }); ``` From 1335e7ee565e7306029105a12594b83fa8b8f541 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 28 Nov 2022 15:26:37 +0100 Subject: [PATCH 0509/2609] Update support policy (#8363) Remove info about Laravel 6.x & 7.x and update dates for Laravel 9.x, 10.x & 11.x to match Tuesdays which is the common release date. --- releases.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/releases.md b/releases.md index 2b6aa90422a..3ae008328d5 100644 --- a/releases.md +++ b/releases.md @@ -23,11 +23,10 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | | --- | --- | --- | --- | --- | -| 6 (LTS) | 7.2 - 8.0 | September 3rd, 2019 | January 25th, 2022 | September 6th, 2022 | -| 7 | 7.2 - 8.0 | March 3rd, 2020 | October 6th, 2020 | March 3rd, 2021 | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | -| 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 8th, 2024 | -| 10 | 8.1 | February 7th, 2023 | August 7th, 2024 | February 7th, 2025 | +| 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | +| 10 | 8.1 | February 7th, 2023 | August 6th, 2024 | February 4th, 2025 | +| 11 | 8.2 | February 6th, 2024 | August 5th, 2025 | February 3rd, 2026 |
    From 42fcf4526c09e8b6923ef4322ee5878eff9c75f7 Mon Sep 17 00:00:00 2001 From: Anjorin Damilare Date: Mon, 28 Nov 2022 21:07:14 +0100 Subject: [PATCH 0510/2609] [9.x] Add example for cancelled job batch option. (#8364) * [9.x] Add example for cancelled job batch option. * Update queues.md Co-authored-by: Taylor Otwell --- queues.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/queues.md b/queues.md index a527e0debea..7b250d8c058 100644 --- a/queues.md +++ b/queues.md @@ -1468,6 +1468,10 @@ Sometimes, your `jobs_batches` table may accumulate batch records for batches th $schedule->command('queue:prune-batches --hours=48 --unfinished=72')->daily(); +Likewise, your `jobs_batches` table may also accumulate batch records for cancelled batches. You may instruct the `queue:prune-batches` command to prune these cancelled batch records using the `cancelled` option: + + $schedule->command('queue:prune-batches --hours=48 --cancelled=72')->daily(); + ## Queueing Closures From 17051d87e0b16264c13d1b938aa3673430e4e7ef Mon Sep 17 00:00:00 2001 From: Hristo Dimitrov <45231299+hdimitrov1@users.noreply.github.com> Date: Tue, 29 Nov 2022 15:16:54 +0000 Subject: [PATCH 0511/2609] Add note to specify default scopes are ignored for personal access tokens (#8367) * add note to specify default scopes are ignored for personal access tokens * Update passport.md Co-authored-by: Hristo Co-authored-by: Taylor Otwell --- passport.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/passport.md b/passport.md index b9548855cc8..8ae80964ab2 100644 --- a/passport.md +++ b/passport.md @@ -1064,6 +1064,9 @@ If a client does not request any specific scopes, you may configure your Passpor 'place-orders', ]); +> **Note** +> Passport's default scopes do not apply to personal access tokens that are generated by the user. + ### Assigning Scopes To Tokens From 891fd5149412e1c549cfd1951770d7907e306909 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Nov 2022 09:44:01 -0600 Subject: [PATCH 0512/2609] document singleton resources --- controllers.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/controllers.md b/controllers.md index 0c8bcc90518..0fcc86f28e8 100644 --- a/controllers.md +++ b/controllers.md @@ -13,6 +13,7 @@ - [Scoping Resource Routes](#restful-scoping-resource-routes) - [Localizing Resource URIs](#restful-localizing-resource-uris) - [Supplementing Resource Controllers](#restful-supplementing-resource-controllers) + - [Singleton Resource Controllers](#singleton-resource-controllers) - [Dependency Injection & Controllers](#dependency-injection-and-controllers) @@ -375,6 +376,62 @@ If you need to add additional routes to a resource controller beyond the default > **Note** > Remember to keep your controllers focused. If you find yourself routinely needing methods outside of the typical set of resource actions, consider splitting your controller into two, smaller controllers. + +### Singleton Resource Controllers + +Sometimes, your application will have resources that may only have a single instance. For example, a user's "profile" can be edited or updated, but a user may not have more than one "profile". Likewise, an image may have a single "thumbnail". These resources are called "singleton resources", meaning one and only one instance of the resource may exist. In these scenarios, you may register a "singleton" resource controller: + +```php +use App\Http\Controllers\ProfileController; +use Illuminate\Support\Facades\Route; + +Route::singleton('profile', ProfileController::class); +``` + +The singleton resource definition above will register the following routes. As you can see, "creation" routes are not registered for singleton resources, and the registered routes do not accept an identifier since only one instance of the resource may exist: + +Verb | URI | Action | Route Name +----------|-----------------------------------|--------------|--------------------- +GET | `/profile` | show | profile.show +GET | `/profile/edit` | edit | profile.edit +PUT/PATCH | `/profile` | update | profile.update +DELETE | `/profile` | destroy | profile.destroy + +Singleton resources may also be nested within a standard resource: + +```php +Route::singleton('photos.thumbnail', ThumbnailController::class); +``` + +In this example, the `photos` resource would receive all of the [standard resource routes](#actions-handled-by-resource-controller); however, the `thumbnail` resource would be a singleton resource with the following routes: + +| Verb | URI | Action | Route Name | +|-----------|----------------------------------|---------|--------------------------| +| GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | +| GET | `/photos/{photo}/thumbnail/edit` | edit | photos.thumbnail.edit | +| PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | +| DELETE | `/photos/{photo}/thumbnail` | destroy | photos.thumbnail.destroy | + + +#### Creatable Singleton Resources + +Occasionally, you may want to define creation and storage routes for a singleton resource. To accomplish this, you may invoke the `creatable` method when registering the singleton resource route: + +```php +Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable(); +``` + +In this example, the following routes will be registered: + +| Verb | URI | Action | Route Name | +|-----------|------------------------------------|---------|--------------------------| +| GET | `/photos/{photo}/thumbnail/create` | create | photos.thumbnail.create | +| POST | `/photos/{photo}/thumbnail` | store | photos.thumbnail.store | +| GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | +| GET | `/photos/{photo}/thumbnail/edit` | edit | photos.thumbnail.edit | +| PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | +| DELETE | `/photos/{photo}/thumbnail` | destroy | photos.thumbnail.destroy | + ## Dependency Injection & Controllers From 7eccd79222be8d15a5140251d4d6294905e050e8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Nov 2022 09:45:57 -0600 Subject: [PATCH 0513/2609] wip --- helpers.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/helpers.md b/helpers.md index 37c21bb1d65..262741c519e 100644 --- a/helpers.md +++ b/helpers.md @@ -313,6 +313,8 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [policy](#method-policy) [redirect](#method-redirect) [report](#method-report) +[report_if](#method-report-if) +[report_unless](#method-report-unless) [request](#method-request) [rescue](#method-rescue) [resolve](#method-resolve) @@ -3741,6 +3743,24 @@ The `report` function also accepts a string as an argument. When a string is giv report('Something went wrong.'); + +#### `report_if()` {.collection-method} + +The `report_if` function will report an exception using your [exception handler](/docs/{{version}}/errors#the-exception-handler) if the given condition is `true`: + + report_if($shouldReport, $e); + + report_if($shouldReport, 'Something went wrong.'); + + +#### `report_unless()` {.collection-method} + +The `report_unless` function will report an exception using your [exception handler](/docs/{{version}}/errors#the-exception-handler) if the given condition is `false`: + + report_unless($reportingDisabled, $e); + + report_unless($reportingDisabled, 'Something went wrong.'); + #### `request()` {.collection-method} From 7ddfc692465683d308cb5e009554215fc803f9d5 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 30 Nov 2022 14:42:34 +0100 Subject: [PATCH 0514/2609] Update billing.md --- billing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/billing.md b/billing.md index d79d7df7cdf..06cd49ec7dc 100644 --- a/billing.md +++ b/billing.md @@ -78,7 +78,7 @@ When upgrading to a new version of Cashier, it's important that you carefully review [the upgrade guide](https://github.com/laravel/cashier-stripe/blob/master/UPGRADE.md). > **Warning** -> To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 14 utilizes Stripe API version `2022-08-01`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. +> To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 14 utilizes Stripe API version `2022-11-15`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. ## Installation From 2a05e69ae62a5fed0a6af59299c12607415da572 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 30 Nov 2022 10:23:36 -0600 Subject: [PATCH 0515/2609] update singleton resource route controller docs --- controllers.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/controllers.md b/controllers.md index 0fcc86f28e8..44ed8f1ebe7 100644 --- a/controllers.md +++ b/controllers.md @@ -395,7 +395,6 @@ Verb | URI | Action | Route Name GET | `/profile` | show | profile.show GET | `/profile/edit` | edit | profile.edit PUT/PATCH | `/profile` | update | profile.update -DELETE | `/profile` | destroy | profile.destroy Singleton resources may also be nested within a standard resource: @@ -410,7 +409,6 @@ In this example, the `photos` resource would receive all of the [standard resour | GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | | GET | `/photos/{photo}/thumbnail/edit` | edit | photos.thumbnail.edit | | PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | -| DELETE | `/photos/{photo}/thumbnail` | destroy | photos.thumbnail.destroy | #### Creatable Singleton Resources @@ -421,7 +419,7 @@ Occasionally, you may want to define creation and storage routes for a singleton Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable(); ``` -In this example, the following routes will be registered: +In this example, the following routes will be registered. As you can see, a `DELETE` route will also be registered for creatable singleton resources: | Verb | URI | Action | Route Name | |-----------|------------------------------------|---------|--------------------------| From 25beed11e992396c4af885da194a8f8e7a5a6bc8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 30 Nov 2022 14:44:28 -0600 Subject: [PATCH 0516/2609] fix enum capitalization --- eloquent-mutators.md | 4 ++-- releases.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index c9413f15cda..596abe636c3 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -443,8 +443,8 @@ Eloquent also allows you to cast your attribute values to PHP [Enums](https://ww Once you have defined the cast on your model, the specified attribute will be automatically cast to and from an enum when you interact with the attribute: - if ($server->status == ServerStatus::provisioned) { - $server->status = ServerStatus::ready; + if ($server->status == ServerStatus::Provisioned) { + $server->status = ServerStatus::Ready; $server->save(); } diff --git a/releases.md b/releases.md index 2b6aa90422a..24e378a679d 100644 --- a/releases.md +++ b/releases.md @@ -151,8 +151,8 @@ Eloquent now allows you to cast your attribute values to PHP ["backed" Enums](ht Once you have defined the cast on your model, the specified attribute will be automatically cast to and from an enum when you interact with the attribute: - if ($server->status == ServerStatus::provisioned) { - $server->status = ServerStatus::ready; + if ($server->status == ServerStatus::Provisioned) { + $server->status = ServerStatus::Ready; $server->save(); } From e3a8ebbd922e816f8291f8fac4bda8fca1b06a2c Mon Sep 17 00:00:00 2001 From: Daniel Sepp <43651556+bloomlive@users.noreply.github.com> Date: Wed, 30 Nov 2022 22:53:34 +0200 Subject: [PATCH 0517/2609] [9.x] Add apiSingleton documentation (#8369) * Add apiSingleton documentation * Remove extra linebreak * Formatting Co-authored-by: Taylor Otwell --- controllers.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/controllers.md b/controllers.md index 44ed8f1ebe7..4ee0bfd8e05 100644 --- a/controllers.md +++ b/controllers.md @@ -430,6 +430,21 @@ In this example, the following routes will be registered. As you can see, a `DEL | PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | | DELETE | `/photos/{photo}/thumbnail` | destroy | photos.thumbnail.destroy | + +#### API Singleton Resources + +The `apiSingleton` method may be used to register a singleton resource that will be manipulated via an API, thus rendering the `create` and `edit` routes unnecessary: + +```php +Route::apiSingleton('profile', ProfileController::class); +``` + +Of course, API singleton resources may also be `creatable`, which will register `store` and `destroy` routes for the resource: + +```php +Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable(); +``` + ## Dependency Injection & Controllers From 7cbf985bf595e2d4a70ae744554b92ee9b1ef6ac Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Dec 2022 12:38:07 -0600 Subject: [PATCH 0518/2609] show replyTo exampel --- mail.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mail.md b/mail.md index e8ac8b4f341..4c99a572f09 100644 --- a/mail.md +++ b/mail.md @@ -199,6 +199,14 @@ First, let's explore configuring the sender of the email. Or, in other words, wh ); } +If you would like, you may also specify a `replyTo` address: + + return new Envelope( + from: new Address('jeffrey@example.com', 'Jeffrey Way'), + replyTo: new Address('taylor@example.com', 'Taylor Otwell'), + subject: 'Order Shipped', + ); + #### Using A Global `from` Address From 159323253ad7de8ffd039bcac1976dc8844a90c5 Mon Sep 17 00:00:00 2001 From: Perry van der Meer <11609290+PerryvanderMeer@users.noreply.github.com> Date: Fri, 2 Dec 2022 16:14:00 +0100 Subject: [PATCH 0519/2609] Pass array to replyTo param in Envelope (#8372) See https://github.com/laravel/framework/issues/45162 --- mail.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 4c99a572f09..51d681044f9 100644 --- a/mail.md +++ b/mail.md @@ -203,7 +203,9 @@ If you would like, you may also specify a `replyTo` address: return new Envelope( from: new Address('jeffrey@example.com', 'Jeffrey Way'), - replyTo: new Address('taylor@example.com', 'Taylor Otwell'), + replyTo: [ + new Address('taylor@example.com', 'Taylor Otwell'), + ], subject: 'Order Shipped', ); From 3412e0a9576e5cc90ec259c9f82ec9a9a339e6dc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 6 Dec 2022 13:22:21 -0600 Subject: [PATCH 0520/2609] document filterable attributes --- scout.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/scout.md b/scout.md index d837e468127..f6a21120a53 100644 --- a/scout.md +++ b/scout.md @@ -181,6 +181,25 @@ By default, the entire `toArray` form of a given model will be persisted to its } } + +#### Configuring Filterable Data (MeiliSearch) + +Unlike Scout's other drivers, MeiliSearch requires you to pre-define the attributes that will be "filterable". Filterable attributes are any attributes you plan to filter on when invoking Scout's `where` method. To define your filterable attributes, adjust the `index-settings` portion of your `meilisearch` configuration entry in your application's `scout` configuration file: + +```php +'meilisearch' => [ + 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700/'), + 'key' => env('MEILISEARCH_KEY', null), + 'index-settings' => [ + 'users' => [ + 'filterableAttributes'=> ['id', 'name', 'email'], + ], + 'flights' => [ + 'filterableAttributes'=> ['id', 'destination'], + ], + ], +], +``` ### Configuring The Model ID @@ -520,6 +539,9 @@ You may use the `whereIn` method to constrain results against a given set of val Since a search index is not a relational database, more advanced "where" clauses are not currently supported. +> **Warning** +> If your application is using MeiliSearch, you must configure your application's [filterable attributes](#configuring-filterable-data-for-meilisearch) before utilizing Scout's "where" clauses. + ### Pagination From c2439802923551f232f494bf069df3c56ecc0827 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 6 Dec 2022 13:25:21 -0600 Subject: [PATCH 0521/2609] document command --- scout.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scout.md b/scout.md index f6a21120a53..c1321a2e4b5 100644 --- a/scout.md +++ b/scout.md @@ -200,6 +200,13 @@ Unlike Scout's other drivers, MeiliSearch requires you to pre-define the attribu ], ], ``` + +After configuring your application's filterable attributes, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform MeiliSearch of your currently configured filterable attributes. For convenience, you may wish to make this command part of your deployment process: + +```shell +php artisan scout:sync-index-settings +``` + ### Configuring The Model ID From 827bc33c96bc1e675988669251358f83081008e5 Mon Sep 17 00:00:00 2001 From: Glenn Forrest Date: Wed, 7 Dec 2022 22:57:09 +0800 Subject: [PATCH 0522/2609] [9.x] Include closure examples for where JSON assertions (#8375) * Include closure examples for where JSON assertions * Remove semicolons from where closure examples --- http-tests.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/http-tests.md b/http-tests.md index 45298bc8e6e..412a3f7dbc5 100644 --- a/http-tests.md +++ b/http-tests.md @@ -364,6 +364,7 @@ Laravel also offers a beautiful way to fluently test your application's JSON res ->assertJson(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) ->whereNot('status', 'pending') ->missing('password') ->etc() @@ -419,6 +420,7 @@ In these situations, we may use the fluent JSON object's `has` method to make as ->first(fn ($json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -445,6 +447,7 @@ When testing these routes, you may use the `has` method to assert against the nu ->has('users.0', fn ($json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -458,6 +461,7 @@ However, instead of making two separate calls to the `has` method to assert agai ->has('users', 3, fn ($json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) From b13d2e42663c01107aa29f884a156658d42130e3 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 8 Dec 2022 18:03:07 +0330 Subject: [PATCH 0523/2609] fix the syntax of lowercase and uppercase rules (#8377) --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 3cd6a62890f..eacbae6749d 100644 --- a/validation.md +++ b/validation.md @@ -1318,7 +1318,7 @@ The field under validation must be less than the given _field_. The two fields m The field under validation must be less than or equal to the given _field_. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the [`size`](#rule-size) rule. -#### lowercase:_field_ +#### lowercase The field under validation must be lowercase. @@ -1626,7 +1626,7 @@ You may specify additional query conditions by customizing the query using the ` 'email' => Rule::unique('users')->where(fn ($query) => $query->where('account_id', 1)) -#### uppercase:_field_ +#### uppercase The field under validation must be uppercase. From 2c6d9a5bef7417cf6a19c213a4d0bfb3828c3dea Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 8 Dec 2022 18:15:08 +0330 Subject: [PATCH 0524/2609] [9.x] Add `Str::isUlid`, fluent `isUlid` and `whenIsUlid` (#8378) * add Str::isUlid, fluent isUlid and whenIsUlid * Update helpers.md Co-authored-by: Taylor Otwell --- helpers.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index 262741c519e..ef6082f0930 100644 --- a/helpers.md +++ b/helpers.md @@ -119,6 +119,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::is](#method-str-is) [Str::isAscii](#method-str-is-ascii) [Str::isJson](#method-str-is-json) +[Str::isUlid](#method-str-is-ulid) [Str::isUuid](#method-str-is-uuid) [Str::kebab](#method-kebab-case) [Str::lcfirst](#method-str-lcfirst) @@ -197,6 +198,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [isEmpty](#method-fluent-str-is-empty) [isNotEmpty](#method-fluent-str-is-not-empty) [isJson](#method-fluent-str-is-json) +[isUlid](#method-fluent-str-is-ulid) [isUuid](#method-fluent-str-is-uuid) [kebab](#method-fluent-str-kebab) [lcfirst](#method-fluent-str-lcfirst) @@ -252,6 +254,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [whenNotExactly](#method-fluent-str-when-not-exactly) [whenIs](#method-fluent-str-when-is) [whenIsAscii](#method-fluent-str-when-is-ascii) +[whenIsUlid](#method-fluent-str-when-is-ulid) [whenIsUuid](#method-fluent-str-when-is-uuid) [whenTest](#method-fluent-str-when-test) [wordCount](#method-fluent-str-word-count) @@ -1516,6 +1519,21 @@ The `Str::isJson` method determines if the given string is valid JSON: // false + +#### `Str::isUlid()` {.collection-method} + +The `Str::isUlid` method determines if the given string is a valid ULID: + + use Illuminate\Support\Str; + + $isUlid = Str::isUlid('01gd6r360bp37zj17nxb55yv40'); + + // true + + $isUlid = Str::isUlid('laravel'); + + // false + #### `Str::isUuid()` {.collection-method} @@ -2467,6 +2485,21 @@ The `isJson` method determines if a given string is valid JSON: // false + +#### `isUlid` {.collection-method} + +The `isUlid` method determines if a given string is a ULID: + + use Illuminate\Support\Str; + + $result = Str::of('01gd6r360bp37zj17nxb55yv40')->isUlid(); + + // true + + $result = Str::of('Taylor')->isUlid(); + + // false + #### `isUuid` {.collection-method} @@ -3233,12 +3266,25 @@ The `whenIsAscii` method invokes the given closure if the string is 7 bit ASCII. use Illuminate\Support\Str; - $string = Str::of('foo/bar')->whenIsAscii('laravel', function ($string) { + $string = Str::of('laravel')->whenIsAscii(function ($string) { return $string->title(); }); // 'Laravel' + +#### `whenIsUlid` {.collection-method} + +The `whenIsUlid` method invokes the given closure if the string is a valid ULID. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + + $string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function ($string) { + return $string->substr(0, 8); + }); + + // '01gd6r36' + #### `whenIsUuid` {.collection-method} @@ -3246,7 +3292,7 @@ The `whenIsUuid` method invokes the given closure if the string is a valid UUID. use Illuminate\Support\Str; - $string = Str::of('foo/bar')->whenIsUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de', function ($string) { + $string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function ($string) { return $string->substr(0, 8); }); From 373210c01b835c45414f286179dcb3a1bacd20f7 Mon Sep 17 00:00:00 2001 From: Njogu Amos <29255728+njoguamos@users.noreply.github.com> Date: Thu, 8 Dec 2022 17:52:20 +0300 Subject: [PATCH 0525/2609] Clarity for Meilisearch Configuring Indexes Settings (#8376) * Clarity for Meiliserach Configuring Indexes Settings The current documentation for `Configuring Indexes Settings (Meilisearch)` may imply that ONLY `filterableAttributes` can be configured. This PR intends to clarify that other supported fields can also be configured. These includes `searchableAttributes`,`filterableAttributes`,`sortableAttributes`,`rankingRules`, `stopWords`, and [settings supported by Meilisearch](https://docs.meilisearch.com/reference/api/settings.html#settings-object): * formatting * Update scout.md * formatting Co-authored-by: Taylor Otwell --- scout.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scout.md b/scout.md index c1321a2e4b5..894ad212745 100644 --- a/scout.md +++ b/scout.md @@ -182,9 +182,11 @@ By default, the entire `toArray` form of a given model will be persisted to its } -#### Configuring Filterable Data (MeiliSearch) +#### Configuring Filterable Data & Index Settings (MeiliSearch) -Unlike Scout's other drivers, MeiliSearch requires you to pre-define the attributes that will be "filterable". Filterable attributes are any attributes you plan to filter on when invoking Scout's `where` method. To define your filterable attributes, adjust the `index-settings` portion of your `meilisearch` configuration entry in your application's `scout` configuration file: +Unlike Scout's other drivers, MeiliSearch requires you to pre-define index search settings such as filterable attributes, sortable attributes, and [other supported settings fields](https://docs.meilisearch.com/reference/api/settings.html). + +Filterable attributes are any attributes you plan to filter on when invoking Scout's `where` method, while sortable attributes are any attributes you plan to sort by when invoking Scout's `orderBy` method. To define your index settings, adjust the `index-settings` portion of your `meilisearch` configuration entry in your application's `scout` configuration file: ```php 'meilisearch' => [ @@ -193,15 +195,18 @@ Unlike Scout's other drivers, MeiliSearch requires you to pre-define the attribu 'index-settings' => [ 'users' => [ 'filterableAttributes'=> ['id', 'name', 'email'], + 'sortableAttributes' => ['created_at'], + // Other settings fields... ], 'flights' => [ 'filterableAttributes'=> ['id', 'destination'], + 'sortableAttributes' => ['updated_at'], ], ], ], ``` -After configuring your application's filterable attributes, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform MeiliSearch of your currently configured filterable attributes. For convenience, you may wish to make this command part of your deployment process: +After configuring your application's index settings, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform MeiliSearch of your currently configured index settings. For convenience, you may wish to make this command part of your deployment process: ```shell php artisan scout:sync-index-settings From 00ed51add0d3cdfa294e907e7e662a46f643555b Mon Sep 17 00:00:00 2001 From: Sander de Vos <15908302+sander3@users.noreply.github.com> Date: Thu, 8 Dec 2022 17:57:27 +0100 Subject: [PATCH 0526/2609] LaravelValetDriver was moved to a namespace; updated the docs accordingly (#8379) --- valet.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/valet.md b/valet.md index 5e6ab9b71e0..a38d5a5b18f 100644 --- a/valet.md +++ b/valet.md @@ -424,6 +424,8 @@ The `frontControllerPath` method should return the fully qualified path to your 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`: + use Valet\Drivers\LaravelValetDriver; + class LocalValetDriver extends LaravelValetDriver { /** From e4b14cda4f0bf34eebbef8d30b755f3b775f942c Mon Sep 17 00:00:00 2001 From: Hemp Date: Thu, 8 Dec 2022 12:08:05 -0600 Subject: [PATCH 0527/2609] Update horizon.md (#8380) --- horizon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/horizon.md b/horizon.md index b4a59566530..b83ee868230 100644 --- a/horizon.md +++ b/horizon.md @@ -81,7 +81,7 @@ When you start Horizon, it will use the worker process configuration options for #### Supervisors -As you can see in Horizon's default configuration file. Each environment can contain one or more "supervisors". By default, the configuration file defines this supervisor as `supervisor-1`; however, you are free to name your supervisors whatever you want. Each supervisor is essentially responsible for "supervising" a group of worker processes and takes care of balancing worker processes across queues. +As you can see in Horizon's default configuration file, each environment can contain one or more "supervisors". By default, the configuration file defines this supervisor as `supervisor-1`; however, you are free to name your supervisors whatever you want. Each supervisor is essentially responsible for "supervising" a group of worker processes and takes care of balancing worker processes across queues. You may add additional supervisors to a given environment if you would like to define a new group of worker processes that should run in that environment. You may choose to do this if you would like to define a different balancing strategy or worker process count for a given queue used by your application. From f4b0dfc2fe263ea2301e3ad12d0158642bb5e272 Mon Sep 17 00:00:00 2001 From: Brian Ball Date: Fri, 9 Dec 2022 06:11:18 -0800 Subject: [PATCH 0528/2609] Remove "under the hood" (#8383) More language that, sounds fine when talking to native English speakers, it just doesn't add to the clarity of the docs. --- facades.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/facades.md b/facades.md index 2a8e9faeb8e..0744ae0d0ed 100644 --- a/facades.md +++ b/facades.md @@ -13,7 +13,7 @@ Throughout the Laravel documentation, you will see examples of code that interacts with Laravel's features via "facades". Facades provide a "static" interface to classes that are available in the application's [service container](/docs/{{version}}/container). Laravel ships with many facades which provide access to almost all of Laravel's features. -Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. It's perfectly fine if you don't totally understand how facades work under the hood - just go with the flow and continue learning about Laravel. +Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. It's perfectly fine if you don't totally understand how facades work - just go with the flow and continue learning about Laravel. All of Laravel's facades are defined in the `Illuminate\Support\Facades` namespace. So, we can easily access a facade like so: @@ -102,7 +102,7 @@ There is absolutely no practical difference between facades and helper functions return cache('key'); }); -Under the hood, the `cache` helper is going to call the `get` method on the class underlying the `Cache` facade. So, even though we are using the helper function, we can write the following test to verify that the method was called with the argument we expected: +The `cache` helper is going to call the `get` method on the class underlying the `Cache` facade. So, even though we are using the helper function, we can write the following test to verify that the method was called with the argument we expected: use Illuminate\Support\Facades\Cache; From 7e9733384b06715bfa94f303d3b3cf0715af918f Mon Sep 17 00:00:00 2001 From: Wermeille Bastien Date: Fri, 9 Dec 2022 15:11:38 +0100 Subject: [PATCH 0529/2609] Fix small sentence meaning (#8384) --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index e3d6f8d658d..ec08a2b97a2 100644 --- a/migrations.md +++ b/migrations.md @@ -60,7 +60,7 @@ php artisan schema:dump --prune When you execute this command, Laravel will write a "schema" file to your application's `database/schema` directory. The schema file's name will correspond to the database connection. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will execute first the SQL statements of the schema file of the database connection you are using. After executing the schema file's statements, Laravel will execute any remaining migrations that were not part of the schema dump. -If your application's tests use a different database connection than the one you typically use during local development, you should ensure you have a dumped a schema file using that database connection so that your tests are able to build your database. You may wish to do this after dumping the database connection you typically use during local development: +If your application's tests use a different database connection than the one you typically use during local development, you should ensure you have dumped a schema file using that database connection so that your tests are able to build your database. You may wish to do this after dumping the database connection you typically use during local development: ```shell php artisan schema:dump From a748da4ceecbbfe7e761c5b49e8bb91c7b7dbb99 Mon Sep 17 00:00:00 2001 From: Brian Ball Date: Fri, 9 Dec 2022 06:13:12 -0800 Subject: [PATCH 0530/2609] There is no "box" (#8381) * There is no "box" Can we let go of this phrase? "Out of the box, " * Update middleware.md Co-authored-by: Taylor Otwell --- middleware.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware.md b/middleware.md index cf3c112b5a5..94061ba4d65 100644 --- a/middleware.md +++ b/middleware.md @@ -183,7 +183,7 @@ The `withoutMiddleware` method can only remove route middleware and does not app Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish this using the `$middlewareGroups` property of your HTTP kernel. -Out of the box, Laravel comes with `web` and `api` middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, these middleware groups are automatically applied by your application's `App\Providers\RouteServiceProvider` service provider to routes within your corresponding `web` and `api` route files: +Laravel includes predefined `web` and `api` middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, these middleware groups are automatically applied by your application's `App\Providers\RouteServiceProvider` service provider to routes within your corresponding `web` and `api` route files: /** * The application's route middleware groups. From 15bae8b15673b7df6e0aeba1635880321b899564 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Fri, 9 Dec 2022 23:21:51 +0900 Subject: [PATCH 0531/2609] [9.x] Add ulidMorphs and nullableUlidMorphs to migrations.md (#8385) --- migrations.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/migrations.md b/migrations.md index ec08a2b97a2..90000aa7ddd 100644 --- a/migrations.md +++ b/migrations.md @@ -417,6 +417,7 @@ The schema builder blueprint offers a variety of methods that correspond to the [multiPolygon](#column-method-multiPolygon) [nullableMorphs](#column-method-nullableMorphs) [nullableTimestamps](#column-method-nullableTimestamps) +[nullableUlidMorphs](#column-method-nullableUlidMorphs) [nullableUuidMorphs](#column-method-nullableUuidMorphs) [point](#column-method-point) [polygon](#column-method-polygon) @@ -443,6 +444,7 @@ The schema builder blueprint offers a variety of methods that correspond to the [unsignedMediumInteger](#column-method-unsignedMediumInteger) [unsignedSmallInteger](#column-method-unsignedSmallInteger) [unsignedTinyInteger](#column-method-unsignedTinyInteger) +[ulidMorphs](#column-method-ulidMorphs) [uuidMorphs](#column-method-uuidMorphs) [ulid](#column-method-ulid) [uuid](#column-method-uuid) @@ -704,6 +706,13 @@ The method is similar to the [morphs](#column-method-morphs) method; however, th $table->nullableMorphs('taggable'); + +#### `nullableUlidMorphs()` {.collection-method} + +The method is similar to the [ulidMorphs](#column-method-ulidMorphs) method; however, the columns that are created will be "nullable": + + $table->nullableUlidMorphs('taggable'); + #### `nullableUuidMorphs()` {.collection-method} @@ -886,6 +895,15 @@ The `unsignedTinyInteger` method creates an `UNSIGNED TINYINT` equivalent column $table->unsignedTinyInteger('votes'); + +#### `ulidMorphs()` {.collection-method} + +The `ulidMorphs` method is a convenience method that adds a `{column}_id` `CHAR(26)` 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 ULID identifiers. In the following example, `taggable_id` and `taggable_type` columns would be created: + + $table->ulidMorphs('taggable'); + #### `uuidMorphs()` {.collection-method} From b777da728962faae39c90f61195956d7afcce089 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 12 Dec 2022 16:06:12 +0100 Subject: [PATCH 0532/2609] Update releases.md --- releases.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releases.md b/releases.md index 24e378a679d..9bd0b06785a 100644 --- a/releases.md +++ b/releases.md @@ -26,8 +26,8 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 6 (LTS) | 7.2 - 8.0 | September 3rd, 2019 | January 25th, 2022 | September 6th, 2022 | | 7 | 7.2 - 8.0 | March 3rd, 2020 | October 6th, 2020 | March 3rd, 2021 | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | -| 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 8th, 2024 | -| 10 | 8.1 | February 7th, 2023 | August 7th, 2024 | February 7th, 2025 | +| 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | +| 10 | 8.1 | February 7th, 2023 | August 6th, 2024 | February 4th, 2025 |
    From 1d5b951529f261456b066982d32230f3490107cd Mon Sep 17 00:00:00 2001 From: Hassan Tariq <115977031+lazypolymath@users.noreply.github.com> Date: Mon, 12 Dec 2022 20:15:07 +0500 Subject: [PATCH 0533/2609] Fix syntax error (#8391) --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index d50f5b4db17..dfa26b2440f 100644 --- a/authentication.md +++ b/authentication.md @@ -282,7 +282,7 @@ For complex query conditions, you may provide a closure in your array of credent 'email' => $email, 'password' => $password, fn ($query) => $query->has('activeSubscription'), - ]) { + ])) { // Authentication was successful... } From d7cbc1f505ac479c29ee555e250a0197e4310964 Mon Sep 17 00:00:00 2001 From: Erik Sadewater <72336569+esadewater@users.noreply.github.com> Date: Tue, 13 Dec 2022 01:22:58 +1000 Subject: [PATCH 0534/2609] Add Sail-PHP-Version (#8387) Added Sail-PHP-Version 8.2 as supported since v1.16.0 (https://github.com/laravel/sail/releases/tag/v1.16.0) --- sail.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 4454d468f11..bc5ad6c9b22 100644 --- a/sail.md +++ b/sail.md @@ -353,9 +353,12 @@ sail tinker ## PHP Versions -Sail currently supports serving your application via PHP 8.1, PHP 8.0, or PHP 7.4. The default PHP version used by Sail is currently PHP 8.1. 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: +Sail currently supports serving your application via PHP 8.2, 8.1, PHP 8.0, or PHP 7.4. The default PHP version used by Sail is currently PHP 8.1. 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.2 +context: ./vendor/laravel/sail/runtimes/8.2 + # PHP 8.1 context: ./vendor/laravel/sail/runtimes/8.1 From 041cce046c840021457f06e28dc0d89d88de95d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abdulkadir=20Cemilo=C4=9Flu?= <32229751+megasteve19@users.noreply.github.com> Date: Mon, 12 Dec 2022 17:35:39 +0200 Subject: [PATCH 0535/2609] documenting 'make:cast' command (#8386) * documenting 'make:cast' command * formatting Co-authored-by: Taylor Otwell --- eloquent-mutators.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 596abe636c3..f1dfbfd4a3a 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -488,9 +488,13 @@ 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. You may accomplish this by defining a class that implements the `CastsAttributes` interface. +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: -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: +```shell +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: ### Inbound Casting -Occasionally, you may need to write a custom cast 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. A classic example of an inbound only cast is a "hashing" cast. Inbound only custom casts should implement the `CastsInboundAttributes` interface, which only requires a `set` method to be defined. +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: + +```shell +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: Date: Mon, 12 Dec 2022 16:54:22 +0100 Subject: [PATCH 0536/2609] Added colum type docs for filtering in meilisearch (#8388) * Update scout.md * formatting * Update scout.md Co-authored-by: Taylor Otwell --- scout.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scout.md b/scout.md index 894ad212745..db6d3dab700 100644 --- a/scout.md +++ b/scout.md @@ -181,6 +181,17 @@ By default, the entire `toArray` form of a given model will be persisted to its } } +Some search engines such as MeiliSearch will only perform filter operations (`>`, `<`, etc.) on data of the correct type. So, when using these search engines and customizing your searchable data, you should ensure that numeric values are cast to their correct type: + + public function toSearchableArray() + { + return [ + 'id' => (int) $this->id, + 'name' => $this->name, + 'price' => (float) $this->price, + ]; + } + #### Configuring Filterable Data & Index Settings (MeiliSearch) From 812cd68616af71c5b25a029cc66f36ec608761b3 Mon Sep 17 00:00:00 2001 From: Jamie York Date: Wed, 14 Dec 2022 18:40:07 +0000 Subject: [PATCH 0537/2609] Add assert streamed content docs (#8399) The docs for this merged PR: https://github.com/laravel/framework/pull/45298 --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 412a3f7dbc5..8426cc1de8d 100644 --- a/http-tests.md +++ b/http-tests.md @@ -650,6 +650,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertLocation](#assert-location) [assertContent](#assert-content) [assertNoContent](#assert-no-content) +[assertStreamedContent](#assert-streamed-content) [assertNotFound](#assert-not-found) [assertOk](#assert-ok) [assertPlainCookie](#assert-plain-cookie) @@ -963,6 +964,13 @@ Assert that the response has the given HTTP status code and no content: $response->assertNoContent($status = 204); + +#### assertStreamedContent + +Assert that the given string matches the streamed response content: + + $response->assertStreamedContent($value); + #### assertNotFound From 21b95cdef5b6e5a8d557f50052a0f5ac3b2ef441 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 15 Dec 2022 18:54:28 +0330 Subject: [PATCH 0538/2609] [9.x] Add `ascii` and `ulid` validation rules (#8397) * add `ascii` and `ulid` validation rules * Update validation.md Co-authored-by: Taylor Otwell --- validation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/validation.md b/validation.md index eacbae6749d..71ea356d99b 100644 --- a/validation.md +++ b/validation.md @@ -806,6 +806,7 @@ Below is a list of all available validation rules and their function: [Alpha Dash](#rule-alpha-dash) [Alpha Numeric](#rule-alpha-num) [Array](#rule-array) +[Ascii](#rule-ascii) [Bail](#rule-bail) [Before (Date)](#rule-before) [Before Or Equal (Date)](#rule-before-or-equal) @@ -883,6 +884,7 @@ Below is a list of all available validation rules and their function: [Unique (Database)](#rule-unique) [Uppercase](#rule-uppercase) [URL](#rule-url) +[ULID](#rule-ulid) [UUID](#rule-uuid)
    @@ -956,6 +958,11 @@ When additional values are provided to the `array` rule, each key in the input a In general, you should always specify the array keys that are allowed to be present within your array. + +#### ascii + +The field under validation must be entirely 7-bit ASCII characters. + #### bail @@ -1635,6 +1642,11 @@ The field under validation must be uppercase. The field under validation must be a valid URL. + +#### ulid + +The field under validation must be a valid [Universally Unique Lexicographically Sortable Identifier](https://github.com/ulid/spec) (ULID). + #### uuid From eb58fb282324494cc31d224812d414f4e2efb503 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 15 Dec 2022 19:02:06 +0330 Subject: [PATCH 0539/2609] [9.x] Remove Doctrine DBAL from prerequisites of renaming/dropping column (#8396) * remove Doctrine DBAL from prerequisites of renaming/dropping column * formatting Co-authored-by: Taylor Otwell --- migrations.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/migrations.md b/migrations.md index 90000aa7ddd..c2fe7d15a5d 100644 --- a/migrations.md +++ b/migrations.md @@ -15,6 +15,7 @@ - [Available Column Types](#available-column-types) - [Column Modifiers](#column-modifiers) - [Modifying Columns](#modifying-columns) + - [Renaming Columns](#renaming-columns) - [Dropping Columns](#dropping-columns) - [Indexes](#indexes) - [Creating Indexes](#creating-indexes) @@ -1056,21 +1057,31 @@ We could also modify a column to be nullable: > The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `double`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `tinyText`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). -#### Renaming Columns +### Renaming Columns -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: +To rename a column, you may use the `renameColumn` method provided by the schema builder: Schema::table('users', function (Blueprint $table) { $table->renameColumn('from', 'to'); }); -> **Warning** -> Renaming an `enum` column is not currently supported. + +#### Renaming Columns On Legacy Databases + +If you are running a database installation older than one of the following releases, you should ensure that you have installed the `doctrine/dbal` library via the Composer package manager before renaming a column: + +
    + +- MySQL < `8.0.3` +- MariaDB < `10.5.2` +- SQLite < `3.25.0` + +
    ### Dropping Columns -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: +To drop a column, you may use the `dropColumn` method on the schema builder: Schema::table('users', function (Blueprint $table) { $table->dropColumn('votes'); @@ -1082,8 +1093,11 @@ You may drop multiple columns from a table by passing an array of column names t $table->dropColumn(['votes', 'avatar', 'location']); }); -> **Warning** -> Dropping or modifying multiple columns within a single migration while using an SQLite database is not supported. + + +#### Dropping Columns On Legacy Databases + +If you are running a version of SQLite prior to `3.35.0`, you must install the `doctrine/dbal` package via the Composer package manager before the `dropColumn` method may be used. Dropping or modifying multiple columns within a single migration while using this package is not supported. #### Available Command Aliases @@ -1167,6 +1181,9 @@ To rename an index, you may use the `renameIndex` method provided by the schema $table->renameIndex('from', 'to') +> **Warning** +> If your application is utilizing an SQLite database, you must install the `doctrine/dbal` package via the Composer package manager before the `renameIndex` method may be used. + ### Dropping Indexes From 00edb20dbaf441f419ff2ecfec500a946121a533 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 09:36:28 -0600 Subject: [PATCH 0540/2609] add note about soft deleting --- scout.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scout.md b/scout.md index db6d3dab700..2342bff4a3d 100644 --- a/scout.md +++ b/scout.md @@ -200,16 +200,19 @@ Unlike Scout's other drivers, MeiliSearch requires you to pre-define index searc Filterable attributes are any attributes you plan to filter on when invoking Scout's `where` method, while sortable attributes are any attributes you plan to sort by when invoking Scout's `orderBy` method. To define your index settings, adjust the `index-settings` portion of your `meilisearch` configuration entry in your application's `scout` configuration file: ```php +use App\Models\User; +use App\Models\Flight; + 'meilisearch' => [ 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700/'), 'key' => env('MEILISEARCH_KEY', null), 'index-settings' => [ - 'users' => [ + User::class => [ 'filterableAttributes'=> ['id', 'name', 'email'], 'sortableAttributes' => ['created_at'], // Other settings fields... ], - 'flights' => [ + Flight::class => [ 'filterableAttributes'=> ['id', 'destination'], 'sortableAttributes' => ['updated_at'], ], @@ -217,6 +220,8 @@ Filterable attributes are any attributes you plan to filter on when invoking Sco ], ``` +If the model underlying a given index is soft deletable, Scout will automatically include support for filtering on soft deleted models to that index. + After configuring your application's index settings, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform MeiliSearch of your currently configured index settings. For convenience, you may wish to make this command part of your deployment process: ```shell From 8262ed9b4f980aa16b89e3561bdc3d47c6f038b3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 09:37:11 -0600 Subject: [PATCH 0541/2609] wip --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index 2342bff4a3d..673a786587f 100644 --- a/scout.md +++ b/scout.md @@ -220,7 +220,7 @@ use App\Models\Flight; ], ``` -If the model underlying a given index is soft deletable, Scout will automatically include support for filtering on soft deleted models to that index. +If the model underlying a given index is soft deletable and is included in the `index-settings` array, Scout will automatically include support for filtering on soft deleted models to that index. After configuring your application's index settings, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform MeiliSearch of your currently configured index settings. For convenience, you may wish to make this command part of your deployment process: From 8e8ff6aaf0dcacfa1b7a3f5e7386d660f7b760eb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 09:39:12 -0600 Subject: [PATCH 0542/2609] wip --- scout.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scout.md b/scout.md index 673a786587f..a391bf20c1f 100644 --- a/scout.md +++ b/scout.md @@ -220,7 +220,13 @@ use App\Models\Flight; ], ``` -If the model underlying a given index is soft deletable and is included in the `index-settings` array, Scout will automatically include support for filtering on soft deleted models to that index. +If the model underlying a given index is soft deletable and is included in the `index-settings` array, Scout will automatically include support for filtering on soft deleted models on that index. If you have no other filterable or sortable attributes to define for a soft deletable model index, you may simple add an empty entry to the `index-settings` array for that model: + +```php +'index-settings' => [ + Flight::class => [] +], +``` After configuring your application's index settings, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform MeiliSearch of your currently configured index settings. For convenience, you may wish to make this command part of your deployment process: From 080fcdd3d3817f55c05f26cd721bb1346fe91635 Mon Sep 17 00:00:00 2001 From: James Hemery Date: Thu, 15 Dec 2022 16:40:47 +0100 Subject: [PATCH 0543/2609] add viaConnections to notifications doc (#8393) * add viaConnections to notifications doc * formatting Co-authored-by: Taylor Otwell --- notifications.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/notifications.md b/notifications.md index 16f958dda51..167ec9e7a31 100644 --- a/notifications.md +++ b/notifications.md @@ -224,6 +224,21 @@ If you would like to specify a specific queue that should be used for each notif ]; } +Likewise, if you would like to specify a specific queue connection that should be used for each notification channel supported by the notification, you may define a `viaConnections` method on your notification. This method should return an array of channel name / queue connection name pairs: + + /** + * Determine which connections should be used for each notification channel. + * + * @return array + */ + public function viaConnections() + { + return [ + 'mail' => 'redis', + 'database' => 'sync', + ]; + } + #### Queued Notifications & Database Transactions From 3a635e6a02d3b141f078f61c80f2342275d8adfa Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 09:42:08 -0600 Subject: [PATCH 0544/2609] document closure on throwIf --- http-client.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index a3aab56fe55..058854b702f 100644 --- a/http-client.md +++ b/http-client.md @@ -238,10 +238,16 @@ If you have a response instance and would like to throw an instance of `Illumina // Throw an exception if an error occurred and the given condition is true... $response->throwIf($condition); - + + // Throw an exception if an error occurred and the given closure resolves to true... + $response->throwIf(fn () => true); + // Throw an exception if an error occurred and the given condition is false... $response->throwUnless($condition); + // Throw an exception if an error occurred and the given closure resolves to false... + $response->throwUnless(fn () => false); + return $response['user']['id']; The `Illuminate\Http\Client\RequestException` instance has a public `$response` property which will allow you to inspect the returned response. From 118b7d17302a172b5f1ca99f9f5850236608fd72 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 09:44:42 -0600 Subject: [PATCH 0545/2609] document data-dusk as new preferred method --- dusk.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dusk.md b/dusk.md index 55db79d4d66..9e9e55d2d2e 100644 --- a/dusk.md +++ b/dusk.md @@ -459,11 +459,11 @@ Choosing good CSS selectors for interacting with elements is one of the hardest $browser->click('.login-page .container div > button'); -Dusk selectors allow you to focus on writing effective tests rather than remembering CSS selectors. To define a selector, add a `dusk` attribute to your HTML element. Then, when interacting with a Dusk browser, prefix the selector with `@` to manipulate the attached element within your test: +Dusk selectors allow you to focus on writing effective tests rather than remembering CSS selectors. To define a selector, add a `data-dusk` attribute to your HTML element. Then, when interacting with a Dusk browser, prefix the selector with `@` to manipulate the attached element within your test: // HTML... - + // Test... @@ -1545,7 +1545,7 @@ Dusk even allows you to make assertions on the state of [Vue component](https:// // HTML... - + // Component Definition... From d948d94b5403fc4d43335fad8a9f4c29840622a8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 09:45:37 -0600 Subject: [PATCH 0546/2609] wip --- http-client.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-client.md b/http-client.md index 058854b702f..df368b092e9 100644 --- a/http-client.md +++ b/http-client.md @@ -240,13 +240,13 @@ If you have a response instance and would like to throw an instance of `Illumina $response->throwIf($condition); // Throw an exception if an error occurred and the given closure resolves to true... - $response->throwIf(fn () => true); + $response->throwIf(fn ($response) => true); // Throw an exception if an error occurred and the given condition is false... $response->throwUnless($condition); // Throw an exception if an error occurred and the given closure resolves to false... - $response->throwUnless(fn () => false); + $response->throwUnless(fn ($response) => false); return $response['user']['id']; From 21e7ed878cd87f43e2406f0dc10a7aeec66d1fb8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 10:01:48 -0600 Subject: [PATCH 0547/2609] wip --- notifications.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/notifications.md b/notifications.md index 167ec9e7a31..0f0119f8acb 100644 --- a/notifications.md +++ b/notifications.md @@ -206,36 +206,36 @@ By default, queued notifications will be queued using your application's default */ public $connection = 'redis'; - -#### Customizing Notification Channel Queues - -If you would like to specify a specific queue that should be used for each notification channel supported by the notification, you may define a `viaQueues` method on your notification. This method should return an array of channel name / queue name pairs: +Or, if you would like to specify a specific queue connection that should be used for each notification channel supported by the notification, you may define a `viaConnections` method on your notification. This method should return an array of channel name / queue connection name pairs: /** - * Determine which queues should be used for each notification channel. + * Determine which connections should be used for each notification channel. * * @return array */ - public function viaQueues() + public function viaConnections() { return [ - 'mail' => 'mail-queue', - 'slack' => 'slack-queue', + 'mail' => 'redis', + 'database' => 'sync', ]; } -Likewise, if you would like to specify a specific queue connection that should be used for each notification channel supported by the notification, you may define a `viaConnections` method on your notification. This method should return an array of channel name / queue connection name pairs: + +#### Customizing Notification Channel Queues + +If you would like to specify a specific queue that should be used for each notification channel supported by the notification, you may define a `viaQueues` method on your notification. This method should return an array of channel name / queue name pairs: /** - * Determine which connections should be used for each notification channel. + * Determine which queues should be used for each notification channel. * * @return array */ - public function viaConnections() + public function viaQueues() { return [ - 'mail' => 'redis', - 'database' => 'sync', + 'mail' => 'mail-queue', + 'slack' => 'slack-queue', ]; } From e14d606f663a6c0a356220fc6028275ae890b831 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 10:46:41 -0600 Subject: [PATCH 0548/2609] add note about react and vue views --- views.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/views.md b/views.md index 30b521b836c..c17982befb5 100644 --- a/views.md +++ b/views.md @@ -1,6 +1,7 @@ # Views - [Introduction](#introduction) + - [Writing Views In React / Vue](#writing-views-in-react-or-vue) - [Creating & Rendering Views](#creating-and-rendering-views) - [Nested View Directories](#nested-view-directories) - [Creating The First Available View](#creating-the-first-available-view) @@ -14,7 +15,9 @@ ## Introduction -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: +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. When using Laravel, view templates are usually written using the [Blade templating language](/docs/{{version}}/blade). A simple view might look something like this: ```blade @@ -35,6 +38,13 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return > **Note** > Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. + +### Writing Views In React / Vue + +Instead of writing their frontend templates in PHP via Blade, many developers have begun to prefer to write their templates in React or Vue. Laravel makes this painless thanks to [Inertia](https://inertiajs.com/), a library that makes it a cinch to tie your React / Vue frontend to your Laravel backend. + +Our Breeze and Jetstream [starter kits](/docs/{{version}}/starter-kits) give you a great starting point for your next Laravel application powered by Inertia. In addition, the [Laravel Bootcamp](https://bootcamp.laravel.com) provides a full demonstration of building a Laravel application powered by Inertia, including examples in Vue and React. + ## Creating & Rendering Views From 38b30aaf444d73a39abc8b130408d5355e5e6ff5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 10:47:10 -0600 Subject: [PATCH 0549/2609] wip --- views.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views.md b/views.md index c17982befb5..586dcf99b5e 100644 --- a/views.md +++ b/views.md @@ -41,7 +41,7 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return ### Writing Views In React / Vue -Instead of writing their frontend templates in PHP via Blade, many developers have begun to prefer to write their templates in React or Vue. Laravel makes this painless thanks to [Inertia](https://inertiajs.com/), a library that makes it a cinch to tie your React / Vue frontend to your Laravel backend. +Instead of writing their frontend templates in PHP via Blade, many developers have begun to prefer to write their templates using React or Vue. Laravel makes this painless thanks to [Inertia](https://inertiajs.com/), a library that makes it a cinch to tie your React / Vue frontend to your Laravel backend. Our Breeze and Jetstream [starter kits](/docs/{{version}}/starter-kits) give you a great starting point for your next Laravel application powered by Inertia. In addition, the [Laravel Bootcamp](https://bootcamp.laravel.com) provides a full demonstration of building a Laravel application powered by Inertia, including examples in Vue and React. From 7d8db97e6d047460ed69ea444ed3b393667ca0e4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 10:47:41 -0600 Subject: [PATCH 0550/2609] wip --- views.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views.md b/views.md index 586dcf99b5e..f3c0da56b50 100644 --- a/views.md +++ b/views.md @@ -41,7 +41,7 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return ### Writing Views In React / Vue -Instead of writing their frontend templates in PHP via Blade, many developers have begun to prefer to write their templates using React or Vue. Laravel makes this painless thanks to [Inertia](https://inertiajs.com/), a library that makes it a cinch to tie your React / Vue frontend to your Laravel backend. +Instead of writing their frontend templates in PHP via Blade, many developers have begun to prefer to write their templates using React or Vue. Laravel makes this painless thanks to [Inertia](https://inertiajs.com/), a library that makes it a cinch to tie your React / Vue frontend to your Laravel backend without the typical complexities of building an SPA. Our Breeze and Jetstream [starter kits](/docs/{{version}}/starter-kits) give you a great starting point for your next Laravel application powered by Inertia. In addition, the [Laravel Bootcamp](https://bootcamp.laravel.com) provides a full demonstration of building a Laravel application powered by Inertia, including examples in Vue and React. From 6d49fc708187265600197611033a8aa07ba47175 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 10:51:51 -0600 Subject: [PATCH 0551/2609] wip --- blade.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/blade.md b/blade.md index 713ea5acde6..0c9615aae18 100644 --- a/blade.md +++ b/blade.md @@ -1,6 +1,7 @@ # Blade Templates - [Introduction](#introduction) + - [Supercharging Blade With Livewire](#supercharging-blade-with-livewire) - [Displaying Data](#displaying-data) - [HTML Entity Encoding](#html-entity-encoding) - [Blade & JavaScript Frameworks](#blade-and-javascript-frameworks) @@ -55,8 +56,10 @@ Blade views may be returned from routes or controllers using the global `view` h return view('greeting', ['name' => 'Finn']); }); -> **Note** -> Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://laravel-livewire.com). + +### Supercharging Blade With Livewire + +Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://laravel-livewire.com). Livewire allows you to write Blade components that are augmented with dynamic functionality that would typically only be possible via frontend frameworks like React or Vue, providing a great approach to building modern, reactive frontends without the complexities, client-side rendering, or build steps of many JavaScript frameworks. ## Displaying Data From 8c760aeaa9613b5f81fd383da4aa8ce82c135a90 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 Dec 2022 17:07:15 -0600 Subject: [PATCH 0552/2609] wip --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 3ae4b298e66..df2b312df80 100644 --- a/vite.md +++ b/vite.md @@ -336,7 +336,7 @@ createInertiaApp({ ### URL Processing -When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of things to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory. +When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of caveats to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory. When referencing relative asset paths, you should remember that the paths are relative to the file where they are referenced. Any assets referenced via a relative path will be re-written, versioned, and bundled by Vite. From 42cba3f9dbfa29557c977c2483df6b196d01de44 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 16 Dec 2022 08:45:56 -0600 Subject: [PATCH 0553/2609] wip --- dusk.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dusk.md b/dusk.md index 9e9e55d2d2e..55db79d4d66 100644 --- a/dusk.md +++ b/dusk.md @@ -459,11 +459,11 @@ Choosing good CSS selectors for interacting with elements is one of the hardest $browser->click('.login-page .container div > button'); -Dusk selectors allow you to focus on writing effective tests rather than remembering CSS selectors. To define a selector, add a `data-dusk` attribute to your HTML element. Then, when interacting with a Dusk browser, prefix the selector with `@` to manipulate the attached element within your test: +Dusk selectors allow you to focus on writing effective tests rather than remembering CSS selectors. To define a selector, add a `dusk` attribute to your HTML element. Then, when interacting with a Dusk browser, prefix the selector with `@` to manipulate the attached element within your test: // HTML... - + // Test... @@ -1545,7 +1545,7 @@ Dusk even allows you to make assertions on the state of [Vue component](https:// // HTML... - + // Component Definition... From 80b834c53d26dc80725b82c4eb84e384ec889c22 Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Mon, 19 Dec 2022 08:47:23 -0700 Subject: [PATCH 0554/2609] Correct grammar (#8409) Set up is being used as a verb in this case and should be two words. --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index 72313c96ac6..6812e22aa6a 100644 --- a/cache.md +++ b/cache.md @@ -42,7 +42,7 @@ The cache configuration file also contains various other options, which are docu #### Database -When using the `database` cache driver, you will need to setup a table to contain the cache items. You'll find an example `Schema` declaration for the table below: +When using the `database` cache driver, you will need to set up a table to contain the cache items. You'll find an example `Schema` declaration for the table below: Schema::create('cache', function ($table) { $table->string('key')->unique(); From 20d04480b64fb28a0f6b48b730a9b3d628562963 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Dec 2022 08:18:48 -0600 Subject: [PATCH 0555/2609] decimal rule --- validation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/validation.md b/validation.md index 71ea356d99b..44a290ee660 100644 --- a/validation.md +++ b/validation.md @@ -817,6 +817,7 @@ Below is a list of all available validation rules and their function: [Date](#rule-date) [Date Equals](#rule-date-equals) [Date Format](#rule-date-format) +[Decimal](#rule-decimal) [Declined](#rule-declined) [Declined If](#rule-declined-if) [Different](#rule-different) @@ -1021,6 +1022,17 @@ The field under validation must be equal to the given date. The dates will be pa The field under validation must match the given _format_. You should use **either** `date` or `date_format` when validating a field, not both. This validation rule supports all formats supported by PHP's [DateTime](https://www.php.net/manual/en/class.datetime.php) class. + +#### decimal:_min_,_max_ + +The field under validation must be numeric and must contain the specified number of decimal places: + + // Must have exactly two decimal places (9.99)... + 'price' => 'decimal:2' + + // Must have between 2 and 4 decimal places... + 'price' => 'decimal:2,4' + #### declined From 5afca801cb812c8bd4b9f205c712f9296da4a7ab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Dec 2022 08:19:17 -0600 Subject: [PATCH 0556/2609] document whereUlid --- routing.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing.md b/routing.md index 8379951863e..4aa397aab80 100644 --- a/routing.md +++ b/routing.md @@ -236,6 +236,10 @@ For convenience, some commonly used regular expression patterns have helper meth // })->whereUuid('id'); + Route::get('/user/{id}', function ($id) { + // + })->whereUlid('id'); + Route::get('/category/{category}', function ($category) { // })->whereIn('category', ['movie', 'song', 'painting']); From 6989faf019e6c081d29f3b7845915e249fcb15f3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Dec 2022 08:29:59 -0600 Subject: [PATCH 0557/2609] document anonymous component paths as more flexible alternative to component namespaces --- blade.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/blade.md b/blade.md index 0c9615aae18..62bf8956887 100644 --- a/blade.md +++ b/blade.md @@ -29,7 +29,7 @@ - [Anonymous Index Components](#anonymous-index-components) - [Data Properties / Attributes](#data-properties-attributes) - [Accessing Parent Data](#accessing-parent-data) - - [Anonymous Components Namespaces](#anonymous-component-namespaces) + - [Anonymous Components Paths](#anonymous-component-paths) - [Building Layouts](#building-layouts) - [Layouts Using Components](#layouts-using-components) - [Layouts Using Template Inheritance](#layouts-using-template-inheritance) @@ -1381,14 +1381,12 @@ Because the `color` prop was only passed into the parent (``), it won't > **Warning** > The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. - -### Anonymous Component Namespaces + +### Anonymous Component Paths As previously discussed, anonymous components are typically defined by placing a Blade template within your `resources/views/components` directory. However, you may occasionally want to register other anonymous component paths with Laravel in addition to the default path. -For example, when building a vacation booking application, you may wish to place flight booking related anonymous components within a `resources/views/flights/bookings/components` directory. To inform Laravel of this anonymous component location, you may use the `anonymousComponentNamespace` method provided by the `Blade` facade. - -The `anonymousComponentNamespace` method accepts the "path" to the anonymous component location as its first argument and the "namespace" that components should be placed under as its second argument. As you will see in the example below, the "namespace" will be prefixed to the component's name when the component is rendered. Typically, this method should be called from the `boot` method of one of your application's [service providers](/docs/{{version}}/providers): +The `anonymousComponentPath` method accepts the "path" to the anonymous component location as its first argument and an optional "namespace" that components should be placed under as its second argument. Typically, this method should be called from the `boot` method of one of your application's [service providers](/docs/{{version}}/providers): /** * Bootstrap any application services. @@ -1397,13 +1395,23 @@ The `anonymousComponentNamespace` method accepts the "path" to the anonymous com */ public function boot() { - Blade::anonymousComponentNamespace('flights.bookings.components', 'flights'); + Blade::anonymousComponentPath(__DIR__.'/../components'); } -Given the example above, you may render a `panel` component that exists within the newly registered component directory like so: +When component paths are registered without a specified prefix as in the example above, they may be rendered in your Blade components without a corresponding prefix as well. For example, if a `panel.blade.php` component exists in the path registered above, it may be rendered like so: + +```blade + +``` + +Prefix "namespaces" may be provided as the second argument to the `anonymousComponentPath` method: + + Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard'); + +When a prefix is provided, components within that "namespace" may be rendered by prefixing to the component's namespace to the component name when the component is rendered: ```blade - + ``` From fd45ba9f20b0c2e22f8f43ac32fff4b9b8d24984 Mon Sep 17 00:00:00 2001 From: Julius Kiekbusch Date: Thu, 22 Dec 2022 21:31:36 +0100 Subject: [PATCH 0558/2609] [9.x] Add PHP 8.2 to supported PHP versions (#8413) --- releases.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releases.md b/releases.md index 9bd0b06785a..f1c73c9d7eb 100644 --- a/releases.md +++ b/releases.md @@ -26,8 +26,8 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 6 (LTS) | 7.2 - 8.0 | September 3rd, 2019 | January 25th, 2022 | September 6th, 2022 | | 7 | 7.2 - 8.0 | March 3rd, 2020 | October 6th, 2020 | March 3rd, 2021 | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | -| 9 | 8.0 - 8.1 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | -| 10 | 8.1 | February 7th, 2023 | August 6th, 2024 | February 4th, 2025 | +| 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | +| 10 | 8.1 - 8.2 | February 7th, 2023 | August 6th, 2024 | February 4th, 2025 |
    From 0da9a2e933542e81a487321247f5fba000034f6e Mon Sep 17 00:00:00 2001 From: Amadeusz Annissimo Date: Fri, 23 Dec 2022 15:16:08 +0100 Subject: [PATCH 0559/2609] how to run pint on selected files/directories (#8416) * how to run pint on selected files/directories * Update pint.md Co-authored-by: Taylor Otwell --- pint.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pint.md b/pint.md index 00fdb18fb6f..98de2a6e078 100644 --- a/pint.md +++ b/pint.md @@ -33,6 +33,14 @@ You can instruct Pint to fix code style issues by invoking the `pint` binary tha ./vendor/bin/pint ``` +You may also run Pint on specific files or directories: + +```shell +./vendor/bin/pint app/Models + +./vendor/bin/pint app/Models/User.php +``` + Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: ```shell From fbb65bb4859a5f70ebedd15b6a1cd43ddf56678f Mon Sep 17 00:00:00 2001 From: tushar-juneja <117457944+tushar-juneja@users.noreply.github.com> Date: Sat, 24 Dec 2022 02:03:12 +0530 Subject: [PATCH 0560/2609] Fix(typo): grammar error (#8417) --- configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index 2b2c0811721..7f9c7fde14d 100644 --- a/configuration.md +++ b/configuration.md @@ -115,7 +115,7 @@ You may also pass arguments to the `environment` method to determine if the envi ### Encrypting Environment Files -Unencrypted environment files should never be stored in source control. However, Laravel allows you to encrypt your environment files so that they may be safely be added to source control with the rest of your application. +Unencrypted environment files should never be stored in source control. However, Laravel allows you to encrypt your environment files so that they may safely be added to source control with the rest of your application. #### Encryption From 6df68c138fc565b859b28a621e75b4792b50b2de Mon Sep 17 00:00:00 2001 From: Vincent Hagen Date: Fri, 23 Dec 2022 21:38:40 +0100 Subject: [PATCH 0561/2609] Add clarification for job attempts (#8418) * add clarification for job attempts I stumbled on this edge case (of feature) in laravel while working on a client code base and couldn't find any documentation for it. Apparently a job with `0` $tries will be retried indefinitely. What is more, I tried defining `0` on the job and `1` on the worker, because if `0` meant infinite then, `1` attempt would be more specific to `infinite` attempts right? But the laravel workers doesn't check for specificity, it is the default if the Job didn't specify anything. The word `specific` threw me off, because what does it mean to be more specific? In this case it meant: The job as a `$tries` set, which means it is more granular and thus more specific. While I read it as, a `$tries` value which is more specific, and thus the least amount of `attempts` . * Update queues.md * Update queues.md * Update queues.md Co-authored-by: Taylor Otwell --- queues.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index 7b250d8c058..eec2edbff66 100644 --- a/queues.md +++ b/queues.md @@ -996,13 +996,13 @@ Alternatively, you may specify the job's connection by calling the `onConnection 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: +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 the number of times it may be attempted: ```shell php artisan queue:work --tries=3 ``` -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). +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). If `--tries=0` is provided to the `queue:work` command, the job will retried indefinitely. 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: From 572c8b5a31d19ff49dfc21e7791c2edda42c68c2 Mon Sep 17 00:00:00 2001 From: Vitor Pena Date: Sat, 24 Dec 2022 09:35:34 -0800 Subject: [PATCH 0562/2609] Update queries.md (#8419) The pointed version for SQLite doesn't exist. I'm assuming the intended version was 3.39.0. According to the SQLite Json functions documentation, it was introduced as an opt-in param in version 3.37 but made as default in version 3.38, so I believe the functionality would also work with that version. Still needs confirmation. --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index d6739a652ba..20ee6812498 100644 --- a/queries.md +++ b/queries.md @@ -485,7 +485,7 @@ The `whereNot` and `orWhereNot` methods may be used to negate a given group of q ### 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: +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.39.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') From a727b4213bbb7bd68be3327d1c7734a2efe75175 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 27 Dec 2022 20:07:11 +0330 Subject: [PATCH 0563/2609] Update validation.md (#8422) --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 44a290ee660..58397d74ba4 100644 --- a/validation.md +++ b/validation.md @@ -1018,9 +1018,9 @@ The field under validation must be a valid, non-relative date according to the ` 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_ +#### date_format:_format_,... -The field under validation must match the given _format_. You should use **either** `date` or `date_format` when validating a field, not both. This validation rule supports all formats supported by PHP's [DateTime](https://www.php.net/manual/en/class.datetime.php) class. +The field under validation must match one of the given _formats_. You should use **either** `date` or `date_format` when validating a field, not both. This validation rule supports all formats supported by PHP's [DateTime](https://www.php.net/manual/en/class.datetime.php) class. #### decimal:_min_,_max_ From 8dca5ee8a404596f77805c16ad2aac4af54907da Mon Sep 17 00:00:00 2001 From: Azer Mamedov Date: Tue, 27 Dec 2022 20:37:28 +0400 Subject: [PATCH 0564/2609] Update cashier-paddle.md (#8421) Paddle update their documentation url --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index af8d5bf757f..c66b1e417ff 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -48,7 +48,7 @@ [Laravel Cashier Paddle](https://github.com/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 review 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). ## Upgrading Cashier From 84257725571f282542b28c507fc2c16110f03c06 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Fri, 30 Dec 2022 02:39:22 +0100 Subject: [PATCH 0565/2609] [9.x] Rewrite docs on failed subscription payments (#8425) * Rewrite docs on failed subscription payments This part was still using the old way of overwriting the webhook while you now have a more easier way to listen to an event. * Update cashier-paddle.md * Update cashier-paddle.md Co-authored-by: Taylor Otwell --- cashier-paddle.md | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index c66b1e417ff..372218529ab 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -1207,28 +1207,49 @@ Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->forma Subscription payments fail for various reasons, such as expired cards or a card having insufficient funds. When this happens, we recommend that you let Paddle handle payment failures for you. Specifically, you may [setup Paddle's automatic billing emails](https://vendors.paddle.com/subscription-settings) in your Paddle dashboard. -Alternatively, you can perform more precise customization by catching the [`subscription_payment_failed`](https://developer.paddle.com/webhook-reference/subscription-alerts/subscription-payment-failed) webhook and enabling the "Subscription Payment Failed" option in the Webhook settings of your Paddle dashboard: +Alternatively, you can perform more precise customization by [listening](/docs/{{version}}/events) for the `subscription_payment_failed` Paddle event via the `WebhookReceived` event dispatched by Cashier. You should also ensure the "Subscription Payment Failed" option is enabled in the Webhook settings of your Paddle dashboard: payload['alert_name'] === 'subscription_payment_failed') { + // Handle the failed subscription payment... + } } } +Once your listener has been defined, you should register it within your application's `EventServiceProvider`: + + [ + PaddleEventListener::class, + ], + ]; + } + ## Testing From 722a4749f8ace1a3067f5686f88a540a7525fc0d Mon Sep 17 00:00:00 2001 From: Iman Date: Sun, 1 Jan 2023 00:12:48 +0330 Subject: [PATCH 0566/2609] [9.x] Passing a subquery to whereIn (#8426) * Subquery for whereIn Documents the possibility of passing a query object to `whereIn` method family as the second parameter. * Update queries.md Co-authored-by: Taylor Otwell --- queries.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/queries.md b/queries.md index 20ee6812498..d7d221c17b4 100644 --- a/queries.md +++ b/queries.md @@ -560,6 +560,24 @@ The `whereNotIn` method verifies that the given column's value is not contained ->whereNotIn('id', [1, 2, 3]) ->get(); +You may also provide a query object as the `whereIn` method's second argument: + + $activeUsers = DB::table('users')->select('id')->where('is_active', 0); + + $users = DB::table('comments') + ->whereIn('user_id', $activeUsers) + ->get(); + +The example above will produce the following SQL: + +```sql +select * from comments where user_id in ( + select id + from users + where is_active = 0 +) +``` + > **Warning** > 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. From b0bed54a61d5f9e88ba79756a2a4907ddfb8e8c1 Mon Sep 17 00:00:00 2001 From: Muhammad Syifa Date: Mon, 2 Jan 2023 21:46:29 +0700 Subject: [PATCH 0567/2609] Fix typo directory name (#8431) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index d2fa4d2f1d1..dec210bb361 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1501,7 +1501,7 @@ If you are listening for many events on a given model, you may use observers to 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: +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: Date: Tue, 3 Jan 2023 09:34:50 +0000 Subject: [PATCH 0568/2609] =?UTF-8?q?[10.x]=20Uses=20PHP=20Native=20Type?= =?UTF-8?q?=20Declarations=20=F0=9F=90=98=20(#8287)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds types to `up` and `down` migration methods * Adds types casts `get` and `set` methods * Adds types to `handle` middleware methods * Adds types to `join` channel method * Adds types to `handle` command method * Adds types to `render` component method * Adds types to `broadcastOn` event method * Adds types to `definition` model factory method * Adds missing types to `render` blade component * Adds missing types to `handle` middleware methods, `handle` listeners methods, and exceptions `render` methods * Adds missing types to `build` mail component * Adds missing types to notification component * Adds types to observers * Adds types to policies * Adds types to providers * Adds types to form requests * Adds types to resources * Adds types to rules * Adds types to scopes * Adds types to seeds * Adds types to tests * Fully types `artisan.md` * Fully types `authentication.md` * Fully types `authorization.md` * Fully types `billing.md` * Fully types `blade.md` * Fully types `broadcasting.md` * Fully types `cache.md` * Fully types `collections.md` * Fully types `container.md` * Fully types `controllers.md` * Fully types `database.md` * Fully types `cashier-paddle.md` * Fully types `container.md` * Fully types `controllers.md` * Fully types `database.md` * Fully types `dusk.md` * Fully types `eloquent-collections.md` * Fully types `eloquent-factories.md` * Fully types `eloquent-mutators.md` * Fully types `eloquent-relationships.md` * Fully types `eloquent-resources.md` * Fully types `eloquent-serialization.md` * Fully types `eloquent.md` * Fully types `encryption.md` * Fully types `errors.md` * Fully types `events.md` * Fully types `facades.md` * Fully types `filesystem.md` * Fully types `fortify.md` * Fully types `frontend.md` * Fully types `hashing.md` * Fully types `helpers.md` * Fully types `horizon.md` * Fully types `http-client.md` * Fully types `locatization.md` * Fully types `logging.md` * Fully types `mail.md` * Fully types `middleware.md` * Fully types `migrations.md` * Fully types `mocking.md` * Fully types `notifications.md` * Fully types `octane.md` * Fully types `pagination.md` * Fully types `passport.md` * Fully types `passport.md` * Fully types `passwords.md` * Fully types `providers.md` * Re-adds missing type * Fully types `queries.md` * Fully types `queues.md` * Fully types `rate-limiting.md` * Fully types `redirects.md` * Fully types `redis.md` * Fully types `requests.md` * Fully types `responses.md` * Fully types `routing.md` * Fully types `scheduling.md` * Fully types `scout.md` * Fully types `session.md` * Fully types `telescope.md` * Fully types `testing.md` * Fully types `urls.md` * Fully types `validation.md` * Fully types `verification.md` * Fully types `views.md` * Fully types `vite.md` * Adds some missing types * Types middlewares `$next` argument * Types missing responses * Uses `void` as command return type * Reverts changes on dots * Fixes signature * Reverts changes on Authenticable * Reverts changes on `UserProvider` * Reverts changes on `UserProvider` * Fixes type * Reverts changes on `resolveChildRouteBinding` * Clarifies returns on components * Reverts order of types * Updates types order * Reworks broadcasting docs * Removes unused import * Uses dots * Applies more types on casts * Fixes `withResponse` type * Improves order of types * Fixes order of imports * More dots * Fixes return type of scope * Improves `report` on exceptions * Improves errors * Fixes style * Adds missing type on Horizon's gate method * Revert changes on `sendPasswordResetNotifications` * Adds missing import * Revert changes on upgrade * More adjustsments * Clarifies that `$notifiable` is an object * Removes non-needed imports * Uses `response()->noContent()` * Improves rules types * Adjusts isolable commands types * Typo on comments * Adds remaining docs types --- artisan.md | 70 ++++------- authentication.md | 62 +++++----- authorization.md | 133 +++++++------------- billing.md | 103 ++++++---------- blade.md | 84 ++++--------- broadcasting.md | 132 ++++++++++---------- cache.md | 29 ++--- cashier-paddle.md | 74 +++++------ collections.md | 164 ++++++++++++------------ console-tests.md | 8 +- container.md | 72 +++++------ contracts.md | 10 +- contributions.md | 2 +- controllers.md | 47 +++---- database-testing.md | 10 +- database.md | 32 ++--- dusk.md | 109 ++++++---------- eloquent-collections.md | 11 +- eloquent-factories.md | 37 +++--- eloquent-mutators.md | 104 +++++----------- eloquent-relationships.md | 158 ++++++++++++++---------- eloquent-resources.md | 108 +++++++--------- eloquent-serialization.md | 7 +- eloquent.md | 165 +++++++++---------------- encryption.md | 10 +- errors.md | 66 +++++----- events.md | 126 +++++++------------ facades.md | 34 ++--- filesystem.md | 25 ++-- fortify.md | 43 +++---- frontend.md | 6 +- hashing.md | 8 +- helpers.md | 83 ++++++++----- horizon.md | 28 ++--- http-client.md | 27 ++-- http-tests.md | 62 ++++------ localization.md | 10 +- logging.md | 29 ++--- mail.md | 109 ++++++---------- middleware.md | 65 +++++----- migrations.md | 24 ++-- mocking.md | 46 +++---- notifications.md | 254 ++++++++++++-------------------------- octane.md | 41 +++--- packages.md | 56 +++------ pagination.md | 17 +-- passport.md | 55 +++------ passwords.md | 15 ++- providers.md | 33 ++--- queries.md | 50 ++++---- queues.md | 216 ++++++++++++-------------------- rate-limiting.md | 5 +- redirects.md | 4 +- redis.md | 26 ++-- releases.md | 4 +- requests.md | 76 ++++++------ responses.md | 10 +- routing.md | 106 ++++++++-------- sanctum.md | 10 +- scheduling.md | 13 +- scout.md | 47 +++---- seeding.md | 16 +-- session.md | 43 +++---- telescope.md | 33 ++--- testing.md | 19 ++- urls.md | 20 ++- validation.md | 137 ++++++++++---------- verification.md | 6 +- views.md | 51 ++++---- vite.md | 16 ++- 70 files changed, 1594 insertions(+), 2317 deletions(-) diff --git a/artisan.md b/artisan.md index 8795ecb8cc9..843bb5898b2 100644 --- a/artisan.md +++ b/artisan.md @@ -147,11 +147,8 @@ Let's take a look at an example command. Note that we are able to request any de /** * Execute the console command. - * - * @param \App\Support\DripEmailer $drip - * @return mixed */ - public function handle(DripEmailer $drip) + public function handle(DripEmailer $drip): void { $drip->send(User::find($this->argument('user'))); } @@ -167,17 +164,15 @@ Closure based commands provide an alternative to defining console commands as cl /** * Register the closure based commands for the application. - * - * @return void */ - protected function commands() + protected function commands(): void { 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 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 command's arguments and options: - Artisan::command('mail:send {user}', function ($user) { + Artisan::command('mail:send {user}', function (string $user) { $this->info("Sending email to: {$user}!"); }); @@ -191,7 +186,7 @@ In addition to receiving your command's arguments and options, command closures use App\Models\User; use App\Support\DripEmailer; - Artisan::command('mail:send {user}', function (DripEmailer $drip, $user) { + Artisan::command('mail:send {user}', function (DripEmailer $drip, string $user) { $drip->send(User::find($user)); }); @@ -200,7 +195,7 @@ In addition to receiving your command's arguments and options, command closures 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('mail:send {user}', function ($user) { + Artisan::command('mail:send {user}', function (string $user) { // ... })->purpose('Send a marketing email to a user'); @@ -242,12 +237,13 @@ php artisan mail:send 1 --isolated=12 By default, isolation locks expire after the command is finished. Or, if the command is interrupted and unable to finish, the lock will expire after one hour. However, you may adjust the lock expiration time by defining a `isolationLockExpiresAt` method on your command: ```php +use DateTimeInterface; +use DateInterval; + /** * Determine when an isolation lock expires for the command. - * - * @return \DateTimeInterface|\DateInterval */ -public function isolationLockExpiresAt() +public function isolationLockExpiresAt(): DateTimeInterface|DateInterval { return now()->addMinutes(5); } @@ -385,14 +381,10 @@ While your command is executing, you will likely need to access the values for t /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): void { $userId = $this->argument('user'); - - // } If you need to retrieve all of the arguments as an `array`, call the `arguments` method: @@ -414,12 +406,12 @@ In addition to displaying output, you may also ask the user to provide input dur /** * Execute the console command. - * - * @return mixed */ - public function handle() + public function handle(): void { $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 passwords: @@ -432,13 +424,13 @@ The `secret` method is similar to `ask`, but the user's input will not be visibl 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)) { - // + // ... } @@ -450,7 +442,7 @@ The `anticipate` method can be used to provide auto-completion for possible choi 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 address?', function ($input) { + $name = $this->anticipate('What is your address?', function (string $input) { // Return auto-completion options... }); @@ -482,10 +474,8 @@ To send output to the console, you may use the `line`, `info`, `comment`, `quest /** * Execute the console command. - * - * @return mixed */ - public function handle() + public function handle(): void { // ... @@ -528,7 +518,7 @@ For long running tasks, it can be helpful to show a progress bar that informs us use App\Models\User; - $users = $this->withProgressBar(User::all(), function ($user) { + $users = $this->withProgressBar(User::all(), function (User $user) { $this->performTask($user); }); @@ -558,10 +548,8 @@ All of your console commands are registered within your application's `App\Conso /** * Register the commands for the application. - * - * @return void */ - protected function commands() + protected function commands(): void { $this->load(__DIR__.'/Commands'); $this->load(__DIR__.'/../Domain/Orders/Commands'); @@ -582,12 +570,12 @@ Sometimes you may wish to execute an Artisan command outside of the CLI. For exa use Illuminate\Support\Facades\Artisan; - Route::post('/user/{user}/mail', function ($user) { + Route::post('/user/{user}/mail', function (string $user) { $exitCode = Artisan::call('mail:send', [ 'user' => $user, '--queue' => 'default' ]); - // + // ... }); Alternatively, you may pass the entire Artisan command to the `call` method as a string: @@ -623,12 +611,12 @@ Using the `queue` method on the `Artisan` facade, you may even queue Artisan com use Illuminate\Support\Facades\Artisan; - Route::post('/user/{user}/mail', function ($user) { + Route::post('/user/{user}/mail', function (string $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: @@ -644,16 +632,14 @@ Sometimes you may wish to call other commands from an existing Artisan command. /** * Execute the console command. - * - * @return mixed */ - public function handle() + public function handle(): void { $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 `callSilently` method. The `callSilently` method has the same signature as the `call` method: @@ -669,10 +655,8 @@ As you may know, operating systems allow signals to be sent to running processes /** * Execute the console command. - * - * @return mixed */ - public function handle() + public function handle(): void { $this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false); @@ -683,7 +667,7 @@ As you may know, operating systems allow signals to be sent to running processes To listen for multiple signals at once, you may provide an array of signals to the `trap` method: - $this->trap([SIGTERM, SIGQUIT], function ($signal) { + $this->trap([SIGTERM, SIGQUIT], function (int $signal) { $this->shouldKeepRunning = false; dump($signal); // SIGTERM / SIGQUIT diff --git a/authentication.md b/authentication.md index dfa26b2440f..fd80f884a9d 100644 --- a/authentication.md +++ b/authentication.md @@ -147,18 +147,18 @@ Alternatively, once a user is authenticated, you may access the authenticated us namespace App\Http\Controllers; use Illuminate\Http\Request; + use Illuminate\Http\Response; class FlightController extends Controller { /** * Update the flight information for an existing flight. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function update(Request $request) + public function update(Request $request): Response { // $request->user() + + return response()->noContent(); } } @@ -190,13 +190,12 @@ To determine if the user making the incoming HTTP request is authenticated, you 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: + use Illuminate\Http\Request; + /** * Get the path the user should be redirected to. - * - * @param \Illuminate\Http\Request $request - * @return string */ - protected function redirectTo($request) + protected function redirectTo(Request $request): string { return route('login'); } @@ -230,17 +229,15 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ namespace App\Http\Controllers; use Illuminate\Http\Request; + use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { /** * Handle an authentication attempt. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function authenticate(Request $request) + public function authenticate(Request $request): RedirectResponse { $credentials = $request->validate([ 'email' => ['required', 'email'], @@ -278,10 +275,12 @@ If you wish, you may also add extra query conditions to the authentication query For complex query conditions, you may provide a closure in your array of credentials. This closure will be invoked with the query instance, allowing you to customize the query based on your application's needs: + use Illuminate\Database\Eloquent\Builder; + if (Auth::attempt([ 'email' => $email, 'password' => $password, - fn ($query) => $query->has('activeSubscription'), + fn (Builder $query) => $query->has('activeSubscription'), ])) { // Authentication was successful... } @@ -294,7 +293,7 @@ The `attemptWhen` method, which receives a closure as its second argument, may b if (Auth::attemptWhen([ 'email' => $email, 'password' => $password, - ], function ($user) { + ], function (User $user) { return $user->isNotBanned(); })) { // Authentication was successful... @@ -369,7 +368,7 @@ You may pass a boolean value as the second argument to the `loginUsingId` method 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)) { - // + // ... } @@ -402,18 +401,19 @@ You may also use HTTP Basic Authentication without setting a user identifier coo namespace App\Http\Middleware; + use Closure; + use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; + use Symfony\Component\HttpFoundation\Response; class AuthenticateOnceWithBasicAuth { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, $next) + public function handle(Request $request, Closure $next): Response { return Auth::onceBasic() ?: $next($request); } @@ -434,15 +434,13 @@ To manually log users out of your application, you may use the `logout` method p 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\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; /** * Log the user out of the application. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function logout(Request $request) + public function logout(Request $request): RedirectResponse { Auth::logout(); @@ -547,6 +545,7 @@ You may define your own authentication guards using the `extend` method on the ` namespace App\Providers; use App\Services\Auth\JwtGuard; + use Illuminate\Contracts\Foundation\Application; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Auth; @@ -554,14 +553,12 @@ You may define your own authentication guards using the `extend` method on the ` { /** * Register any application authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); - Auth::extend('jwt', function ($app, $name, array $config) { + Auth::extend('jwt', function (Application $app, string $name, array $config) { // Return an instance of Illuminate\Contracts\Auth\Guard... return new JwtGuard(Auth::createUserProvider($config['provider'])); @@ -591,10 +588,8 @@ To get started, call the `Auth::viaRequest` method within the `boot` method of y /** * Register any application authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -621,6 +616,7 @@ If you are not using a traditional relational database to store your users, you namespace App\Providers; use App\Extensions\MongoUserProvider; + use Illuminate\Contracts\Foundation\Application; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Auth; @@ -628,14 +624,12 @@ If you are not using a traditional relational database to store your users, you { /** * Register any application authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); - Auth::provider('mongo', function ($app, array $config) { + Auth::provider('mongo', function (Application $app, array $config) { // Return an instance of Illuminate\Contracts\Auth\UserProvider... return new MongoUserProvider($app->make('mongo.connection')); diff --git a/authorization.md b/authorization.md index 3175bcd6bd3..58367dcbb7b 100644 --- a/authorization.md +++ b/authorization.md @@ -51,10 +51,8 @@ In this example, we'll define a gate to determine if a user can update a given ` /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -70,10 +68,8 @@ Like controllers, gates may also be defined using a class callback array: /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -92,24 +88,23 @@ To authorize an action using gates, you should use the `allows` or `denies` meth use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\Request; + use Illuminate\Http\Response; use Illuminate\Support\Facades\Gate; 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) + public function update(Request $request, Post $post): Response { if (! Gate::allows('update-post', $post)) { abort(403); } // Update the post... + + return response()->noContent(); } } @@ -151,7 +146,7 @@ The gate methods for authorizing abilities (`allows`, `denies`, `check`, `any`, use App\Models\User; use Illuminate\Support\Facades\Gate; - Gate::define('create-post', function (User $user, Category $category, $pinned) { + Gate::define('create-post', function (User $user, Category $category, bool $pinned) { if (! $user->canPublishToGroup($category->group)) { return false; } elseif ($pinned && ! $user->canPinPosts()) { @@ -228,9 +223,10 @@ Because hiding resources via a `404` response is such a common pattern for web a 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 App\Models\User; use Illuminate\Support\Facades\Gate; - Gate::before(function ($user, $ability) { + Gate::before(function (User $user, string $ability) { if ($user->isAdministrator()) { return true; } @@ -240,7 +236,9 @@ If the `before` closure returns a non-null result that result will be considered 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) { + use App\Models\User; + + Gate::after(function (User $user, string $ability, bool|null $result, mixed $arguments) { if ($user->isAdministrator()) { return true; } @@ -254,11 +252,12 @@ Similar to the `before` method, if the `after` closure returns a non-null result Occasionally, you may wish to determine if the currently authenticated user is authorized to perform a given action without writing a dedicated gate that corresponds to the action. Laravel allows you to perform these types of "inline" authorization checks via the `Gate::allowIf` and `Gate::denyIf` methods: ```php +use App\Models\User; use Illuminate\Support\Facades\Gate; -Gate::allowIf(fn ($user) => $user->isAdministrator()); +Gate::allowIf(fn (User $user) => $user->isAdministrator()); -Gate::denyIf(fn ($user) => $user->banned()); +Gate::denyIf(fn (User $user) => $user->banned()); ``` If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an `Illuminate\Auth\Access\AuthorizationException` exception. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler. @@ -312,14 +311,12 @@ The `App\Providers\AuthServiceProvider` included with fresh Laravel applications /** * Register any application authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); - // + // ... } } @@ -332,7 +329,7 @@ If you would like to define your own policy discovery logic, you may register a use Illuminate\Support\Facades\Gate; - Gate::guessPolicyNamesUsing(function ($modelClass) { + Gate::guessPolicyNamesUsing(function (string $modelClass) { // Return the name of the policy class for the given model... }); @@ -360,12 +357,8 @@ The `update` method will receive a `User` and a `Post` instance as its arguments { /** * Determine if the given post can be updated by the user. - * - * @param \App\Models\User $user - * @param \App\Models\Post $post - * @return bool */ - public function update(User $user, Post $post) + public function update(User $user, Post $post): bool { return $user->id === $post->user_id; } @@ -389,12 +382,8 @@ So far, we have only examined policy methods that return simple boolean values. /** * Determine if the given post can be updated by the user. - * - * @param \App\Models\User $user - * @param \App\Models\Post $post - * @return \Illuminate\Auth\Access\Response */ - public function update(User $user, Post $post) + public function update(User $user, Post $post): Response { return $user->id === $post->user_id ? Response::allow() @@ -430,12 +419,8 @@ When an action is denied via a policy method, a `403` HTTP response is returned; /** * Determine if the given post can be updated by the user. - * - * @param \App\Models\User $user - * @param \App\Models\Post $post - * @return \Illuminate\Auth\Access\Response */ - public function update(User $user, Post $post) + public function update(User $user, Post $post): Response { return $user->id === $post->user_id ? Response::allow() @@ -450,12 +435,8 @@ Because hiding resources via a `404` response is such a common pattern for web a /** * Determine if the given post can be updated by the user. - * - * @param \App\Models\User $user - * @param \App\Models\Post $post - * @return \Illuminate\Auth\Access\Response */ - public function update(User $user, Post $post) + public function update(User $user, Post $post): Response { return $user->id === $post->user_id ? Response::allow() @@ -469,11 +450,8 @@ Some policy methods only receive an instance of the currently authenticated user /** * Determine if the given user can create posts. - * - * @param \App\Models\User $user - * @return bool */ - public function create(User $user) + public function create(User $user): bool { return $user->role == 'writer'; } @@ -494,12 +472,8 @@ By default, all gates and policies automatically return `false` if the incoming { /** * Determine if the given post can be updated by the user. - * - * @param \App\Models\User $user - * @param \App\Models\Post $post - * @return bool */ - public function update(?User $user, Post $post) + public function update(?User $user, Post $post): bool { return optional($user)->id === $post->user_id; } @@ -514,16 +488,14 @@ For certain users, you may wish to authorize all actions within a given policy. /** * Perform pre-authorization checks. - * - * @param \App\Models\User $user - * @param string $ability - * @return void|bool */ - public function before(User $user, $ability) + public function before(User $user, string $ability): bool|null { if ($user->isAdministrator()) { return true; } + + return null; } 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. @@ -546,23 +518,22 @@ The `App\Models\User` model that is included with your Laravel application inclu use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\Request; + use Illuminate\Http\Response; 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) + public function update(Request $request, Post $post): Response { if ($request->user()->cannot('update', $post)) { abort(403); } // Update the post... + + return response()->noContent(); } } @@ -580,22 +551,22 @@ Remember, some actions may correspond to policy methods like `create` that do no use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\Request; + use Illuminate\Http\Response; class PostController extends Controller { /** * Create a post. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { if ($request->user()->cannot('create', Post::class)) { abort(403); } // Create the post... + + return response()->noContent(); } } @@ -613,23 +584,22 @@ Like the `can` method, this method accepts the name of the action you wish to au use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\Request; + use Illuminate\Http\Response; class PostController extends Controller { /** * Update the given blog post. * - * @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) + public function update(Request $request, Post $post): Response { $this->authorize('update', $post); // The current user can update the blog post... + + return response()->noContent(); } } @@ -640,20 +610,20 @@ As previously discussed, some policy methods like `create` do not require a mode use App\Models\Post; use Illuminate\Http\Request; + use Illuminate\Http\Response; /** * Create a new blog post. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - * * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function create(Request $request) + public function create(Request $request): Response { $this->authorize('create', Post::class); // The current user can create blog posts... + + return response()->noContent(); } @@ -675,8 +645,6 @@ The `authorizeResource` method accepts the model's class name as its first argum { /** * Create the controller instance. - * - * @return void */ public function __construct() { @@ -802,13 +770,8 @@ When authorizing actions using policies, you may pass an array as the second arg /** * Determine if the given post can be updated by the user. - * - * @param \App\Models\User $user - * @param \App\Models\Post $post - * @param int $category - * @return bool */ - public function update(User $user, Post $post, int $category) + public function update(User $user, Post $post, int $category): bool { return $user->id === $post->user_id && $user->canUpdateCategory($category); @@ -819,15 +782,13 @@ When attempting to determine if the authenticated user can update a given post, /** * Update the given blog post. * - * @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) + public function update(Request $request, Post $post): Response { $this->authorize('update', [$post, $request->category]); // The current user can update the blog post... + + return response()->noContent(); } diff --git a/billing.md b/billing.md index 06cd49ec7dc..62519b8e934 100644 --- a/billing.md +++ b/billing.md @@ -113,10 +113,8 @@ If you would like to prevent Cashier's migrations from running entirely, you may /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { Cashier::ignoreMigrations(); } @@ -146,10 +144,8 @@ Cashier assumes your billable model will be the `App\Models\User` class that shi /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Cashier::useCustomerModel(User::class); } @@ -198,10 +194,8 @@ Thanks to [Stripe Tax](https://stripe.com/tax), it's possible to automatically c /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Cashier::calculateTaxes(); } @@ -243,10 +237,8 @@ After defining your model, you may instruct Cashier to use your custom model via /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Cashier::useSubscriptionModel(Subscription::class); Cashier::useSubscriptionItemModel(SubscriptionItem::class); @@ -346,16 +338,15 @@ Typically, when your application's users update their name, email address, or ot To automate this, you may define an event listener on your billable model that reacts to the model's `updated` event. Then, within your event listener, you may invoke the `syncStripeCustomerDetails` method on the model: + use App\Models\User; use function Illuminate\Events\queueable; /** * The "booted" method of the model. - * - * @return void */ - protected static function booted() + protected static function booted(): void { - static::updated(queueable(function ($customer) { + static::updated(queueable(function (User $customer) { if ($customer->hasStripeId()) { $customer->syncStripeCustomerDetails(); } @@ -368,10 +359,8 @@ You may customize the columns used for syncing customer information to Stripe by /** * Get the customer name that should be synced to Stripe. - * - * @return string|null */ - public function stripeName() + public function stripeName(): string|null { return $this->company_name; } @@ -555,19 +544,19 @@ You can retrieve a specific payment method that is attached to the billable mode To determine if a billable model has a default payment method attached to their account, invoke the `hasDefaultPaymentMethod` method: if ($user->hasDefaultPaymentMethod()) { - // + // ... } 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()) { - // + // ... } This method will determine if the billable model has payment methods of the `card` type. To determine if a payment method of another type exists for the model, you may pass the `type` as an argument to the method: if ($user->hasPaymentMethod('sepa_debit')) { - // + // ... } @@ -759,7 +748,7 @@ Finally, you should always make sure to only add one active subscription per typ 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')) { - // + // ... } 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: @@ -769,17 +758,17 @@ The `subscribed` method also makes a great candidate for a [route middleware](/d namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; class EnsureUserIsSubscribed { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { if ($request->user() && ! $request->user()->subscribed('default')) { // This user is not a paying customer... @@ -793,31 +782,31 @@ The `subscribed` method also makes a great candidate for a [route middleware](/d 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 `subscribedToProduct` method may be used to determine if the user is subscribed to a given product based on a given Stripe product's identifier. In Stripe, products are collections of prices. In this example, we will determine if the user's `default` subscription is actively subscribed to the application's "premium" product. The given Stripe product identifier should correspond to one of your product's identifiers in the Stripe dashboard: if ($user->subscribedToProduct('prod_premium', 'default')) { - // + // ... } By passing an array to the `subscribedToProduct` method, you may determine if the user's `default` subscription is actively subscribed to the application's "basic" or "premium" product: if ($user->subscribedToProduct(['prod_basic', 'prod_premium'], 'default')) { - // + // ... } The `subscribedToPrice` method may be used to determine if a customer's subscription corresponds to a given price ID: if ($user->subscribedToPrice('price_basic_monthly', 'default')) { - // + // ... } The `recurring` method may be used to determine if the user is currently subscribed and is no longer within their trial period: if ($user->subscription('default')->recurring()) { - // + // ... } > **Warning** @@ -829,19 +818,19 @@ The `recurring` method may be used to determine if the user is currently subscri To determine if the user was once an active subscriber but has canceled their subscription, you may use the `canceled` method: if ($user->subscription('default')->canceled()) { - // + // ... } You may also determine if a user has canceled 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()) { - // + // ... } To determine if the user has canceled their subscription and is no longer within their "grace period", you may use the `ended` method: if ($user->subscription('default')->ended()) { - // + // ... } @@ -852,11 +841,11 @@ If a subscription requires a secondary payment action after creation the subscri Similarly, if a secondary payment action is required when swapping prices 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')) { - // + // ... } if ($user->subscription('default')->hasIncompletePayment()) { - // + // ... } 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: @@ -873,10 +862,8 @@ If you would like the subscription to still be considered active when it's in a /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { Cashier::keepPastDueSubscriptionsActive(); Cashier::keepIncompleteSubscriptionsActive(); @@ -1197,9 +1184,9 @@ To specify the tax rates a user pays on a subscription, you should implement the /** * The tax rates that should apply to the customer's subscriptions. * - * @return array + * @return array */ - public function taxRates() + public function taxRates(): array { return ['txr_id']; } @@ -1211,9 +1198,9 @@ If you're offering subscriptions with multiple products, you may define differen /** * The tax rates that should apply to the customer's subscriptions. * - * @return array + * @return array> */ - public function priceTaxRates() + public function priceTaxRates(): array { return [ 'price_monthly' => ['txr_id'], @@ -1281,7 +1268,7 @@ For example, if a customer cancels a subscription on March 1st, but the subscrip You may determine if a user has canceled their subscription but are still on their "grace period" using the `onGracePeriod` method: if ($user->subscription('default')->onGracePeriod()) { - // + // ... } If you wish to cancel a subscription immediately, call the `cancelNow` method on the user's subscription: @@ -1341,11 +1328,11 @@ The `trialUntil` method allows you to provide a `DateTime` instance that specifi 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')) { - // + // ... } if ($user->subscription('default')->onTrial()) { - // + // ... } You may use the `endTrial` method to immediately end a subscription trial: @@ -1355,11 +1342,11 @@ You may use the `endTrial` method to immediately end a subscription trial: To determine if an existing trial has expired, you may use the `hasExpiredTrial` methods: if ($user->hasExpiredTrial('default')) { - // + // ... } if ($user->subscription('default')->hasExpiredTrial()) { - // + // ... } @@ -1500,11 +1487,8 @@ Both events contain the full payload of the Stripe webhook. For example, if you { /** * Handle received Stripe webhooks. - * - * @param \Laravel\Cashier\Events\WebhookReceived $event - * @return void */ - public function handle(WebhookReceived $event) + public function handle(WebhookReceived $event): void { if ($event->payload['type'] === 'invoice.payment_succeeded') { // Handle the incoming event... @@ -1573,7 +1557,7 @@ The `charge` method will throw an exception if the charge fails. If the charge i try { $payment = $user->charge(100, $paymentMethod); } catch (Exception $e) { - // + // ... } > **Warning** @@ -1720,7 +1704,7 @@ From within a route or controller, you may use the `downloadInvoice` method to g use Illuminate\Http\Request; - Route::get('/user/invoice/{invoice}', function (Request $request, $invoiceId) { + Route::get('/user/invoice/{invoice}', function (Request $request, string $invoiceId) { return $request->user()->downloadInvoice($invoiceId); }); @@ -1754,11 +1738,6 @@ Cashier also makes it possible to use a custom invoice renderer. By default, Cas { /** * Render the given invoice and return the raw PDF bytes. - * - * @param \Laravel\Cashier\Invoice. $invoice - * @param array $data - * @param array $options - * @return string */ public function render(Invoice $invoice, array $data = [], array $options = []): string { @@ -1996,11 +1975,11 @@ Payment exceptions may be thrown for the following methods: `charge`, `invoiceFo 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()) { - // + // ... } You can derive the specific status of an incomplete payment by inspecting the `payment` property on the exception instance: diff --git a/blade.md b/blade.md index 62bf8956887..2368e328cbb 100644 --- a/blade.md +++ b/blade.md @@ -101,10 +101,8 @@ By default, Blade (and the Laravel `e` helper) will double encode HTML entities. { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::withoutDoubleEncoding(); } @@ -653,7 +651,7 @@ However, if you are building a package that utilizes Blade components, you will /** * Bootstrap your package's services. */ - public function boot() + public function boot(): void { Blade::component('package-alert', Alert::class); } @@ -670,10 +668,8 @@ Alternatively, you may use the `componentNamespace` method to autoload component /** * Bootstrap your package's services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); } @@ -720,6 +716,7 @@ You should define all of the component's data attributes in its class constructo namespace App\View\Components; use Illuminate\View\Component; + use Illuminate\View\View; class Alert extends Component { @@ -739,12 +736,8 @@ You should define all of the component's data attributes in its class constructo /** * Create the component instance. - * - * @param string $type - * @param string $message - * @return void */ - public function __construct($type, $message) + public function __construct(string $type, string $message) { $this->type = $type; $this->message = $message; @@ -752,10 +745,8 @@ You should define all of the component's data attributes in its class constructo /** * Get the view / contents that represent the component. - * - * @return \Illuminate\View\View|\Closure|string */ - public function render() + public function render(): View { return view('components.alert'); } @@ -776,11 +767,8 @@ Component constructor arguments should be specified using `camelCase`, while `ke /** * Create the component instance. - * - * @param string $alertType - * @return void */ - public function __construct($alertType) + public function __construct(string $alertType) { $this->alertType = $alertType; } @@ -830,11 +818,8 @@ In addition to public variables being available to your component template, any /** * Determine if the given option is the currently selected option. - * - * @param string $option - * @return bool */ - public function isSelected($option) + public function isSelected(string $option): bool { return $option === $this->selected; } @@ -852,12 +837,12 @@ You may execute this method from your component template by invoking the variabl 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: + use Closure; + /** * Get the view / contents that represent the component. - * - * @return \Illuminate\View\View|\Closure|string */ - public function render() + public function render(): Closure { return function (array $data) { // $data['componentName']; @@ -882,13 +867,8 @@ use App\Services\AlertCreator; /** * Create the component instance. - * - * @param \App\Services\AlertCreator $creator - * @param string $type - * @param string $message - * @return void */ -public function __construct(AlertCreator $creator, $type, $message) +public function __construct(AlertCreator $creator, string $type, string $message) { $this->creator = $creator; $this->type = $type; @@ -1032,7 +1012,7 @@ If you would like an attribute other than `class` to have its default value and 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: ```blade -{{ $attributes->filter(fn ($value, $key) => $key == 'foo') }} +{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }} ``` For convenience, you may use the `whereStartsWith` method to retrieve all attributes whose keys begin with a given string: @@ -1191,10 +1171,8 @@ For very small components, it may feel cumbersome to manage both the component c /** * Get the view / contents that represent the component. - * - * @return \Illuminate\View\View|\Closure|string */ - public function render() + public function render(): string { return <<<'blade'
    @@ -1236,10 +1214,8 @@ However, if you are building a package that utilizes Blade components or placing /** * Bootstrap your package's services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::component('package-alert', AlertComponent::class); } @@ -1258,10 +1234,8 @@ Alternatively, you may use the `componentNamespace` method to autoload component /** * Bootstrap your package's services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); } @@ -1390,10 +1364,8 @@ The `anonymousComponentPath` method accepts the "path" to the anonymous componen /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::anonymousComponentPath(__DIR__.'/../components'); } @@ -1740,22 +1712,18 @@ The following example creates a `@datetime($var)` directive which formats a give { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - Blade::directive('datetime', function ($expression) { + Blade::directive('datetime', function (string $expression) { return "format('m/d/Y H:i'); ?>"; }); } @@ -1780,10 +1748,8 @@ In these cases, Blade allows you to register a custom echo handler for that part /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::stringable(function (Money $money) { return $money->formatTo('en_GB'); @@ -1805,12 +1771,10 @@ Programming a custom directive is sometimes more complex than necessary when def /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - Blade::if('disk', function ($value) { + Blade::if('disk', function (string $value) { return config('filesystems.default') === $value; }); } diff --git a/broadcasting.md b/broadcasting.md index 7a5c7d5292a..84a44c1a9c3 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -281,7 +281,6 @@ When a user is viewing one of their orders, we don't want them to have to refres use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; - use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Queue\SerializesModels; @@ -297,14 +296,32 @@ When a user is viewing one of their orders, we don't want them to have to refres The `ShouldBroadcast` interface requires our event to define a `broadcastOn` method. This method is responsible for returning the channels that the event should broadcast on. An empty stub of this method is already defined on generated event classes, so we only need to fill in its details. We only want the creator of the order to be able to view status updates, so we will broadcast the event on a private channel that is tied to the order: + use Illuminate\Broadcasting\Channel; + use Illuminate\Broadcasting\PrivateChannel; + + /** + * Get the channel the event should broadcast on. + */ + public function broadcastOn(): Channel + { + return new PrivateChannel('orders.'.$this->order->id); + } + +If you wish the event to broadcast on multiple channels, you may return an `array` instead: + + use Illuminate\Broadcasting\PrivateChannel; + /** * Get the channels the event should broadcast on. * - * @return \Illuminate\Broadcasting\PrivateChannel + * @return array */ - public function broadcastOn() + public function broadcastOn(): array { - return new PrivateChannel('orders.'.$this->order->id); + return [ + new PrivateChannel('orders.'.$this->order->id), + // ... + ]; } @@ -313,8 +330,9 @@ The `ShouldBroadcast` interface requires our event to define a `broadcastOn` met 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 `orders.1` channel is actually the creator of the order: use App\Models\Order; + use App\Models\User; - Broadcast::channel('orders.{orderId}', function ($user, $orderId) { + Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); @@ -366,9 +384,6 @@ The `ShouldBroadcast` interface requires you to implement a single method: `broa /** * Create a new event instance. - * - * @param \App\Models\User $user - * @return void */ public function __construct(User $user) { @@ -378,11 +393,13 @@ The `ShouldBroadcast` interface requires you to implement a single method: `broa /** * Get the channels the event should broadcast on. * - * @return Channel|array + * @return array */ - public function broadcastOn() + public function broadcastOn(): array { - return new PrivateChannel('user.'.$this->user->id); + return [ + new PrivateChannel('user.'.$this->user->id), + ]; } } @@ -395,10 +412,8 @@ By default, Laravel will broadcast the event using the event's class name. Howev /** * The event's broadcast name. - * - * @return string */ - public function broadcastAs() + public function broadcastAs(): string { return 'server.created'; } @@ -429,9 +444,9 @@ However, if you wish to have more fine-grained control over your broadcast paylo /** * Get the data to broadcast. * - * @return array + * @return array */ - public function broadcastWith() + public function broadcastWith(): array { return ['id' => $this->user->id]; } @@ -459,10 +474,8 @@ Alternatively, you may customize the queue name by defining a `broadcastQueue` m /** * The name of the queue on which to place the broadcasting job. - * - * @return string */ - public function broadcastQueue() + public function broadcastQueue(): string { return 'default'; } @@ -475,7 +488,7 @@ If you would like to broadcast your event using the `sync` queue instead of the class OrderShipmentStatusUpdated implements ShouldBroadcastNow { - // + // ... } @@ -485,10 +498,8 @@ Sometimes you want to broadcast your event only if a given condition is true. Yo /** * Determine if this event should broadcast. - * - * @return bool */ - public function broadcastWhen() + public function broadcastWhen(): bool { return $this->order->value > 100; } @@ -578,7 +589,9 @@ window.Echo = new Echo({ 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('orders.{orderId}', function ($user, $orderId) { + use App\Models\User; + + Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); @@ -592,8 +605,9 @@ All authorization callbacks receive the currently authenticated user as their fi 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; + use App\Models\User; - Broadcast::channel('orders.{order}', function ($user, Order $order) { + Broadcast::channel('orders.{order}', function (User $user, Order $order) { return $user->id === $order->user_id; }); @@ -637,22 +651,16 @@ Finally, you may place the authorization logic for your channel in the channel c { /** * Create a new channel instance. - * - * @return void */ public function __construct() { - // + // ... } /** * Authenticate the user's access to the channel. - * - * @param \App\Models\User $user - * @param \App\Models\Order $order - * @return array|bool */ - public function join(User $user, Order $order) + public function join(User $user, Order $order): array|bool { return $user->id === $order->user_id; } @@ -733,8 +741,6 @@ Alternatively, you may specify the event's broadcast connection by calling the ` /** * Create a new event instance. - * - * @return void */ public function __construct() { @@ -808,7 +814,7 @@ Alternatively, you may prefix event classes with a `.` when subscribing to them ```js Echo.channel('orders') .listen('.Namespace\\Event\\Class', (e) => { - // + // ... }); ``` @@ -824,7 +830,9 @@ All presence channels are also private channels; therefore, users must be [autho The data returned by the authorization callback will be made available to the presence channel event listeners in your JavaScript application. If the user is not authorized to join the presence channel, you should return `false` or `null`: - Broadcast::channel('chat.{roomId}', function ($user, $roomId) { + use App\Models\User; + + Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) { if ($user->canJoinRoom($roomId)) { return ['id' => $user->id, 'name' => $user->name]; } @@ -838,7 +846,7 @@ To join a presence channel, you may use Echo's `join` method. The `join` method ```js Echo.join(`chat.${roomId}`) .here((users) => { - // + // ... }) .joining((user) => { console.log(user.name); @@ -861,11 +869,13 @@ Presence channels may receive events just like public or private channels. Using /** * Get the channels the event should broadcast on. * - * @return Channel|array + * @return array */ - public function broadcastOn() + public function broadcastOn(): array { - return new PresenceChannel('room.'.$this->message->room_id); + return [ + new PresenceChannel('room.'.$this->message->room_id), + ]; } As with other events, you may use the `broadcast` helper and the `toOthers` method to exclude the current user from receiving the broadcast: @@ -882,7 +892,7 @@ Echo.join(`chat.${roomId}`) .joining(/* ... */) .leaving(/* ... */) .listen('NewMessage', (e) => { - // + // ... }); ``` @@ -903,10 +913,12 @@ To get started, your Eloquent model should use the `Illuminate\Database\Eloquent namespace App\Models; +use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Database\Eloquent\BroadcastsEvents; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class Post extends Model { @@ -915,7 +927,7 @@ class Post extends Model /** * Get the user that the post belongs to. */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class); } @@ -923,10 +935,9 @@ class Post extends Model /** * Get the channels that model events should broadcast on. * - * @param string $event - * @return \Illuminate\Broadcasting\Channel|array + * @return array */ - public function broadcastOn($event) + public function broadcastOn(string $event): array { return [$this, $this->user]; } @@ -941,10 +952,9 @@ In addition, you may have noticed that the `broadcastOn` method receives a strin /** * Get the channels that model events should broadcast on. * - * @param string $event - * @return \Illuminate\Broadcasting\Channel|array + * @return array> */ -public function broadcastOn($event) +public function broadcastOn(string $event): array { return match ($event) { 'deleted' => [], @@ -963,11 +973,8 @@ use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred; /** * Create a new broadcastable model event for the model. - * - * @param string $event - * @return \Illuminate\Database\Eloquent\BroadcastableModelEventOccurred */ -protected function newBroadcastableEvent($event) +protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred { return (new BroadcastableModelEventOccurred( $this, $event @@ -991,12 +998,13 @@ use Illuminate\Broadcasting\PrivateChannel; /** * Get the channels that model events should broadcast on. * - * @param string $event - * @return \Illuminate\Broadcasting\Channel|array + * @return array */ -public function broadcastOn($event) +public function broadcastOn(string $event): array { - return [new PrivateChannel('user.'.$this->id)]; + return [ + new PrivateChannel('user.'.$this->id) + ]; } ``` @@ -1038,11 +1046,8 @@ If you would like, you may define a custom broadcast name and payload by adding ```php /** * The model event's broadcast name. - * - * @param string $event - * @return string|null */ -public function broadcastAs($event) +public function broadcastAs(string $event): string|null { return match ($event) { 'created' => 'post.created', @@ -1053,10 +1058,9 @@ public function broadcastAs($event) /** * Get the data to broadcast for the model. * - * @param string $event - * @return array + * @return array */ -public function broadcastWith($event) +public function broadcastWith(string $event): array { return match ($event) { 'created' => ['title' => $this->title], diff --git a/cache.md b/cache.md index 6812e22aa6a..2654f42c92d 100644 --- a/cache.md +++ b/cache.md @@ -44,7 +44,7 @@ The cache configuration file also contains various other options, which are docu When using the `database` cache driver, you will need to set up a table to contain the cache items. You'll find an example `Schema` declaration for the table below: - Schema::create('cache', function ($table) { + Schema::create('cache', function (Blueprint $table) { $table->string('key')->unique(); $table->text('value'); $table->integer('expiration'); @@ -110,14 +110,14 @@ To obtain a cache store instance, you may use the `Cache` facade, which is what { /** * Show a list of all users of the application. - * - * @return Response */ - public function index() + public function index(): array { $value = Cache::get('key'); - // + return [ + // ... + ]; } } @@ -151,7 +151,7 @@ You may even pass a closure as the default value. The result of the closure will 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')) { - // + // ... } @@ -311,7 +311,7 @@ In contrast, this statement would remove only cached values tagged with `authors 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) { + Schema::create('cache_locks', function (Blueprint $table) { $table->string('key')->primary(); $table->string('owner'); $table->integer('expiration'); @@ -413,7 +413,7 @@ To create our custom cache driver, we first need to implement the `Illuminate\Co We just need to implement each of these methods using a MongoDB connection. For an example of how to implement each of these methods, take a look at the `Illuminate\Cache\MemcachedStore` in the [Laravel framework source code](https://github.com/laravel/framework). Once our implementation is complete, we can finish our custom driver registration by calling the `Cache` facade's `extend` method: - Cache::extend('mongo', function ($app) { + Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore); }); @@ -430,6 +430,7 @@ To register the custom cache driver with Laravel, we will use the `extend` metho namespace App\Providers; use App\Extensions\MongoStore; + use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\Cache; use Illuminate\Support\ServiceProvider; @@ -437,13 +438,11 @@ To register the custom cache driver with Laravel, we will use the `extend` metho { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { $this->app->booting(function () { - Cache::extend('mongo', function ($app) { + Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore); }); }); @@ -451,12 +450,10 @@ To register the custom cache driver with Laravel, we will use the `extend` metho /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - // + // ... } } diff --git a/cashier-paddle.md b/cashier-paddle.md index af8d5bf757f..dcfb3926660 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -101,10 +101,8 @@ If you would like to prevent Cashier's migrations from running entirely, you may /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { Cashier::ignoreMigrations(); } @@ -198,10 +196,8 @@ After defining your model, you may instruct Cashier to use your custom model via /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Cashier::useReceiptModel(Receipt::class); Cashier::useSubscriptionModel(Subscription::class); @@ -448,10 +444,8 @@ Cashier allows you to define some useful defaults for your customers when creati /** * Get the customer's email address to associate with Paddle. - * - * @return string|null */ - public function paddleEmail() + public function paddleEmail(): string|null { return $this->email; } @@ -461,12 +455,11 @@ Cashier allows you to define some useful defaults for your customers when creati * * This needs to be a 2 letter code. See the link below for supported countries. * - * @return string|null * @link https://developer.paddle.com/reference/platform-parameters/supported-countries */ - public function paddleCountry() + public function paddleCountry(): string|null { - // + // ... } /** @@ -474,12 +467,11 @@ Cashier allows you to define some useful defaults for your customers when creati * * See the link below for countries which require this. * - * @return string|null * @link https://developer.paddle.com/reference/platform-parameters/supported-countries#countries-requiring-postcode */ - public function paddlePostcode() + public function paddlePostcode(): string|null { - // + // ... } These defaults will be used for every action in Cashier that generates a [pay link](#pay-links). @@ -554,7 +546,7 @@ You can also pass an array of metadata using the `withMetadata` method: Once a user is subscribed to your application, you may 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: if ($user->subscribed('default')) { - // + // ... } 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: @@ -564,17 +556,17 @@ The `subscribed` method also makes a great candidate for a [route middleware](/d namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; class EnsureUserIsSubscribed { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { if ($request->user() && ! $request->user()->subscribed('default')) { // This user is not a paying customer... @@ -588,25 +580,25 @@ The `subscribed` method also makes a great candidate for a [route middleware](/d 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 Paddle plan ID. In this example, we will determine if the user's `default` subscription is actively subscribed to the monthly plan: if ($user->subscribedToPlan($monthly = 12345, '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: if ($user->subscribedToPlan([$monthly = 12345, $yearly = 54321], 'default')) { - // + // ... } The `recurring` method may be used to determine if the user is currently subscribed and is no longer within their trial period: if ($user->subscription('default')->recurring()) { - // + // ... } @@ -615,19 +607,19 @@ The `recurring` method may be used to determine if the user is currently subscri 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: if ($user->subscription('default')->onGracePeriod()) { - // + // ... } To determine if the user has cancelled their subscription and is no longer within their "grace period", you may use the `ended` method: if ($user->subscription('default')->ended()) { - // + // ... } @@ -636,7 +628,7 @@ To determine if the user has cancelled their subscription and is no longer withi 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). @@ -647,10 +639,8 @@ If you would like subscriptions to still be considered active when they are `pas /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { Cashier::keepPastDueSubscriptionsActive(); } @@ -823,7 +813,7 @@ When a subscription is paused, Cashier will automatically set the `paused_from` You may determine if a user has paused their subscription but are still on their "grace period" using the `onPausedGracePeriod` method: if ($user->subscription('default')->onPausedGracePeriod()) { - // + // ... } To resume a paused a subscription, you may call the `unpause` method on the user's subscription: @@ -845,7 +835,7 @@ When a subscription is cancelled, Cashier will automatically set the `ends_at` c You may determine if a user has cancelled their subscription but are still on their "grace period" using the `onGracePeriod` method: if ($user->subscription('default')->onGracePeriod()) { - // + // ... } If you wish to cancel a subscription immediately, you may call the `cancelNow` method on the user's subscription: @@ -885,21 +875,21 @@ This method will set the trial period ending date on the subscription record wit 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')) { - // + // ... } if ($user->subscription('default')->onTrial()) { - // + // ... } To determine if an existing trial has expired, you may use the `hasExpiredTrial` methods: if ($user->hasExpiredTrial('default')) { - // + // ... } if ($user->subscription('default')->hasExpiredTrial()) { - // + // ... } @@ -1007,11 +997,8 @@ Both events contain the full payload of the Paddle webhook. For example, if you { /** * Handle received Paddle webhooks. - * - * @param \Laravel\Paddle\Events\WebhookReceived $event - * @return void */ - public function handle(WebhookReceived $event) + public function handle(WebhookReceived $event): void { if ($event->payload['alert_name'] === 'payment_succeeded') { // Handle the incoming event... @@ -1219,11 +1206,8 @@ Alternatively, you can perform more precise customization by catching the [`subs { /** * Handle subscription payment failed. - * - * @param array $payload - * @return void */ - public function handleSubscriptionPaymentFailed($payload) + public function handleSubscriptionPaymentFailed(array $payload): void { // Handle the failed subscription payment... } diff --git a/collections.md b/collections.md index 10758e24ffe..b74a6c727c6 100644 --- a/collections.md +++ b/collections.md @@ -16,9 +16,9 @@ The `Illuminate\Support\Collection` class provides a fluent, convenient wrapper for working with arrays of data. For example, check out the following code. We'll use the `collect` helper to create a new collection instance from the array, run the `strtoupper` function on each element, and then remove all empty elements: - $collection = collect(['taylor', 'abigail', null])->map(function ($name) { + $collection = collect(['taylor', 'abigail', null])->map(function (string $name) { return strtoupper($name); - })->reject(function ($name) { + })->reject(function (string $name) { return empty($name); }); @@ -43,7 +43,7 @@ Collections are "macroable", which allows you to add additional methods to the ` use Illuminate\Support\Str; Collection::macro('toUpper', function () { - return $this->map(function ($value) { + return $this->map(function (string $value) { return Str::upper($value); }); }); @@ -64,8 +64,8 @@ If necessary, you may define macros that accept additional arguments: use Illuminate\Support\Collection; use Illuminate\Support\Facades\Lang; - Collection::macro('toLocale', function ($locale) { - return $this->map(function ($value) use ($locale) { + Collection::macro('toLocale', function (string $locale) { + return $this->map(function (string $value) use ($locale) { return Lang::get($value, [], $locale); }); }); @@ -313,7 +313,7 @@ The `chunkWhile` method breaks the collection into multiple, smaller collections $collection = collect(str_split('AABBCCCD')); - $chunks = $collection->chunkWhile(function ($value, $key, $chunk) { + $chunks = $collection->chunkWhile(function (string $value, int $key, Collection $chunk) { return $value === $chunk->last(); }); @@ -407,7 +407,7 @@ The `contains` method determines whether the collection contains a given item. Y $collection = collect([1, 2, 3, 4, 5]); - $collection->contains(function ($value, $key) { + $collection->contains(function (int $value, int $key) { return $value > 5; }); @@ -493,7 +493,7 @@ You pass a closure to the `countBy` method to count all items by a custom value: $collection = collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']); - $counted = $collection->countBy(function ($email) { + $counted = $collection->countBy(function (string $email) { return substr(strrchr($email, "@"), 1); }); @@ -629,7 +629,7 @@ The `doesntContain` method determines whether the collection does not contain a $collection = collect([1, 2, 3, 4, 5]); - $collection->doesntContain(function ($value, $key) { + $collection->doesntContain(function (int $value, int $key) { return $value < 5; }); @@ -713,13 +713,15 @@ This method has the same signature as the [`duplicates`](#method-duplicates) met The `each` method iterates over the items in the collection and passes each item to a closure: - $collection->each(function ($item, $key) { - // + $collection = collect([1, 2, 3, 4]); + + $collection->each(function (int $item, int $key) { + // ... }); If you would like to stop iterating through the items, you may return `false` from your closure: - $collection->each(function ($item, $key) { + $collection->each(function (int $item, int $key) { if (/* condition */) { return false; } @@ -732,13 +734,13 @@ The `eachSpread` method iterates over the collection's items, passing each neste $collection = collect([['John Doe', 35], ['Jane Doe', 33]]); - $collection->eachSpread(function ($name, $age) { - // + $collection->eachSpread(function (string $name, int $age) { + // ... }); You may stop iterating through the items by returning `false` from the callback: - $collection->eachSpread(function ($name, $age) { + $collection->eachSpread(function (string $name, int $age) { return false; }); @@ -747,7 +749,7 @@ You may stop iterating through the items by returning `false` from the callback: The `every` method may be used to verify that all elements of a collection pass a given truth test: - collect([1, 2, 3, 4])->every(function ($value, $key) { + collect([1, 2, 3, 4])->every(function (int $value, int $key) { return $value > 2; }); @@ -757,7 +759,7 @@ If the collection is empty, the `every` method will return true: $collection = collect([]); - $collection->every(function ($value, $key) { + $collection->every(function (int $value, int $key) { return $value > 2; }); @@ -788,7 +790,7 @@ The `filter` method filters the collection using the given callback, keeping onl $collection = collect([1, 2, 3, 4]); - $filtered = $collection->filter(function ($value, $key) { + $filtered = $collection->filter(function (int $value, int $key) { return $value > 2; }); @@ -811,7 +813,7 @@ For the inverse of `filter`, see the [reject](#method-reject) method. The `first` method returns the first element in the collection that passes a given truth test: - collect([1, 2, 3, 4])->first(function ($value, $key) { + collect([1, 2, 3, 4])->first(function (int $value, int $key) { return $value > 2; }); @@ -828,7 +830,7 @@ You may also call the `first` method with no arguments to get the first element The `firstOrFail` method is identical to the `first` method; however, if no result is found, an `Illuminate\Support\ItemNotFoundException` exception will be thrown: - collect([1, 2, 3, 4])->firstOrFail(function ($value, $key) { + collect([1, 2, 3, 4])->firstOrFail(function (int $value, int $key) { return $value > 5; }); @@ -879,7 +881,7 @@ The `flatMap` method iterates through the collection and passes each value to th ['age' => 28] ]); - $flattened = $collection->flatMap(function ($values) { + $flattened = $collection->flatMap(function (array $values) { return array_map('strtoupper', $values); }); @@ -1033,7 +1035,7 @@ The `groupBy` method groups the collection's items by a given key: Instead of passing a string `key`, you may pass a callback. The callback should return the value you wish to key the group by: - $grouped = $collection->groupBy(function ($item, $key) { + $grouped = $collection->groupBy(function (array $item, int $key) { return substr($item['account_id'], -3); }); @@ -1060,7 +1062,7 @@ Multiple grouping criteria may be passed as an array. Each array element will be 40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']], ]); - $result = $data->groupBy(['skill', function ($item) { + $result = $data->groupBy(['skill', function (array $item) { return $item['roles']; }], preserveKeys: true); @@ -1145,7 +1147,7 @@ If the collection contains simple strings or numeric values, you should pass the You may pass a closure to the `implode` method if you would like to format the values being imploded: - $collection->implode(function ($item, $key) { + $collection->implode(function (array $item, int $key) { return strtoupper($item['product']); }, ', '); @@ -1236,7 +1238,7 @@ The `keyBy` method keys the collection by the given key. If multiple items have You may also pass a callback to the method. The callback should return the value to key the collection by: - $keyed = $collection->keyBy(function ($item, $key) { + $keyed = $collection->keyBy(function (array $item, int $key) { return strtoupper($item['product_id']); }); @@ -1270,7 +1272,7 @@ The `keys` method returns all of the collection's keys: The `last` method returns the last element in the collection that passes a given truth test: - collect([1, 2, 3, 4])->last(function ($value, $key) { + collect([1, 2, 3, 4])->last(function (int $value, int $key) { return $value < 3; }); @@ -1324,7 +1326,7 @@ The `map` method iterates through the collection and passes each value to the gi $collection = collect([1, 2, 3, 4, 5]); - $multiplied = $collection->map(function ($item, $key) { + $multiplied = $collection->map(function (int $item, int $key) { return $item * 2; }); @@ -1344,9 +1346,6 @@ The `mapInto()` method iterates over the collection, creating a new instance of { /** * Create a new currency instance. - * - * @param string $code - * @return void */ function __construct(string $code) { @@ -1371,7 +1370,7 @@ The `mapSpread` method iterates over the collection's items, passing each nested $chunks = $collection->chunk(2); - $sequence = $chunks->mapSpread(function ($even, $odd) { + $sequence = $chunks->mapSpread(function (int $even, int $odd) { return $even + $odd; }); @@ -1399,7 +1398,7 @@ The `mapToGroups` method groups the collection's items by the given closure. The ] ]); - $grouped = $collection->mapToGroups(function ($item, $key) { + $grouped = $collection->mapToGroups(function (array $item, int $key) { return [$item['department'] => $item['name']]; }); @@ -1434,7 +1433,7 @@ The `mapWithKeys` method iterates through the collection and passes each value t ] ]); - $keyed = $collection->mapWithKeys(function ($item, $key) { + $keyed = $collection->mapWithKeys(function (array $item, int $key) { return [$item['email'] => $item['name']]; }); @@ -1624,7 +1623,7 @@ The `partition` method may be combined with PHP array destructuring to separate $collection = collect([1, 2, 3, 4, 5, 6]); - [$underThree, $equalOrAboveThree] = $collection->partition(function ($i) { + [$underThree, $equalOrAboveThree] = $collection->partition(function (int $i) { return $i < 3; }); @@ -1643,7 +1642,7 @@ The `pipe` method passes the collection to the given closure and returns the res $collection = collect([1, 2, 3]); - $piped = $collection->pipe(function ($collection) { + $piped = $collection->pipe(function (Collection $collection) { return $collection->sum(); }); @@ -1663,9 +1662,6 @@ The `pipeInto` method creates a new instance of the given class and passes the c /** * Create a new ResourceCollection instance. - * - * @param Collection $collection - * @return void */ public function __construct(Collection $collection) { @@ -1686,13 +1682,15 @@ The `pipeInto` method creates a new instance of the given class and passes the c The `pipeThrough` method passes the collection to the given array of closures and returns the result of the executed closures: + use Illuminate\Support\Collection; + $collection = collect([1, 2, 3]); $result = $collection->pipeThrough([ - function ($collection) { + function (Collection $collection) { return $collection->merge([4, 5]); }, - function ($collection) { + function (Collection $collection) { return $collection->sum(); }, ]); @@ -1875,7 +1873,9 @@ If the collection instance has fewer items than requested, the `random` method w The `random` method also accepts a closure, which will receive the current collection instance: - $random = $collection->random(fn ($items) => min(10, count($items))); + use Illuminate\Support\Collection; + + $random = $collection->random(fn (Collection $items) => min(10, count($items))); $random->all(); @@ -1899,7 +1899,7 @@ The `reduce` method reduces the collection to a single value, passing the result $collection = collect([1, 2, 3]); - $total = $collection->reduce(function ($carry, $item) { + $total = $collection->reduce(function (int $carry, int $item) { return $carry + $item; }); @@ -1907,7 +1907,7 @@ The `reduce` method reduces the collection to a single value, passing the result The value for `$carry` on the first iteration is `null`; however, you may specify its initial value by passing a second argument to `reduce`: - $collection->reduce(function ($carry, $item) { + $collection->reduce(function (int $carry, int $item) { return $carry + $item; }, 4); @@ -1927,7 +1927,7 @@ The `reduce` method also passes array keys in associative collections to the giv 'eur' => 1.22, ]; - $collection->reduce(function ($carry, $value, $key) use ($ratio) { + $collection->reduce(function (int $carry, int $value, int $key) use ($ratio) { return $carry + ($value * $ratio[$key]); }); @@ -1957,7 +1957,7 @@ The `reject` method filters the collection using the given closure. The closure $collection = collect([1, 2, 3, 4]); - $filtered = $collection->reject(function ($value, $key) { + $filtered = $collection->reject(function (int $value, int $key) { return $value > 2; }); @@ -2044,7 +2044,7 @@ The search is done using a "loose" comparison, meaning a string with an integer Alternatively, you may provide your own closure to search for the first item that passes a given truth test: - collect([2, 4, 6, 8])->search(function ($item, $key) { + collect([2, 4, 6, 8])->search(function (int $item, int $key) { return $item > 5; }); @@ -2110,7 +2110,7 @@ The `skipUntil` method skips over items from the collection until the given call $collection = collect([1, 2, 3, 4]); - $subset = $collection->skipUntil(function ($item) { + $subset = $collection->skipUntil(function (int $item) { return $item >= 3; }); @@ -2138,7 +2138,7 @@ The `skipWhile` method skips over items from the collection while the given call $collection = collect([1, 2, 3, 4]); - $subset = $collection->skipWhile(function ($item) { + $subset = $collection->skipWhile(function (int $item) { return $item <= 3; }); @@ -2187,7 +2187,7 @@ The `sliding` method returns a new collection of chunks representing a "sliding This is especially useful in conjunction with the [`eachSpread`](#method-eachspread) method: - $transactions->sliding(2)->eachSpread(function ($previous, $current) { + $transactions->sliding(2)->eachSpread(function (Collection $previous, Collection $current) { $current->total = $previous->total + $current->amount; }); @@ -2206,7 +2206,7 @@ You may optionally pass a second "step" value, which determines the distance bet The `sole` method returns the first element in the collection that passes a given truth test, but only if the truth test matches exactly one element: - collect([1, 2, 3, 4])->sole(function ($value, $key) { + collect([1, 2, 3, 4])->sole(function (int $value, int $key) { return $value === 2; }); @@ -2309,7 +2309,7 @@ Alternatively, you may pass your own closure to determine how to sort the collec ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], ]); - $sorted = $collection->sortBy(function ($product, $key) { + $sorted = $collection->sortBy(function (array $product, int $key) { return count($product['colors']); }); @@ -2358,8 +2358,8 @@ When sorting a collection by multiple attributes, you may also provide closures ]); $sorted = $collection->sortBy([ - fn ($a, $b) => $a['name'] <=> $b['name'], - fn ($a, $b) => $b['age'] <=> $a['age'], + fn (array $a, array $b) => $a['name'] <=> $b['name'], + fn (array $a, array $b) => $b['age'] <=> $a['age'], ]); $sorted->values()->all(); @@ -2545,7 +2545,7 @@ In addition, you may pass your own closure to determine which values of the coll ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], ]); - $collection->sum(function ($product) { + $collection->sum(function (array $product) { return count($product['colors']); }); @@ -2581,7 +2581,7 @@ The `takeUntil` method returns items in the collection until the given callback $collection = collect([1, 2, 3, 4]); - $subset = $collection->takeUntil(function ($item) { + $subset = $collection->takeUntil(function (int $item) { return $item >= 3; }); @@ -2609,7 +2609,7 @@ The `takeWhile` method returns items in the collection until the given callback $collection = collect([1, 2, 3, 4]); - $subset = $collection->takeWhile(function ($item) { + $subset = $collection->takeWhile(function (int $item) { return $item < 3; }); @@ -2627,7 +2627,7 @@ The `tap` method passes the collection to the given callback, allowing you to "t collect([2, 4, 3, 1, 5]) ->sort() - ->tap(function ($collection) { + ->tap(function (Collection $collection) { Log::debug('Values after sorting', $collection->values()->all()); }) ->shift(); @@ -2639,7 +2639,7 @@ The `tap` method passes the collection to the given callback, allowing you to "t The static `times` method creates a new collection by invoking the given closure a specified number of times: - $collection = Collection::times(10, function ($number) { + $collection = Collection::times(10, function (int $number) { return $number * 9; }); @@ -2683,7 +2683,7 @@ The `transform` method iterates over the collection and calls the given callback $collection = collect([1, 2, 3, 4, 5]); - $collection->transform(function ($item, $key) { + $collection->transform(function (int $item, int $key) { return $item * 2; }); @@ -2778,7 +2778,7 @@ When dealing with nested arrays or objects, you may specify the key used to dete Finally, you may also pass your own closure to the `unique` method to specify which value should determine an item's uniqueness: - $unique = $collection->unique(function ($item) { + $unique = $collection->unique(function (array $item) { return $item['brand'].$item['type']; }); @@ -2810,11 +2810,11 @@ The `unless` method will execute the given callback unless the first argument gi $collection = collect([1, 2, 3]); - $collection->unless(true, function ($collection) { + $collection->unless(true, function (Collection $collection) { return $collection->push(4); }); - $collection->unless(false, function ($collection) { + $collection->unless(false, function (Collection $collection) { return $collection->push(5); }); @@ -2826,9 +2826,9 @@ A second callback may be passed to the `unless` method. The second callback will $collection = collect([1, 2, 3]); - $collection->unless(true, function ($collection) { + $collection->unless(true, function (Collection $collection) { return $collection->push(4); - }, function ($collection) { + }, function (Collection $collection) { return $collection->push(5); }); @@ -2907,11 +2907,11 @@ The `when` method will execute the given callback when the first argument given $collection = collect([1, 2, 3]); - $collection->when(true, function ($collection, $value) { + $collection->when(true, function (Collection $collection, int $value) { return $collection->push(4); }); - $collection->when(false, function ($collection, $value) { + $collection->when(false, function (Collection $collection, int $value) { return $collection->push(5); }); @@ -2923,9 +2923,9 @@ A second callback may be passed to the `when` method. The second callback will b $collection = collect([1, 2, 3]); - $collection->when(false, function ($collection, $value) { + $collection->when(false, function (Collection $collection, int $value) { return $collection->push(4); - }, function ($collection) { + }, function (Collection $collection) { return $collection->push(5); }); @@ -2942,7 +2942,7 @@ The `whenEmpty` method will execute the given callback when the collection is em $collection = collect(['Michael', 'Tom']); - $collection->whenEmpty(function ($collection) { + $collection->whenEmpty(function (Collection $collection) { return $collection->push('Adam'); }); @@ -2953,7 +2953,7 @@ The `whenEmpty` method will execute the given callback when the collection is em $collection = collect(); - $collection->whenEmpty(function ($collection) { + $collection->whenEmpty(function (Collection $collection) { return $collection->push('Adam'); }); @@ -2965,9 +2965,9 @@ A second closure may be passed to the `whenEmpty` method that will be executed w $collection = collect(['Michael', 'Tom']); - $collection->whenEmpty(function ($collection) { + $collection->whenEmpty(function (Collection $collection) { return $collection->push('Adam'); - }, function ($collection) { + }, function (Collection $collection) { return $collection->push('Taylor'); }); @@ -2984,7 +2984,7 @@ The `whenNotEmpty` method will execute the given callback when the collection is $collection = collect(['michael', 'tom']); - $collection->whenNotEmpty(function ($collection) { + $collection->whenNotEmpty(function (Collection $collection) { return $collection->push('adam'); }); @@ -2995,7 +2995,7 @@ The `whenNotEmpty` method will execute the given callback when the collection is $collection = collect(); - $collection->whenNotEmpty(function ($collection) { + $collection->whenNotEmpty(function (Collection $collection) { return $collection->push('adam'); }); @@ -3007,9 +3007,9 @@ A second closure may be passed to the `whenNotEmpty` method that will be execute $collection = collect(); - $collection->whenNotEmpty(function ($collection) { + $collection->whenNotEmpty(function (Collection $collection) { return $collection->push('adam'); - }, function ($collection) { + }, function (Collection $collection) { return $collection->push('taylor'); }); @@ -3320,7 +3320,7 @@ For example, imagine your application needs to process a multi-gigabyte log file while (($line = fgets($handle)) !== false) { yield $line; } - })->chunk(4)->map(function ($lines) { + })->chunk(4)->map(function (array $lines) { return LogEntry::fromLines($lines); })->each(function (LogEntry $logEntry) { // Process the log entry... @@ -3330,7 +3330,7 @@ Or, imagine you need to iterate through 10,000 Eloquent models. When using tradi use App\Models\User; - $users = User::all()->filter(function ($user) { + $users = User::all()->filter(function (User $user) { return $user->id > 500; }); @@ -3338,7 +3338,7 @@ However, the query builder's `cursor` method returns a `LazyCollection` instance use App\Models\User; - $users = User::cursor()->filter(function ($user) { + $users = User::cursor()->filter(function (User $user) { return $user->id > 500; }); @@ -3510,7 +3510,7 @@ The `takeUntilTimeout` method returns a new lazy collection that will enumerate $lazyCollection = LazyCollection::times(INF) ->takeUntilTimeout(now()->addMinute()); - $lazyCollection->each(function ($number) { + $lazyCollection->each(function (int $number) { dump($number); sleep(1); @@ -3531,7 +3531,7 @@ To illustrate the usage of this method, imagine an application that submits invo ->takeUntilTimeout( Carbon::createFromTimestamp(LARAVEL_START)->add(14, 'minutes') ) - ->each(fn ($invoice) => $invoice->submit()); + ->each(fn (Invoice $invoice) => $invoice->submit()); #### `tapEach()` {.collection-method} @@ -3539,7 +3539,7 @@ To illustrate the usage of this method, imagine an application that submits invo While the `each` method calls the given callback for each item in the collection right away, the `tapEach` method only calls the given callback as the items are being pulled out of the list one by one: // Nothing has been dumped so far... - $lazyCollection = LazyCollection::times(INF)->tapEach(function ($value) { + $lazyCollection = LazyCollection::times(INF)->tapEach(function (int $value) { dump($value); }); diff --git a/console-tests.md b/console-tests.md index 2ad53ff7644..785d2aa8600 100644 --- a/console-tests.md +++ b/console-tests.md @@ -16,10 +16,8 @@ To get started, let's explore how to make assertions regarding an Artisan comman /** * Test a console command. - * - * @return void */ - public function test_console_command() + public function test_console_command(): void { $this->artisan('inspire')->assertExitCode(0); } @@ -55,10 +53,8 @@ You may test this command with the following test which utilizes the `expectsQue /** * Test a console command. - * - * @return void */ - public function test_console_command() + public function test_console_command(): void { $this->artisan('question') ->expectsQuestion('What is your name?', 'Taylor Otwell') diff --git a/container.md b/container.md index aaac9d05e96..11cab659106 100644 --- a/container.md +++ b/container.md @@ -32,6 +32,7 @@ Let's look at a simple example: use App\Http\Controllers\Controller; use App\Repositories\UserRepository; use App\Models\User; + use Illuminate\View\View; class UserController extends Controller { @@ -44,9 +45,6 @@ Let's look at a simple example: /** * Create a new controller instance. - * - * @param UserRepository $users - * @return void */ public function __construct(UserRepository $users) { @@ -55,11 +53,8 @@ Let's look at a simple example: /** * Show the profile for the given user. - * - * @param int $id - * @return Response */ - public function show($id) + public function show(string $id): View { $user = $this->users->find($id); @@ -80,7 +75,7 @@ If a class has no dependencies or only depends on other concrete classes (not in class Service { - // + // ... } Route::get('/', function (Service $service) { @@ -121,8 +116,9 @@ Within a service provider, you always have access to the container via the `$thi use App\Services\Transistor; use App\Services\PodcastParser; + use Illuminate\Contracts\Foundation\Application; - $this->app->bind(Transistor::class, function ($app) { + $this->app->bind(Transistor::class, function (Application $app) { return new Transistor($app->make(PodcastParser::class)); }); @@ -131,9 +127,10 @@ Note that we receive the container itself as an argument to the resolver. We can As mentioned, you will typically be interacting with the container within service providers; however, if you would like to interact with the container outside of a service provider, you may do so via the `App` [facade](/docs/{{version}}/facades): use App\Services\Transistor; + use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\App; - App::bind(Transistor::class, function ($app) { + App::bind(Transistor::class, function (Application $app) { // ... }); @@ -147,8 +144,9 @@ The `singleton` method binds a class or interface into the container that should use App\Services\Transistor; use App\Services\PodcastParser; + use Illuminate\Contracts\Foundation\Application; - $this->app->singleton(Transistor::class, function ($app) { + $this->app->singleton(Transistor::class, function (Application $app) { return new Transistor($app->make(PodcastParser::class)); }); @@ -159,8 +157,9 @@ The `scoped` method binds a class or interface into the container that should on use App\Services\Transistor; use App\Services\PodcastParser; + use Illuminate\Contracts\Foundation\Application; - $this->app->scoped(Transistor::class, function ($app) { + $this->app->scoped(Transistor::class, function (Application $app) { return new Transistor($app->make(PodcastParser::class)); }); @@ -192,9 +191,6 @@ This statement tells the container that it should inject the `RedisEventPusher` /** * Create a new class instance. - * - * @param \App\Contracts\EventPusher $pusher - * @return void */ public function __construct(EventPusher $pusher) { @@ -275,10 +271,6 @@ Occasionally, you may have a class that receives an array of typed objects using /** * Create a new class instance. - * - * @param \App\Services\Logger $logger - * @param array $filters - * @return void */ public function __construct(Logger $logger, Filter ...$filters) { @@ -291,7 +283,7 @@ Using contextual binding, you may resolve this dependency by providing the `give $this->app->when(Firewall::class) ->needs(Filter::class) - ->give(function ($app) { + ->give(function (Application $app) { return [ $app->make(NullFilter::class), $app->make(ProfanityFilter::class), @@ -324,18 +316,18 @@ Sometimes a class may have a variadic dependency that is type-hinted as a given Occasionally, you may need to resolve all of a certain "category" of binding. For example, perhaps you are building a report analyzer that receives an array of many different `Report` interface implementations. After registering the `Report` implementations, you can assign them a tag using the `tag` method: $this->app->bind(CpuReport::class, function () { - // + // ... }); $this->app->bind(MemoryReport::class, function () { - // + // ... }); $this->app->tag([CpuReport::class, MemoryReport::class], 'reports'); Once the services have been tagged, you may easily resolve them all via the container's `tagged` method: - $this->app->bind(ReportAnalyzer::class, function ($app) { + $this->app->bind(ReportAnalyzer::class, function (Application $app) { return new ReportAnalyzer($app->tagged('reports')); }); @@ -344,7 +336,7 @@ Once the services have been tagged, you may easily resolve them all via the cont The `extend` method allows the modification of resolved services. For example, when a service is resolved, you may run additional code to decorate or configure the service. The `extend` method accepts two arguments, the service class you're extending and a closure that should return the modified service. The closure receives the service being resolved and the container instance: - $this->app->extend(Service::class, function ($service, $app) { + $this->app->extend(Service::class, function (Service $service, Application $app) { return new DecoratedService($service); }); @@ -381,9 +373,6 @@ If you would like to have the Laravel container instance itself injected into a /** * Create a new class instance. - * - * @param \Illuminate\Container\Container $container - * @return void */ public function __construct(Container $container) { @@ -402,6 +391,7 @@ For example, you may type-hint a repository defined by your application in a con namespace App\Http\Controllers; use App\Repositories\UserRepository; + use App\Models\User; class UserController extends Controller { @@ -414,9 +404,6 @@ For example, you may type-hint a repository defined by your application in a con /** * Create a new controller instance. - * - * @param \App\Repositories\UserRepository $users - * @return void */ public function __construct(UserRepository $users) { @@ -425,13 +412,12 @@ For example, you may type-hint a repository defined by your application in a con /** * Show the user with the given ID. - * - * @param int $id - * @return \Illuminate\Http\Response */ - public function show($id) + public function show(string $id): User { - // + $user = $this->users->findOrFail($id); + + return $user; } } @@ -450,13 +436,12 @@ Sometimes you may wish to invoke a method on an object instance while allowing t { /** * Generate a new user report. - * - * @param \App\Repositories\UserRepository $repository - * @return array */ - public function generate(UserRepository $repository) + public function generate(UserRepository $repository): array { - // ... + return [ + // ... + ]; } } @@ -482,12 +467,13 @@ The `call` method accepts any PHP callable. The container's `call` method may ev The service container fires an event each time it resolves an object. You may listen to this event using the `resolving` method: use App\Services\Transistor; + use Illuminate\Contracts\Foundation\Application; - $this->app->resolving(Transistor::class, function ($transistor, $app) { + $this->app->resolving(Transistor::class, function (Transistor $transistor, Application $app) { // Called when container resolves objects of type "Transistor"... }); - $this->app->resolving(function ($object, $app) { + $this->app->resolving(function (mixed $object, Application $app) { // Called when container resolves object of any type... }); @@ -504,7 +490,7 @@ Laravel's service container implements the [PSR-11](https://github.com/php-fig/f Route::get('/', function (ContainerInterface $container) { $service = $container->get(Transistor::class); - // + // ... }); An exception is thrown if the given identifier can't be resolved. The exception will be an instance of `Psr\Container\NotFoundExceptionInterface` if the identifier was never bound. If the identifier was bound but was unable to be resolved, an instance of `Psr\Container\ContainerExceptionInterface` will be thrown. diff --git a/contracts.md b/contracts.md index f589cd4730e..51da8d56c8d 100644 --- a/contracts.md +++ b/contracts.md @@ -57,9 +57,6 @@ For example, take a look at this event listener: /** * Create a new event handler instance. - * - * @param \Illuminate\Contracts\Redis\Factory $redis - * @return void */ public function __construct(Factory $redis) { @@ -68,13 +65,10 @@ For example, take a look at this event listener: /** * Handle the event. - * - * @param \App\Events\OrderWasPlaced $event - * @return void */ - public function handle(OrderWasPlaced $event) + public function handle(OrderWasPlaced $event): void { - // + // ... } } diff --git a/contributions.md b/contributions.md index fbe08faf9ab..e01ad8ca642 100644 --- a/contributions.md +++ b/contributions.md @@ -113,7 +113,7 @@ Below is an example of a valid Laravel documentation block. Note that the `@para */ public function bind($abstract, $concrete = null, $shared = false) { - // + // ... } diff --git a/controllers.md b/controllers.md index 4ee0bfd8e05..77f7407a0a5 100644 --- a/controllers.md +++ b/controllers.md @@ -34,16 +34,14 @@ Let's take a look at an example of a basic controller. Note that the controller namespace App\Http\Controllers; use App\Models\User; + use Illuminate\View\View; class UserController extends Controller { /** * Show the profile for a given user. - * - * @param int $id - * @return \Illuminate\View\View */ - public function show($id) + public function show(string $id): View { return view('user.profile', [ 'user' => User::findOrFail($id) @@ -72,17 +70,18 @@ If a controller action is particularly complex, you might find it convenient to namespace App\Http\Controllers; use App\Models\User; + use Illuminate\Http\Response; class ProvisionServer extends Controller { /** * Provision a new web server. - * - * @return \Illuminate\Http\Response */ - public function __invoke() + public function __invoke(): Response { // ... + + return response()->noContent(); } } @@ -114,8 +113,6 @@ Or, you may find it convenient to specify middleware within your controller's co { /** * Instantiate a new controller instance. - * - * @return void */ public function __construct() { @@ -127,7 +124,10 @@ Or, you may find it convenient to specify middleware within your controller's co Controllers also allow you to register middleware using a closure. This provides a convenient way to define an inline middleware for a single controller without defining an entire middleware class: - $this->middleware(function ($request, $next) { + use Closure; + use Illuminate\Http\Request; + + $this->middleware(function (Request $request, Closure $next) { return $next($request); }); @@ -344,10 +344,8 @@ By default, `Route::resource` will create resource URIs using English verbs and /** * Define your route model bindings, pattern filters, etc. - * - * @return void */ - public function boot() + public function boot(): void { Route::resourceVerbs([ 'create' => 'crear', @@ -468,9 +466,6 @@ The Laravel [service container](/docs/{{version}}/container) is used to resolve /** * Create a new controller instance. - * - * @param \App\Repositories\UserRepository $users - * @return void */ public function __construct(UserRepository $users) { @@ -488,20 +483,20 @@ In addition to constructor injection, you may also type-hint dependencies on you namespace App\Http\Controllers; use Illuminate\Http\Request; + use Illuminate\Http\Response; class UserController extends Controller { /** * Store a new user. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $name = $request->name; - // + // ... + + return response()->noContent(); } } @@ -523,13 +518,11 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` param { /** * Update the given user. - * - * @param \Illuminate\Http\Request $request - * @param string $id - * @return \Illuminate\Http\Response */ - public function update(Request $request, $id) + public function update(Request $request, string $id): Response { - // + // ... + + return response()->noContent(); } } diff --git a/database-testing.md b/database-testing.md index 757d5c8ead6..8d516cd6800 100644 --- a/database-testing.md +++ b/database-testing.md @@ -30,10 +30,8 @@ Before proceeding much further, let's discuss how to reset your database after e /** * A basic functional test example. - * - * @return void */ - public function test_basic_example() + public function test_basic_example(): void { $response = $this->get('/'); @@ -54,7 +52,7 @@ To learn more about creating and utilizing model factories to create models, ple use App\Models\User; - public function test_models_can_be_instantiated() + public function test_models_can_be_instantiated(): void { $user = User::factory()->create(); @@ -82,10 +80,8 @@ If you would like to use [database seeders](/docs/{{version}}/seeding) to popula /** * Test creating a new order. - * - * @return void */ - public function test_orders_can_be_created() + public function test_orders_can_be_created(): void { // Run the DatabaseSeeder... $this->seed(); diff --git a/database.md b/database.md index 34b1579d1c2..64123d35997 100644 --- a/database.md +++ b/database.md @@ -128,15 +128,14 @@ To run a basic SELECT query, you may use the `select` method on the `DB` facade: use App\Http\Controllers\Controller; use Illuminate\Support\Facades\DB; + use Illuminate\View\View; class UserController extends Controller { /** * Show a list of all of the application's users. - * - * @return \Illuminate\Http\Response */ - public function index() + public function index(): View { $users = DB::select('select * from users where active = ?', [1]); @@ -250,6 +249,7 @@ If you would like to specify a closure that is invoked for each SQL query execut namespace App\Providers; + use Illuminate\Database\Events\QueryExecuted; use Illuminate\Support\Facades\DB; use Illuminate\Support\ServiceProvider; @@ -257,22 +257,18 @@ If you would like to specify a closure that is invoked for each SQL query execut { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - DB::listen(function ($query) { + DB::listen(function (QueryExecuted $query) { // $query->sql; // $query->bindings; // $query->time; @@ -298,20 +294,16 @@ A common performance bottleneck of modern web applications is the amount of time { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) { // Notify development team... @@ -431,10 +423,8 @@ use Illuminate\Support\Facades\Notification; /** * Register any other events for your application. - * - * @return void */ -public function boot() +public function boot(): void { Event::listen(function (DatabaseBusy $event) { Notification::route('mail', 'dev@example.com') diff --git a/dusk.md b/dusk.md index 55db79d4d66..f42a2f13d0d 100644 --- a/dusk.md +++ b/dusk.md @@ -109,21 +109,20 @@ To get started, open your `tests/DuskTestCase.php` file, which is the base Dusk * Prepare for Dusk test execution. * * @beforeClass - * @return void */ - public static function prepare() + public static function prepare(): void { // static::startChromeDriver(); } Next, you may modify the `driver` method to connect to the URL and port of your choice. In addition, you may modify the "desired capabilities" that should be passed to the WebDriver: + use Facebook\WebDriver\Remote\RemoteWebDriver; + /** * Create the RemoteWebDriver instance. - * - * @return \Facebook\WebDriver\Remote\RemoteWebDriver */ - protected function driver() + protected function driver(): RemoteWebDriver { return RemoteWebDriver::create( 'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs() @@ -197,21 +196,20 @@ By default, Dusk will automatically attempt to start ChromeDriver. If this does * Prepare for Dusk test execution. * * @beforeClass - * @return void */ - public static function prepare() + public static function prepare(): void { // static::startChromeDriver(); } In addition, if you start ChromeDriver on a port other than 9515, you should modify the `driver` method of the same class to reflect the correct port: + use Facebook\WebDriver\Remote\RemoteWebDriver; + /** * Create the RemoteWebDriver instance. - * - * @return \Facebook\WebDriver\Remote\RemoteWebDriver */ - protected function driver() + protected function driver(): RemoteWebDriver { return RemoteWebDriver::create( 'http://localhost:9515/', DesiredCapabilities::chrome() @@ -239,6 +237,7 @@ To get started, let's write a test that verifies we can log into our application use App\Models\User; use Illuminate\Foundation\Testing\DatabaseMigrations; + use Laravel\Dusk\Browser; use Laravel\Dusk\Chrome; use Tests\DuskTestCase; @@ -248,16 +247,14 @@ To get started, let's write a test that verifies we can log into our application /** * A basic browser test example. - * - * @return void */ - public function test_basic_example() + public function test_basic_example(): void { $user = User::factory()->create([ 'email' => 'taylor@laravel.com', ]); - $this->browse(function ($browser) use ($user) { + $this->browse(function (Browser $browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'password') @@ -274,7 +271,7 @@ As you can see in the example above, the `browse` method accepts a closure. A br Sometimes you may need multiple browsers in order to properly carry out a test. For example, multiple browsers may be needed to test a chat screen that interacts with websockets. To create multiple browsers, simply add more browser arguments to the signature of the closure given to the `browse` method: - $this->browse(function ($first, $second) { + $this->browse(function (Browser $first, Browser $second) { $first->loginAs(User::find(1)) ->visit('/home') ->waitForText('Message'); @@ -349,12 +346,10 @@ If you would like to define a custom browser method that you can re-use in a var { /** * Register Dusk's browser macros. - * - * @return void */ - public function boot() + public function boot(): void { - Browser::macro('scrollToElement', function ($element = null) { + Browser::macro('scrollToElement', function (string $element = null) { $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);"); return $this; @@ -364,7 +359,7 @@ If you would like to define a custom browser method that you can re-use in a var 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 as a method on a `Browser` instance: - $this->browse(function ($browser) use ($user) { + $this->browse(function (Browser $browser) use ($user) { $browser->visit('/pay') ->scrollToElement('#credit-card-details') ->assertSee('Enter Credit Card Details'); @@ -376,8 +371,9 @@ The `macro` function accepts a name as its first argument, and a closure as its Often, you will be testing pages that require authentication. You can use Dusk's `loginAs` method in order to avoid interacting with your application's login screen during every test. The `loginAs` method accepts a primary key associated with your authenticatable model or an authenticatable model instance: use App\Models\User; + use Laravel\Dusk\Browser; - $this->browse(function ($browser) { + $this->browse(function (Browser $browser) { $browser->loginAs(User::find(1)) ->visit('/home'); }); @@ -708,22 +704,22 @@ To close an open JavaScript dialog by clicking the "Cancel" button, you may invo Sometimes you may wish to perform several operations while scoping all of the operations within a given selector. For example, you may wish to assert that some text exists only within a table and then click a button within that table. You may use the `with` method to accomplish this. All operations performed within the closure given to the `with` method will be scoped to the original selector: - $browser->with('.table', function ($table) { + $browser->with('.table', function (Browser $table) { $table->assertSee('Hello World') ->clickLink('Delete'); }); You may occasionally need to execute assertions outside of the current scope. You may use the `elsewhere` and `elsewhereWhenAvailable` methods to accomplish this: - $browser->with('.table', function ($table) { + $browser->with('.table', function (Browser $table) { // Current scope is `body .table`... - $browser->elsewhere('.page-title', function ($title) { + $browser->elsewhere('.page-title', function (Browser $title) { // Current scope is `body .page-title`... $title->assertSee('Hello World'); }); - $browser->elsewhereWhenAvailable('.page-title', function ($title) { + $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { // Current scope is `body .page-title`... $title->assertSee('Hello World'); }); @@ -795,7 +791,7 @@ Or, you may wait until the element matching the given selector is enabled or dis Occasionally, you may wish to wait for an element to appear that matches a given selector and then interact with the element. For example, you may wish to wait until a modal window is available and then press the "OK" button within the modal. The `whenAvailable` method may be used to accomplish this. All element operations performed within the given closure will be scoped to the original selector: - $browser->whenAvailable('.modal', function ($modal) { + $browser->whenAvailable('.modal', function (Browser $modal) { $modal->assertSee('Hello World') ->press('OK'); }); @@ -904,7 +900,7 @@ The `waitForEvent` method can be used to pause the execution of a test until a J The event listener is attached to the current scope, which is the `body` element by default. When using a scoped selector, the event listener will be attached to the matching element: - $browser->with('iframe', function ($iframe) { + $browser->with('iframe', function (Browser $iframe) { // Wait for the iframe's load event... $iframe->waitForEvent('load'); }); @@ -1565,10 +1561,8 @@ You may assert on the state of the Vue component like so: /** * A basic Vue test example. - * - * @return void */ - public function testVue() + public function test_vue(): void { $this->browse(function (Browser $browser) { $browser->visit('/') @@ -1621,10 +1615,8 @@ The `url` method should return the path of the URL that represents the page. Dus /** * Get the URL for the page. - * - * @return string */ - public function url() + public function url(): string { return '/login'; } @@ -1636,10 +1628,8 @@ The `assert` method may make any assertions necessary to verify that the browser /** * Assert that the browser is on the page. - * - * @return void */ - public function assert(Browser $browser) + public function assert(Browser $browser): void { $browser->assertPathIs($this->url()); } @@ -1670,9 +1660,9 @@ The `elements` method within page classes allows you to define quick, easy-to-re /** * Get the element shortcuts for the page. * - * @return array + * @return array */ - public function elements() + public function elements(): array { return [ '@email' => 'input[name=email]', @@ -1691,9 +1681,9 @@ After installing Dusk, a base `Page` class will be placed in your `tests/Browser /** * Get the global element shortcuts for the site. * - * @return array + * @return array */ - public static function siteElements() + public static function siteElements(): array { return [ '@element' => '#selector', @@ -1717,12 +1707,8 @@ In addition to the default methods defined on pages, you may define additional m /** * Create a new playlist. - * - * @param \Laravel\Dusk\Browser $browser - * @param string $name - * @return void */ - public function createPlaylist(Browser $browser, $name) + public function createPlaylist(Browser $browser, string $name): void { $browser->type('name', $name) ->check('share') @@ -1763,21 +1749,16 @@ As shown above, a "date picker" is an example of a component that might exist th { /** * Get the root selector for the component. - * - * @return string */ - public function selector() + public function selector(): string { return '.date-picker'; } /** * Assert that the browser page contains the component. - * - * @param Browser $browser - * @return void */ - public function assert(Browser $browser) + public function assert(Browser $browser): void { $browser->assertVisible($this->selector()); } @@ -1785,9 +1766,9 @@ As shown above, a "date picker" is an example of a component that might exist th /** * Get the element shortcuts for the component. * - * @return array + * @return array */ - public function elements() + public function elements(): array { return [ '@date-field' => 'input.datepicker-input', @@ -1799,23 +1780,17 @@ As shown above, a "date picker" is an example of a component that might exist th /** * Select the given date. - * - * @param \Laravel\Dusk\Browser $browser - * @param int $year - * @param int $month - * @param int $day - * @return void */ - public function selectDate(Browser $browser, $year, $month, $day) + public function selectDate(Browser $browser, int $year, int $month, int $day): void { $browser->click('@date-field') - ->within('@year-list', function ($browser) use ($year) { + ->within('@year-list', function (Browser $browser) use ($year) { $browser->click($year); }) - ->within('@month-list', function ($browser) use ($month) { + ->within('@month-list', function (Browser $browser) use ($month) { $browser->click($month); }) - ->within('@day-list', function ($browser) use ($day) { + ->within('@day-list', function (Browser $browser) use ($day) { $browser->click($day); }); } @@ -1839,14 +1814,12 @@ Once the component has been defined, we can easily select a date within the date { /** * A basic component test example. - * - * @return void */ - public function testBasicExample() + public function test_basic_example(): void { $this->browse(function (Browser $browser) { $browser->visit('/') - ->within(new DatePicker, function ($browser) { + ->within(new DatePicker, function (Browser $browser) { $browser->selectDate(2019, 1, 30); }) ->assertSee('January'); diff --git a/eloquent-collections.md b/eloquent-collections.md index 76ef8113e64..eead73a998b 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -21,9 +21,9 @@ All collections also serve as iterators, allowing you to loop over them as if th However, as previously mentioned, collections are much more powerful than arrays and expose a variety of map / reduce operations that may be chained using an intuitive interface. For example, we may remove all inactive models and then gather the first name for each remaining user: - $names = User::all()->reject(function ($user) { + $names = User::all()->reject(function (User $user) { return $user->active === false; - })->map(function ($user) { + })->map(function (User $user) { return $user->name; }); @@ -223,6 +223,7 @@ If you would like to use a custom `Collection` object when interacting with a gi namespace App\Models; use App\Support\UserCollection; + use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; class User extends Model @@ -230,10 +231,10 @@ If you would like to use a custom `Collection` object when interacting with a gi /** * Create a new Eloquent Collection instance. * - * @param array $models - * @return \Illuminate\Database\Eloquent\Collection + * @param array $models + * @return \Illuminate\Database\Eloquent\Collection */ - public function newCollection(array $models = []) + public function newCollection(array $models = []): Collection { return new UserCollection($models); } diff --git a/eloquent-factories.md b/eloquent-factories.md index d2d88fd67bd..3b5d11b8e31 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -26,17 +26,17 @@ To see an example of how to write a factory, take a look at the `database/factor namespace Database\Factories; - use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; + use Illuminate\Database\Eloquent\Factories\Factory; class UserFactory extends Factory { /** * Define the model's default state. * - * @return array + * @return array */ - public function definition() + public function definition(): array { return [ 'name' => fake()->name(), @@ -76,14 +76,13 @@ Once you have defined your factories, you may use the static `factory` method pr The `HasFactory` trait's `factory` method will use conventions to determine the proper factory for the model the trait is assigned to. Specifically, the method will look for a factory in the `Database\Factories` namespace that has a class name matching the model name and is suffixed with `Factory`. If these conventions do not apply to your particular application or factory, you may overwrite the `newFactory` method on your model to return an instance of the model's corresponding factory directly: + use Illuminate\Database\Eloquent\Factories\Factory; use Database\Factories\Administration\FlightFactory; /** * Create a new factory instance for the model. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory */ - protected static function newFactory() + protected static function newFactory(): Factory { return FlightFactory::new(); } @@ -110,12 +109,12 @@ State manipulation methods allow you to define discrete modifications that can b State transformation methods typically call the `state` method provided by Laravel's base factory class. The `state` method accepts a closure which will receive the array of raw attributes defined for the factory and should return an array of attributes to modify: + use Illuminate\Database\Eloquent\Factories\Factory; + /** * Indicate that the user is suspended. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory */ - public function suspended() + public function suspended(): Factory { return $this->state(function (array $attributes) { return [ @@ -150,12 +149,12 @@ Factory callbacks are registered using the `afterMaking` and `afterCreating` met * * @return $this */ - public function configure() + public function configure(): static { return $this->afterMaking(function (User $user) { - // + // ... })->afterCreating(function (User $user) { - // + // ... }); } @@ -242,10 +241,12 @@ In this example, five users will be created with an `admin` value of `Y` and fiv If necessary, you may include a closure as a sequence value. The closure will be invoked each time the sequence needs a new value: + use Illuminate\Database\Eloquent\Factories\Sequence; + $users = User::factory() ->count(10) ->state(new Sequence( - fn ($sequence) => ['role' => UserRoles::all()->random()], + fn (Sequence $sequence) => ['role' => UserRoles::all()->random()], )) ->create(); @@ -253,7 +254,7 @@ Within a sequence closure, you may access the `$index` or `$count` properties on $users = User::factory() ->count(10) - ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) + ->sequence(fn (Sequence $sequence) => ['name' => 'Name '.$sequence->index]) ->create(); @@ -459,9 +460,9 @@ To define a relationship within your model factory, you will typically assign a /** * Define the model's default state. * - * @return array + * @return array */ - public function definition() + public function definition(): array { return [ 'user_id' => User::factory(), @@ -475,9 +476,9 @@ If the relationship's columns depend on the factory that defines it you may assi /** * Define the model's default state. * - * @return array + * @return array */ - public function definition() + public function definition(): array { return [ 'user_id' => User::factory(), diff --git a/eloquent-mutators.md b/eloquent-mutators.md index f1dfbfd4a3a..33642f5ec5d 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -43,13 +43,11 @@ In this example, we'll define an accessor for the `first_name` attribute. The ac { /** * Get the user's first name. - * - * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function firstName(): Attribute { return Attribute::make( - get: fn ($value) => ucfirst($value), + get: fn (string $value) => ucfirst($value), ); } } @@ -78,13 +76,11 @@ use Illuminate\Database\Eloquent\Casts\Attribute; /** * Interact with the user's address. - * - * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function address(): Attribute { return Attribute::make( - get: fn ($value, $attributes) => new Address( + get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), @@ -112,7 +108,7 @@ However, you may sometimes wish to enable caching for primitive values like stri protected function hash(): Attribute { return Attribute::make( - get: fn ($value) => bcrypt(gzuncompress($value)), + get: fn (string $value) => bcrypt(gzuncompress($value)), )->shouldCache(); } ``` @@ -122,13 +118,11 @@ If you would like to disable the object caching behavior of attributes, you may ```php /** * Interact with the user's address. - * - * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function address(): Attribute { return Attribute::make( - get: fn ($value, $attributes) => new Address( + get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), @@ -152,14 +146,12 @@ A mutator transforms an Eloquent attribute value when it is set. To define a mut { /** * Interact with the user's first name. - * - * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function firstName(): Attribute { return Attribute::make( - get: fn ($value) => ucfirst($value), - set: fn ($value) => strtolower($value), + get: fn (string $value) => ucfirst($value), + set: fn (string $value) => strtolower($value), ); } } @@ -185,13 +177,11 @@ use Illuminate\Database\Eloquent\Casts\Attribute; /** * Interact with the user's address. - * - * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function address(): Attribute { return Attribute::make( - get: fn ($value, $attributes) => new Address( + get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), @@ -260,7 +250,7 @@ After defining the cast, the `is_admin` attribute will always be cast to a boole $user = App\Models\User::find(1); if ($user->is_admin) { - // + // ... } If you need to add a new, temporary cast at runtime, you may use the `mergeCasts` method. These cast definitions will be added to any of the casts already defined on the model: @@ -397,11 +387,8 @@ You may customize the default serialization format for all of your model's dates /** * Prepare a date for array / JSON serialization. - * - * @param \DateTimeInterface $date - * @return string */ - protected function serializeDate(DateTimeInterface $date) + protected function serializeDate(DateTimeInterface $date): string { return $date->format('Y-m-d'); } @@ -501,19 +488,17 @@ All custom cast classes implement the `CastsAttributes` interface. Classes that namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; + use Illuminate\Database\Eloquent\Model; class Json implements CastsAttributes { /** * Cast the given value. * - * @param \Illuminate\Database\Eloquent\Model $model - * @param string $key - * @param mixed $value - * @param array $attributes - * @return array + * @param array $attributes + * @return array */ - public function get($model, $key, $value, $attributes) + public function get(Model $model, string $key, mixed $value, array $attributes): array { return json_decode($value, true); } @@ -521,13 +506,9 @@ All custom cast classes implement the `CastsAttributes` interface. Classes that /** * Prepare the given value for storage. * - * @param \Illuminate\Database\Eloquent\Model $model - * @param string $key - * @param array $value - * @param array $attributes - * @return string + * @param array $attributes */ - public function set($model, $key, $value, $attributes) + public function set(Model $model, string $key, mixed $value, array $attributes): string { return json_encode($value); } @@ -567,6 +548,7 @@ As an example, we will define a custom cast class that casts multiple model valu use App\ValueObjects\Address as AddressValueObject; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; + use Illuminate\Database\Eloquent\Model; use InvalidArgumentException; class Address implements CastsAttributes @@ -574,13 +556,9 @@ As an example, we will define a custom cast class that casts multiple model valu /** * Cast the given value. * - * @param \Illuminate\Database\Eloquent\Model $model - * @param string $key - * @param mixed $value - * @param array $attributes - * @return \App\ValueObjects\Address + * @param array $attributes */ - public function get($model, $key, $value, $attributes) + public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject { return new AddressValueObject( $attributes['address_line_one'], @@ -591,13 +569,10 @@ As an example, we will define a custom cast class that casts multiple model valu /** * Prepare the given value for storage. * - * @param \Illuminate\Database\Eloquent\Model $model - * @param string $key - * @param \App\ValueObjects\Address $value - * @param array $attributes - * @return array + * @param array $attributes + * @return array */ - public function set($model, $key, $value, $attributes) + 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.'); @@ -633,13 +608,9 @@ Therefore, you may specify that your custom cast class will be responsible for s /** * Get the serialized representation of the value. * - * @param \Illuminate\Database\Eloquent\Model $model - * @param string $key - * @param mixed $value - * @param array $attributes - * @return mixed + * @param array $attributes */ - public function serialize($model, string $key, $value, array $attributes) + public function serialize(Model $model, string $key, mixed $value, array $attributes): string { return (string) $value; } @@ -662,6 +633,7 @@ A classic example of an inbound only cast is a "hashing" cast. For example, we m namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes; + use Illuminate\Database\Eloquent\Model; class Hash implements CastsInboundAttributes { @@ -674,11 +646,8 @@ A classic example of an inbound only cast is a "hashing" cast. For example, we m /** * Create a new cast class instance. - * - * @param string|null $algorithm - * @return void */ - public function __construct($algorithm = null) + public function __construct(string $algorithm = null) { $this->algorithm = $algorithm; } @@ -686,13 +655,9 @@ A classic example of an inbound only cast is a "hashing" cast. For example, we m /** * Prepare the given value for storage. * - * @param \Illuminate\Database\Eloquent\Model $model - * @param string $key - * @param array $value - * @param array $attributes - * @return string + * @param array $attributes */ - public function set($model, $key, $value, $attributes) + public function set(Model $model, string $key, mixed $value, array $attributes): string { return is_null($this->algorithm) ? bcrypt($value) @@ -739,10 +704,9 @@ Objects that implement the `Castable` interface must define a `castUsing` method /** * Get the name of the caster class to use when casting from / to this cast target. * - * @param array $arguments - * @return string + * @param array $arguments */ - public static function castUsing(array $arguments) + public static function castUsing(array $arguments): string { return AddressCast::class; } @@ -767,6 +731,7 @@ By combining "castables" with PHP's [anonymous classes](https://www.php.net/manu use Illuminate\Contracts\Database\Eloquent\Castable; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; + use Illuminate\Database\Eloquent\Model; class Address implements Castable { @@ -775,14 +740,13 @@ By combining "castables" with PHP's [anonymous classes](https://www.php.net/manu /** * Get the caster class to use when casting from / to this cast target. * - * @param array $arguments - * @return object|string + * @param array $arguments */ - public static function castUsing(array $arguments) + public static function castUsing(array $arguments): CastsAttributes { return new class implements CastsAttributes { - public function get($model, $key, $value, $attributes) + public function get(Model $model, string $key, mixed $value, array $attributes): Address { return new Address( $attributes['address_line_one'], @@ -790,7 +754,7 @@ By combining "castables" with PHP's [anonymous classes](https://www.php.net/manu ); } - public function set($model, $key, $value, $attributes) + public function set(Model $model, string $key, mixed $value, array $attributes): array { return [ 'address_line_one' => $value->lineOne, diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 95d66119eac..372f4e424f9 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -77,13 +77,14 @@ A one-to-one relationship is a very basic type of database relationship. For exa namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\HasOne; class User extends Model { /** * Get the phone associated with the user. */ - public function phone() + public function phone(): HasOne { return $this->hasOne(Phone::class); } @@ -111,13 +112,14 @@ So, we can access the `Phone` model from our `User` model. Next, let's define a namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsTo; class Phone extends Model { /** * Get the user that owns the phone. */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class); } @@ -130,7 +132,7 @@ Eloquent determines the foreign key name by examining the name of the relationsh /** * Get the user that owns the phone. */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class, 'foreign_key'); } @@ -140,7 +142,7 @@ If the parent model does not use `id` as its primary key, or you wish to find th /** * Get the user that owns the phone. */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class, 'foreign_key', 'owner_key'); } @@ -155,13 +157,14 @@ A one-to-many relationship is used to define relationships where a single model namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\HasMany; class Post extends Model { /** * Get the comments for the blog post. */ - public function comments() + public function comments(): HasMany { return $this->hasMany(Comment::class); } @@ -176,7 +179,7 @@ Once the relationship method has been defined, we can access the [collection](/d $comments = Post::find(1)->comments; foreach ($comments as $comment) { - // + // ... } Since all relationships also serve as query builders, you may add further constraints to the relationship query by calling the `comments` method and continuing to chain conditions onto the query: @@ -201,13 +204,14 @@ Now that we can access all of a post's comments, let's define a relationship to namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsTo; class Comment extends Model { /** * Get the post that owns the comment. */ - public function post() + public function post(): BelongsTo { return $this->belongsTo(Post::class); } @@ -230,7 +234,7 @@ However, if the foreign key for your relationship does not follow these conventi /** * Get the post that owns the comment. */ - public function post() + public function post(): BelongsTo { return $this->belongsTo(Post::class, 'foreign_key'); } @@ -240,7 +244,7 @@ If your parent model does not use `id` as its primary key, or you wish to find t /** * Get the post that owns the comment. */ - public function post() + public function post(): BelongsTo { return $this->belongsTo(Post::class, 'foreign_key', 'owner_key'); } @@ -253,7 +257,7 @@ The `belongsTo`, `hasOne`, `hasOneThrough`, and `morphOne` relationships allow y /** * Get the author of the post. */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class)->withDefault(); } @@ -263,7 +267,7 @@ To populate the default model with attributes, you may pass an array or closure /** * Get the author of the post. */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class)->withDefault([ 'name' => 'Guest Author', @@ -273,9 +277,9 @@ To populate the default model with attributes, you may pass an array or closure /** * Get the author of the post. */ - public function user() + public function user(): BelongsTo { - return $this->belongsTo(User::class)->withDefault(function ($user, $post) { + return $this->belongsTo(User::class)->withDefault(function (User $user, Post $post) { $user->name = 'Guest Author'; }); } @@ -312,7 +316,7 @@ Sometimes a model may have many related models, yet you want to easily retrieve /** * Get the user's most recent order. */ -public function latestOrder() +public function latestOrder(): HasOne { return $this->hasOne(Order::class)->latestOfMany(); } @@ -324,7 +328,7 @@ Likewise, you may define a method to retrieve the "oldest", or first, related mo /** * Get the user's oldest order. */ -public function oldestOrder() +public function oldestOrder(): HasOne { return $this->hasOne(Order::class)->oldestOfMany(); } @@ -338,7 +342,7 @@ For example, using the `ofMany` method, you may retrieve the user's most expensi /** * Get the user's largest order. */ -public function largestOrder() +public function largestOrder(): HasOne { return $this->hasOne(Order::class)->ofMany('price', 'max'); } @@ -358,12 +362,12 @@ So, in summary, we need to retrieve the latest published pricing where the publi /** * Get the current pricing for the product. */ -public function currentPricing() +public function currentPricing(): HasOne { return $this->hasOne(Price::class)->ofMany([ 'published_at' => 'max', 'id' => 'max', - ], function ($query) { + ], function (Builder $query) { $query->where('published_at', '<', now()); }); } @@ -397,13 +401,14 @@ Now that we have examined the table structure for the relationship, let's define namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\HasOneThrough; class Mechanic extends Model { /** * Get the car's owner. */ - public function carOwner() + public function carOwner(): HasOneThrough { return $this->hasOneThrough(Owner::class, Car::class); } @@ -421,7 +426,7 @@ Typical Eloquent foreign key conventions will be used when performing the relati /** * Get the car's owner. */ - public function carOwner() + public function carOwner(): HasOneThrough { return $this->hasOneThrough( Owner::class, @@ -460,13 +465,14 @@ Now that we have examined the table structure for the relationship, let's define namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\HasManyThrough; class Project extends Model { /** * Get all of the deployments for the project. */ - public function deployments() + public function deployments(): HasManyThrough { return $this->hasManyThrough(Deployment::class, Environment::class); } @@ -483,7 +489,7 @@ Typical Eloquent foreign key conventions will be used when performing the relati class Project extends Model { - public function deployments() + public function deployments(): HasOneThrough { return $this->hasManyThrough( Deployment::class, @@ -530,13 +536,14 @@ Many-to-many relationships are defined by writing a method that returns the resu namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsToMany; class User extends Model { /** * The roles that belong to the user. */ - public function roles() + public function roles(): BelongsToMany { return $this->belongsToMany(Role::class); } @@ -549,7 +556,7 @@ Once the relationship is defined, you may access the user's roles using the `rol $user = User::find(1); foreach ($user->roles as $role) { - // + // ... } Since all relationships also serve as query builders, you may add further constraints to the relationship query by calling the `roles` method and continuing to chain conditions onto the query: @@ -574,13 +581,14 @@ To define the "inverse" of a many-to-many relationship, you should define a meth namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Role extends Model { /** * The users that belong to the role. */ - public function users() + public function users(): BelongsToMany { return $this->belongsToMany(User::class); } @@ -684,13 +692,14 @@ Custom many-to-many pivot models should extend the `Illuminate\Database\Eloquent namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Role extends Model { /** * The users that belong to the role. */ - public function users() + public function users(): BelongsToMany { return $this->belongsToMany(User::class)->using(RoleUser::class); } @@ -706,7 +715,7 @@ When defining the `RoleUser` model, you should extend the `Illuminate\Database\E class RoleUser extends Pivot { - // + // ... } > **Warning** @@ -763,35 +772,42 @@ Next, let's examine the model definitions needed to build this relationship: namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphTo; class Image extends Model { /** * Get the parent imageable model (user or post). */ - public function imageable() + public function imageable(): MorphTo { return $this->morphTo(); } } + use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphOne; + class Post extends Model { /** * Get the post's image. */ - public function image() + public function image(): MorphOne { return $this->morphOne(Image::class, 'imageable'); } } + use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphOne; + class User extends Model { /** * Get the user's image. */ - public function image() + public function image(): MorphOne { return $this->morphOne(Image::class, 'imageable'); } @@ -826,7 +842,7 @@ If necessary, you may specify the name of the "id" and "type" columns utilized b /** * Get the model that the image belongs to. */ - public function imageable() + public function imageable(): MorphTo { return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id'); } @@ -865,35 +881,42 @@ Next, let's examine the model definitions needed to build this relationship: namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphTo; class Comment extends Model { /** * Get the parent commentable model (post or video). */ - public function commentable() + public function commentable(): MorphTo { return $this->morphTo(); } } + use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphMany; + class Post extends Model { /** * Get all of the post's comments. */ - public function comments() + public function comments(): MorphMany { return $this->morphMany(Comment::class, 'commentable'); } } + use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphMany; + class Video extends Model { /** * Get all of the video's comments. */ - public function comments() + public function comments(): MorphMany { return $this->morphMany(Comment::class, 'commentable'); } @@ -909,7 +932,7 @@ Once your database table and models are defined, you may access the relationship $post = Post::find(1); foreach ($post->comments as $comment) { - // + // ... } You may also retrieve the parent of a polymorphic child model by accessing the name of the method that performs the call to `morphTo`. In this case, that is the `commentable` method on the `Comment` model. So, we will access that method as a dynamic relationship property in order to access the comment's parent model: @@ -931,7 +954,7 @@ Sometimes a model may have many related models, yet you want to easily retrieve /** * Get the user's most recent image. */ -public function latestImage() +public function latestImage(): MorphOne { return $this->morphOne(Image::class, 'imageable')->latestOfMany(); } @@ -943,7 +966,7 @@ Likewise, you may define a method to retrieve the "oldest", or first, related mo /** * Get the user's oldest image. */ -public function oldestImage() +public function oldestImage(): MorphOne { return $this->morphOne(Image::class, 'imageable')->oldestOfMany(); } @@ -957,7 +980,7 @@ For example, using the `ofMany` method, you may retrieve the user's most "liked" /** * Get the user's most popular image. */ -public function bestImage() +public function bestImage(): MorphOne { return $this->morphOne(Image::class, 'imageable')->ofMany('likes', 'max'); } @@ -1006,13 +1029,14 @@ The `morphToMany` method accepts the name of the related model as well as the "r namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphToMany; class Post extends Model { /** * Get all of the tags for the post. */ - public function tags() + public function tags(): MorphToMany { return $this->morphToMany(Tag::class, 'taggable'); } @@ -1030,13 +1054,14 @@ The `morphedByMany` method accepts the name of the related model as well as the namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\MorphToMany; class Tag extends Model { /** * Get all of the posts that are assigned this tag. */ - public function posts() + public function posts(): MorphToMany { return $this->morphedByMany(Post::class, 'taggable'); } @@ -1044,7 +1069,7 @@ The `morphedByMany` method accepts the name of the related model as well as the /** * Get all of the videos that are assigned this tag. */ - public function videos() + public function videos(): MorphToMany { return $this->morphedByMany(Video::class, 'taggable'); } @@ -1060,7 +1085,7 @@ Once your database table and models are defined, you may access the relationship $post = Post::find(1); foreach ($post->tags as $tag) { - // + // ... } You may retrieve the parent of a polymorphic relation from the polymorphic child model by accessing the name of the method that performs the call to `morphedByMany`. In this case, that is the `posts` or `videos` methods on the `Tag` model: @@ -1070,11 +1095,11 @@ You may retrieve the parent of a polymorphic relation from the polymorphic child $tag = Tag::find(1); foreach ($tag->posts as $post) { - // + // ... } foreach ($tag->videos as $video) { - // + // ... } @@ -1114,7 +1139,7 @@ The `resolveRelationUsing` method accepts the desired relationship name as its f use App\Models\Order; use App\Models\Customer; - Order::resolveRelationUsing('customer', function ($orderModel) { + Order::resolveRelationUsing('customer', function (Order $orderModel) { return $orderModel->belongsTo(Customer::class, 'customer_id'); }); @@ -1133,13 +1158,14 @@ For example, imagine a blog application in which a `User` model has many associa namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\HasMany; class User extends Model { /** * Get all of the posts for the user. */ - public function posts() + public function posts(): HasMany { return $this->hasMany(Post::class); } @@ -1202,7 +1228,7 @@ If you do not need to add additional constraints to an Eloquent relationship que $user = User::find(1); foreach ($user->posts as $post) { - // + // ... } Dynamic relationship properties perform "lazy loading", meaning they will only load their relationship data when you actually access them. Because of this, developers often use [eager loading](#eager-loading) to pre-load relationships they know will be accessed after loading the model. Eager loading provides a significant reduction in SQL queries that must be executed to load a model's relations. @@ -1319,7 +1345,7 @@ You may occasionally need to add query constraints based on the "type" of the re $comments = Comment::whereHasMorph( 'commentable', [Post::class, Video::class], - function (Builder $query, $type) { + function (Builder $query, string $type) { $column = $type === Post::class ? 'content' : 'title'; $query->where($column, 'like', 'code%'); @@ -1389,7 +1415,7 @@ Using the `loadCount` method, you may load a relationship count after the parent If you need to set additional query constraints on the count query, you may pass an array keyed by the relationships you wish to count. The array values should be closures which receive the query builder instance: - $book->loadCount(['reviews' => function ($query) { + $book->loadCount(['reviews' => function (Builder $query) { $query->where('rating', 5); }]) @@ -1476,13 +1502,14 @@ When accessing Eloquent relationships as properties, the related models are "laz namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsTo; class Book extends Model { /** * Get the author that wrote the book. */ - public function author() + public function author(): BelongsTo { return $this->belongsTo(Author::class); } @@ -1547,13 +1574,14 @@ If you would like to eager load a `morphTo` relationship, as well as nested rela morphTo(); } @@ -1594,6 +1622,7 @@ Sometimes you might want to always load some relationships when retrieving a mod namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsTo; class Book extends Model { @@ -1607,7 +1636,7 @@ Sometimes you might want to always load some relationships when retrieving a mod /** * Get the author that wrote the book. */ - public function author() + public function author(): BelongsTo { return $this->belongsTo(Author::class); } @@ -1615,7 +1644,7 @@ Sometimes you might want to always load some relationships when retrieving a mod /** * Get the genre of the book. */ - public function genre() + public function genre(): BelongsTo { return $this->belongsTo(Genre::class); } @@ -1636,13 +1665,13 @@ Sometimes you may wish to eager load a relationship but also specify additional use App\Models\User; - $users = User::with(['posts' => function ($query) { + $users = User::with(['posts' => function (Builder $query) { $query->where('title', 'like', '%code%'); }])->get(); In this example, Eloquent will only eager load posts where the post's `title` column contains the word `code`. You may call other [query builder](/docs/{{version}}/queries) methods to further customize the eager loading operation: - $users = User::with(['posts' => function ($query) { + $users = User::with(['posts' => function (Builder $query) { $query->orderBy('created_at', 'desc'); }])->get(); @@ -1676,8 +1705,9 @@ In this example, Eloquent will only eager load posts that have not been hidden a You may sometimes find yourself needing to check for the existence of a relationship while simultaneously loading the relationship based on the same conditions. For example, you may wish to only retrieve `User` models that have child `Post` models matching a given query condition while also eager loading the matching posts. You may accomplish this using the `withWhereHas` method: use App\Models\User; - - $users = User::withWhereHas('posts', function ($query) { + use Illuminate\Database\Eloquent\Builder; + + $users = User::withWhereHas('posts', function (Builder $query) { $query->where('featured', true); })->get(); @@ -1696,7 +1726,7 @@ Sometimes you may need to eager load a relationship after the parent model has a If you need to set additional query constraints on the eager loading query, you may pass an array keyed by the relationships you wish to load. The array values should be closure instances which receive the query instance: - $author->load(['books' => function ($query) { + $author->load(['books' => function (Builder $query) { $query->orderBy('published_date', 'asc'); }]); @@ -1714,13 +1744,14 @@ This method accepts the name of the `morphTo` relationship as its first argument morphTo(); } @@ -1750,10 +1781,8 @@ use Illuminate\Database\Eloquent\Model; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Model::preventLazyLoading(! $this->app->isProduction()); } @@ -1764,7 +1793,7 @@ After preventing lazy loading, Eloquent will throw a `Illuminate\Database\LazyLo You may customize the behavior of lazy loading violations using the `handleLazyLoadingViolationsUsing` method. For example, using this method, you may instruct lazy loading violations to only be logged instead of interrupting the application's execution with exceptions: ```php -Model::handleLazyLoadingViolationUsing(function ($model, $relation) { +Model::handleLazyLoadingViolationUsing(function (Model $model, string $relation) { $class = get_class($model); info("Attempted to lazy load [{$relation}] on model [{$class}]."); @@ -1959,6 +1988,7 @@ For example, when a `Comment` model is updated, you may want to automatically "t namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\BelongsTo; class Comment extends Model { @@ -1972,7 +2002,7 @@ For example, when a `Comment` model is updated, you may want to automatically "t /** * Get the post that the comment belongs to. */ - public function post() + public function post(): BelongsTo { return $this->belongsTo(Post::class); } diff --git a/eloquent-resources.md b/eloquent-resources.md index 978d7fb6737..e04d9fbafa6 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -53,6 +53,7 @@ Before diving into all of the options available to you when writing resources, l namespace App\Http\Resources; + use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource @@ -60,10 +61,9 @@ Before diving into all of the options available to you when writing resources, l /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -82,7 +82,7 @@ Note that we can access model properties directly from the `$this` variable. Thi use App\Http\Resources\UserResource; use App\Models\User; - Route::get('/user/{id}', function ($id) { + Route::get('/user/{id}', function (string $id) { return new UserResource(User::findOrFail($id)); }); @@ -110,6 +110,7 @@ Once the resource collection class has been generated, you may easily define any namespace App\Http\Resources; + use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; class UserCollection extends ResourceCollection @@ -117,10 +118,9 @@ Once the resource collection class has been generated, you may easily define any /** * Transform the resource collection into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'data' => $this->collection, @@ -205,6 +205,7 @@ In essence, resources are simple. They only need to transform a given model into namespace App\Http\Resources; + use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource @@ -212,10 +213,9 @@ In essence, resources are simple. They only need to transform a given model into /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -232,7 +232,7 @@ Once a resource has been defined, it may be returned directly from a route or co use App\Http\Resources\UserResource; use App\Models\User; - Route::get('/user/{id}', function ($id) { + Route::get('/user/{id}', function (string $id) { return new UserResource(User::findOrFail($id)); }); @@ -242,14 +242,14 @@ Once a resource has been defined, it may be returned directly from a route or co If you would like to include related resources in your response, you may add them to the array returned by your resource's `toArray` method. In this example, we will use the `PostResource` resource's `collection` method to add the user's blog posts to the resource response: use App\Http\Resources\PostResource; + use Illuminate\Http\Request; /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -289,10 +289,9 @@ However, if you need to customize the meta data returned with the collection, it /** * Transform the resource collection into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'data' => $this->collection, @@ -365,20 +364,16 @@ If you would like to disable the wrapping of the outermost resource, you should { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { JsonResource::withoutWrapping(); } @@ -405,10 +400,9 @@ You may be wondering if this will cause your outermost resource to be wrapped in /** * Transform the resource collection into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return ['data' => $this->collection]; } @@ -505,10 +499,9 @@ Sometimes you may wish to only include an attribute in a resource response if a /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -540,10 +533,9 @@ Sometimes you may have several attributes that should only be included in the re /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -575,10 +567,9 @@ The `whenLoaded` method may be used to conditionally load a relationship. In ord /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -604,10 +595,9 @@ The `whenCounted` method may be used to conditionally include a relationship's c /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -629,10 +619,9 @@ In addition to conditionally including relationship information in your resource /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -654,10 +643,9 @@ If your intermediate table is using an accessor other than `pivot`, you may use /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -676,10 +664,9 @@ Some JSON API standards require the addition of meta data to your resource and r /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'data' => $this->collection, @@ -707,10 +694,9 @@ Sometimes you may wish to only include certain meta data with a resource respons /** * Transform the resource collection into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return parent::toArray($request); } @@ -718,10 +704,9 @@ Sometimes you may wish to only include certain meta data with a resource respons /** * Get additional data that should be returned with the resource array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function with($request) + public function with(Request $request): array { return [ 'meta' => [ @@ -749,7 +734,7 @@ As you have already read, resources may be returned directly from routes and con use App\Http\Resources\UserResource; use App\Models\User; - Route::get('/user/{id}', function ($id) { + Route::get('/user/{id}', function (string $id) { return new UserResource(User::findOrFail($id)); }); @@ -770,6 +755,8 @@ Alternatively, you may define a `withResponse` method within the resource itself namespace App\Http\Resources; + use Illuminate\Http\JsonResponse; + use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource @@ -777,10 +764,9 @@ Alternatively, you may define a `withResponse` method within the resource itself /** * Transform the resource into an array. * - * @param \Illuminate\Http\Request $request - * @return array + * @return array */ - public function toArray($request) + public function toArray(Request $request): array { return [ 'id' => $this->id, @@ -789,12 +775,8 @@ Alternatively, you may define a `withResponse` method within the resource itself /** * Customize the outgoing response for the resource. - * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Http\Response $response - * @return void */ - public function withResponse($request, $response) + public function withResponse(Request $request, JsonResponse $response): void { $response->header('X-Value', 'True'); } diff --git a/eloquent-serialization.md b/eloquent-serialization.md index d1fabbef9d6..578fa911fc7 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -139,8 +139,6 @@ Occasionally, when converting models to arrays or JSON, you may wish to add attr { /** * Determine if the user is an administrator. - * - * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function isAdmin(): Attribute { @@ -189,11 +187,8 @@ You may customize the default serialization format by overriding the `serializeD /** * Prepare a date for array / JSON serialization. - * - * @param \DateTimeInterface $date - * @return string */ - protected function serializeDate(DateTimeInterface $date) + protected function serializeDate(DateTimeInterface $date): string { return $date->format('Y-m-d'); } diff --git a/eloquent.md b/eloquent.md index d2fa4d2f1d1..ede927c60cd 100644 --- a/eloquent.md +++ b/eloquent.md @@ -119,7 +119,7 @@ Models generated by the `make:model` command will be placed in the `app/Models` class Flight extends Model { - // + // ... } @@ -228,10 +228,8 @@ You can override the UUID generation process for a given model by defining a `ne /** * Generate a new UUID for the model. - * - * @return string */ - public function newUniqueId() + public function newUniqueId(): string { return (string) Uuid::uuid4(); } @@ -239,9 +237,9 @@ You can override the UUID generation process for a given model by defining a `ne /** * Get the columns that should receive a unique identifier. * - * @return array + * @return array */ - public function uniqueIds() + public function uniqueIds(): array { return ['id', 'discount_code']; } @@ -371,10 +369,8 @@ use Illuminate\Database\Eloquent\Model; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Model::preventLazyLoading(! $this->app->isProduction()); } @@ -454,7 +450,7 @@ The Eloquent `Collection` class extends Laravel's base `Illuminate\Support\Colle ```php $flights = Flight::where('destination', 'Paris')->get(); -$flights = $flights->reject(function ($flight) { +$flights = $flights->reject(function (Flight $flight) { return $flight->cancelled; }); ``` @@ -478,10 +474,11 @@ The `chunk` method will retrieve a subset of Eloquent models, passing them to a ```php use App\Models\Flight; +use Illuminate\Database\Eloquent\Collection; -Flight::chunk(200, function ($flights) { +Flight::chunk(200, function (Collection $flights) { foreach ($flights as $flight) { - // + // ... } }); ``` @@ -492,7 +489,7 @@ If you are filtering the results of the `chunk` method based on a column that yo ```php Flight::where('departed', true) - ->chunkById(200, function ($flights) { + ->chunkById(200, function (Collection $flights) { $flights->each->update(['departed' => false]); }, $column = 'id'); ``` @@ -506,7 +503,7 @@ The `lazy` method works similarly to [the `chunk` method](#chunking-results) in use App\Models\Flight; foreach (Flight::lazy() as $flight) { - // + // ... } ``` @@ -536,7 +533,7 @@ Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual use App\Models\Flight; foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) { - // + // ... } ``` @@ -545,7 +542,7 @@ The `cursor` returns an `Illuminate\Support\LazyCollection` instance. [Lazy coll ```php use App\Models\User; -$users = User::cursor()->filter(function ($user) { +$users = User::cursor()->filter(function (User $user) { return $user->id > 500; }); @@ -626,7 +623,7 @@ If the `ModelNotFoundException` is not caught, a 404 HTTP response is automatica use App\Models\Flight; - Route::get('/api/flights/{id}', function ($id) { + Route::get('/api/flights/{id}', function (string $id) { return Flight::findOrFail($id); }); @@ -685,16 +682,14 @@ Of course, when using Eloquent, we don't only need to retrieve models from the d use App\Http\Controllers\Controller; use App\Models\Flight; use Illuminate\Http\Request; + use Illuminate\Http\Response; class FlightController extends Controller { /** * Store a new flight in the database. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { // Validate the request... @@ -703,6 +698,8 @@ Of course, when using Eloquent, we don't only need to retrieve models from the d $flight->name = $request->name; $flight->save(); + + return response()->noContent(); } } @@ -886,10 +883,8 @@ If you wish, you may instruct Laravel to throw an exception when attempting to f /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Model::preventSilentlyDiscardingAttributes($this->app->isLocal()); } @@ -995,7 +990,7 @@ Now, when you call the `delete` method on the model, the `deleted_at` column wil To determine if a given model instance has been soft deleted, you may use the `trashed` method: if ($flight->trashed()) { - // + // ... } @@ -1062,6 +1057,7 @@ Sometimes you may want to periodically delete models that are no longer needed. namespace App\Models; + use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Prunable; @@ -1071,10 +1067,8 @@ Sometimes you may want to periodically delete models that are no longer needed. /** * Get the prunable model query. - * - * @return \Illuminate\Database\Eloquent\Builder */ - public function prunable() + public function prunable(): Builder { return static::where('created_at', '<=', now()->subMonth()); } @@ -1084,23 +1078,18 @@ When marking models as `Prunable`, you may also define a `pruning` method on the /** * Prepare the model for pruning. - * - * @return void */ - protected function pruning() + protected function pruning(): void { - // + // ... } After configuring your prunable model, you should schedule the `model:prune` Artisan command in your application's `App\Console\Kernel` class. You are free to choose the appropriate interval at which this command should be run: /** * Define the application's command schedule. - * - * @param \Illuminate\Console\Scheduling\Schedule $schedule - * @return void */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { $schedule->command('model:prune')->daily(); } @@ -1135,6 +1124,7 @@ When models are marked with the `Illuminate\Database\Eloquent\MassPrunable` trai namespace App\Models; + use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\MassPrunable; @@ -1144,10 +1134,8 @@ When models are marked with the `Illuminate\Database\Eloquent\MassPrunable` trai /** * Get the prunable model query. - * - * @return \Illuminate\Database\Eloquent\Builder */ - public function prunable() + public function prunable(): Builder { return static::where('created_at', '<=', now()->subMonth()); } @@ -1215,12 +1203,8 @@ The `Scope` interface requires you to implement one method: `apply`. The `apply` { /** * Apply the scope to a given Eloquent query builder. - * - * @param \Illuminate\Database\Eloquent\Builder $builder - * @param \Illuminate\Database\Eloquent\Model $model - * @return void */ - public function apply(Builder $builder, Model $model) + public function apply(Builder $builder, Model $model): void { $builder->where('created_at', '<', now()->subYears(2000)); } @@ -1245,10 +1229,8 @@ To assign a global scope to a model, you should override the model's `booted` me { /** * The "booted" method of the model. - * - * @return void */ - protected static function booted() + protected static function booted(): void { static::addGlobalScope(new AncientScope); } @@ -1276,10 +1258,8 @@ Eloquent also allows you to define global scopes using closures, which is partic { /** * The "booted" method of the model. - * - * @return void */ - protected static function booted() + protected static function booted(): void { static::addGlobalScope('ancient', function (Builder $builder) { $builder->where('created_at', '<', now()->subYears(2000)); @@ -1319,28 +1299,23 @@ Scopes should always return the same query builder instance or `void`: namespace App\Models; + use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Scope a query to only include popular users. - * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder */ - public function scopePopular($query) + public function scopePopular(Builder $query): void { - return $query->where('votes', '>', 100); + $query->where('votes', '>', 100); } /** * Scope a query to only include active users. - * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return void */ - public function scopeActive($query) + public function scopeActive(Builder $query): void { $query->where('active', 1); } @@ -1380,14 +1355,10 @@ Sometimes you may wish to define a scope that accepts parameters. To get started { /** * Scope a query to only include users of a given type. - * - * @param \Illuminate\Database\Eloquent\Builder $query - * @param mixed $type - * @return \Illuminate\Database\Eloquent\Builder */ - public function scopeOfType($query, $type) + public function scopeOfType(Builder $query, string $type): void { - return $query->where('type', $type); + $query->where('type', $type); } } @@ -1401,17 +1372,17 @@ Once the expected arguments have been added to your scope method's signature, yo Sometimes you may need to determine if two models are the "same" or not. The `is` and `isNot` methods may be used to quickly verify two models have the same primary key, table, and database connection or not: if ($post->is($anotherPost)) { - // + // ... } if ($post->isNot($anotherPost)) { - // + // ... } The `is` and `isNot` methods are also available when using the `belongsTo`, `hasOne`, `morphTo`, and `morphOne` [relationships](/docs/{{version}}/eloquent-relationships). This method is particularly helpful when you would like to compare a related model without issuing a query to retrieve that model: if ($post->author()->is($user)) { - // + // ... } @@ -1470,13 +1441,11 @@ Instead of using custom event classes, you may register closures that execute wh { /** * The "booted" method of the model. - * - * @return void */ - protected static function booted() + protected static function booted(): void { - static::created(function ($user) { - // + static::created(function (User $user) { + // ... }); } } @@ -1485,8 +1454,8 @@ If needed, you may utilize [queueable anonymous event listeners](/docs/{{version use function Illuminate\Events\queueable; - static::created(queueable(function ($user) { - // + static::created(queueable(function (User $user) { + // ... })); @@ -1513,57 +1482,42 @@ This command will place the new observer in your `App/Observers` directory. If t { /** * Handle the User "created" event. - * - * @param \App\Models\User $user - * @return void */ - public function created(User $user) + public function created(User $user): void { - // + // ... } /** * Handle the User "updated" event. - * - * @param \App\Models\User $user - * @return void */ - public function updated(User $user) + public function updated(User $user): void { - // + // ... } /** * Handle the User "deleted" event. - * - * @param \App\Models\User $user - * @return void */ - public function deleted(User $user) + public function deleted(User $user): void { - // + // ... } /** * Handle the User "restored" event. - * - * @param \App\Models\User $user - * @return void */ - public function restored(User $user) + public function restored(User $user): void { - // + // ... } /** * Handle the User "forceDeleted" event. - * - * @param \App\Models\User $user - * @return void */ - public function forceDeleted(User $user) + public function forceDeleted(User $user): void { - // + // ... } } @@ -1574,10 +1528,8 @@ To register an observer, you need to call the `observe` method on the model you /** * Register any events for your application. - * - * @return void */ - public function boot() + public function boot(): void { User::observe(UserObserver::class); } @@ -1621,13 +1573,10 @@ When models are being created within a database transaction, you may want to ins /** * Handle the User "created" event. - * - * @param \App\Models\User $user - * @return void */ - public function created(User $user) + public function created(User $user): void { - // + // ... } } diff --git a/encryption.md b/encryption.md index 42cc97df4d1..b46f17d074c 100644 --- a/encryption.md +++ b/encryption.md @@ -29,21 +29,21 @@ You may encrypt a value using the `encryptString` method provided by the `Crypt` use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Http\Request; + use Illuminate\Http\Response; use Illuminate\Support\Facades\Crypt; class DigitalOceanTokenController extends Controller { /** * Store a DigitalOcean API token for the user. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function storeSecret(Request $request) + public function storeSecret(Request $request): Response { $request->user()->fill([ 'token' => Crypt::encryptString($request->token), ])->save(); + + return response()->noContent(); } } @@ -58,5 +58,5 @@ You may decrypt values using the `decryptString` method provided by the `Crypt` try { $decrypted = Crypt::decryptString($encryptedValue); } catch (DecryptException $e) { - // + // ... } diff --git a/errors.md b/errors.md index 60690789777..d9df978c035 100644 --- a/errors.md +++ b/errors.md @@ -37,20 +37,18 @@ For example, if you need to report different types of exceptions in different wa /** * Register the exception handling callbacks for the application. - * - * @return void */ - public function register() + public function register(): void { $this->reportable(function (InvalidOrderException $e) { - // + // ... }); } When you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application. If you wish to stop the propagation of the exception to the default logging stack, you may use the `stop` method when defining your reporting callback or return `false` from the callback: $this->reportable(function (InvalidOrderException $e) { - // + // ... })->stop(); $this->reportable(function (InvalidOrderException $e) { @@ -68,9 +66,9 @@ If available, Laravel automatically adds the current user's ID to every exceptio /** * Get the default context variables for logging. * - * @return array + * @return array */ - protected function context() + protected function context(): array { return array_merge(parent::context(), [ 'foo' => 'bar', @@ -95,9 +93,9 @@ While adding context to every log message can be useful, sometimes a particular /** * Get the exception's context information. * - * @return array + * @return array */ - public function context() + public function context(): array { return ['order_id' => $this->orderId]; } @@ -108,7 +106,7 @@ While adding context to every log message can be useful, sometimes a particular Sometimes you may need to report an exception but continue handling the current request. The `report` helper function allows you to quickly report an exception via the exception handler without rendering an error page to the user: - public function isValid($value) + public function isValid(string $value): bool { try { // Validate the value... @@ -167,31 +165,29 @@ By default, the Laravel exception handler will convert exceptions into an HTTP r The closure passed to the `renderable` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will deduce what type of exception the closure renders by examining the type-hint of the closure: use App\Exceptions\InvalidOrderException; + use Illuminate\Http\Request; /** * Register the exception handling callbacks for the application. - * - * @return void */ - public function register() + public function register(): void { - $this->renderable(function (InvalidOrderException $e, $request) { + $this->renderable(function (InvalidOrderException $e, Request $request) { return response()->view('errors.invalid-order', [], 500); }); } You may also use the `renderable` method to override the rendering behavior for built-in Laravel or Symfony exceptions such as `NotFoundHttpException`. If the closure given to the `renderable` method does not return a value, Laravel's default exception rendering will be utilized: + use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Register the exception handling callbacks for the application. - * - * @return void */ - public function register() + public function register(): void { - $this->renderable(function (NotFoundHttpException $e, $request) { + $this->renderable(function (NotFoundHttpException $e, Request $request) { if ($request->is('api/*')) { return response()->json([ 'message' => 'Record not found.' @@ -210,26 +206,23 @@ Instead of type-checking exceptions in the exception handler's `register` method namespace App\Exceptions; use Exception; + use Illuminate\Http\Request; + use Illuminate\Http\Response; class InvalidOrderException extends Exception { /** * Report the exception. - * - * @return bool|null */ - public function report() + public function report(): void { - // + // ... } /** * Render the exception into an HTTP response. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function render($request) + public function render(Request $request): Response { return response(/* ... */); } @@ -239,13 +232,13 @@ If your exception extends an exception that is already renderable, such as a bui /** * Render the exception into an HTTP response. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function render($request) + public function render(Request $request): Response|bool { - // Determine if the exception needs custom rendering... + if (/** Determine if the exception needs custom rendering */) { + + return response(/* ... */); + } return false; } @@ -254,12 +247,15 @@ If your exception contains custom reporting logic that is only necessary when ce /** * Report the exception. - * - * @return bool|null */ - public function report() + public function report(): bool { - // Determine if the exception needs custom reporting... + if (/** Determine if the exception needs custom reporting */) { + + // ... + + return true; + } return false; } diff --git a/events.md b/events.md index 10faa6c872a..1161f461c7a 100644 --- a/events.md +++ b/events.md @@ -73,10 +73,8 @@ Typically, events should be registered via the `EventServiceProvider` `$listen` /** * Register any other events for your application. - * - * @return void */ - public function boot() + public function boot(): void { Event::listen( PodcastProcessed::class, @@ -84,7 +82,7 @@ Typically, events should be registered via the `EventServiceProvider` `$listen` ); Event::listen(function (PodcastProcessed $event) { - // + // ... }); } @@ -99,20 +97,18 @@ When registering closure based event listeners manually, you may wrap the listen /** * Register any other events for your application. - * - * @return void */ - public function boot() + public function boot(): void { Event::listen(queueable(function (PodcastProcessed $event) { - // + // ... })); } Like queued jobs, you may use the `onConnection`, `onQueue`, and `delay` methods to customize the execution of the queued listener: Event::listen(queueable(function (PodcastProcessed $event) { - // + // ... })->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. This closure will receive the event instance and the `Throwable` instance that caused the listener's failure: @@ -123,7 +119,7 @@ If you would like to handle anonymous queued listener failures, you may provide use Throwable; Event::listen(queueable(function (PodcastProcessed $event) { - // + // ... })->catch(function (PodcastProcessed $event, Throwable $e) { // The queued listener failed... })); @@ -133,8 +129,8 @@ If you would like to handle anonymous queued listener failures, you may provide You may even register listeners using the `*` as a wildcard parameter, allowing you to catch multiple events on the same listener. Wildcard listeners receive the event name as their first argument and the entire event data array as their second argument: - Event::listen('event.*', function ($eventName, array $data) { - // + Event::listen('event.*', function (string $eventName, array $data) { + // ... }); @@ -150,13 +146,10 @@ Laravel finds event listeners by scanning the listener classes using PHP's refle { /** * Handle the given event. - * - * @param \App\Events\PodcastProcessed $event - * @return void */ - public function handle(PodcastProcessed $event) + public function handle(PodcastProcessed $event): void { - // + // ... } } @@ -164,10 +157,8 @@ Event discovery is disabled by default, but you can enable it by overriding the /** * Determine if events and listeners should be automatically discovered. - * - * @return bool */ - public function shouldDiscoverEvents() + public function shouldDiscoverEvents(): bool { return true; } @@ -177,9 +168,9 @@ By default, all listeners within your application's `app/Listeners` directory wi /** * Get the listener directories that should be used to discover events. * - * @return array + * @return array */ - protected function discoverEventsWithin() + protected function discoverEventsWithin(): array { return [ $this->app->path('Listeners'), @@ -218,9 +209,6 @@ An event class is essentially a data container which holds the information relat /** * Create a new event instance. - * - * @param \App\Models\Order $order - * @return void */ public function __construct(Order $order) { @@ -245,21 +233,16 @@ Next, let's take a look at the listener for our example event. Event listeners r { /** * Create the event listener. - * - * @return void */ public function __construct() { - // + // ... } /** * Handle the event. - * - * @param \App\Events\OrderShipped $event - * @return void */ - public function handle(OrderShipped $event) + public function handle(OrderShipped $event): void { // Access the order using $event->order... } @@ -289,7 +272,7 @@ To specify that a listener should be queued, add the `ShouldQueue` interface to class SendShipmentNotification implements ShouldQueue { - // + // ... } That's it! Now, when an event handled by this listener is dispatched, the listener will automatically be queued by the event dispatcher using Laravel's [queue system](/docs/{{version}}/queues). If no exceptions are thrown when the listener is executed by the queue, the queued job will automatically be deleted after it has finished processing. @@ -334,20 +317,16 @@ If you would like to define the listener's queue connection or queue name at run /** * Get the name of the listener's queue connection. - * - * @return string */ - public function viaConnection() + public function viaConnection(): string { return 'sqs'; } /** * Get the name of the listener's queue. - * - * @return string */ - public function viaQueue() + public function viaQueue(): string { return 'listeners'; } @@ -368,22 +347,16 @@ Sometimes, you may need to determine whether a listener should be queued based o { /** * Reward a gift card to the customer. - * - * @param \App\Events\OrderCreated $event - * @return void */ - public function handle(OrderCreated $event) + public function handle(OrderCreated $event): void { - // + // ... } /** * Determine whether the listener should be queued. - * - * @param \App\Events\OrderCreated $event - * @return bool */ - public function shouldQueue(OrderCreated $event) + public function shouldQueue(OrderCreated $event): bool { return $event->order->subtotal >= 5000; } @@ -408,11 +381,8 @@ If you need to manually access the listener's underlying queue job's `delete` an /** * Handle the event. - * - * @param \App\Events\OrderShipped $event - * @return void */ - public function handle(OrderShipped $event) + public function handle(OrderShipped $event): void { if (true) { $this->release(30); @@ -456,6 +426,7 @@ Sometimes your queued event listeners may fail. If the queued listener exceeds t use App\Events\OrderShipped; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; + use Throwable; class SendShipmentNotification implements ShouldQueue { @@ -463,25 +434,18 @@ Sometimes your queued event listeners may fail. If the queued listener exceeds t /** * Handle the event. - * - * @param \App\Events\OrderShipped $event - * @return void */ - public function handle(OrderShipped $event) + public function handle(OrderShipped $event): void { - // + // ... } /** * Handle a job failure. - * - * @param \App\Events\OrderShipped $event - * @param \Throwable $exception - * @return void */ - public function failed(OrderShipped $event, $exception) + public function failed(OrderShipped $event, Throwable $exception): void { - // + // ... } } @@ -514,12 +478,12 @@ You may define a `$tries` property on your listener class to specify how many ti As an alternative to defining how many times a listener may be attempted before it fails, you may define a time at which the listener should no longer be attempted. This allows a listener to be attempted any number of times within a given time frame. To define the time at which a listener should no longer be attempted, add a `retryUntil` method to your listener class. This method should return a `DateTime` instance: + use DateTime; + /** * Determine the time at which the listener should timeout. - * - * @return \DateTime */ - public function retryUntil() + public function retryUntil(): DateTime { return now()->addMinutes(5); } @@ -537,22 +501,22 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th use App\Http\Controllers\Controller; use App\Models\Order; use Illuminate\Http\Request; + use Illuminate\Http\Response; class OrderShipmentController extends Controller { /** * Ship the given order. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $order = Order::findOrFail($request->order_id); // Order shipment logic... OrderShipped::dispatch($order); + + return response()->noContent(); } } @@ -579,26 +543,24 @@ Event subscribers are classes that may subscribe to multiple events from within use Illuminate\Auth\Events\Login; use Illuminate\Auth\Events\Logout; + use Illuminate\Events\Dispatcher; class UserEventSubscriber { /** * Handle user login events. */ - public function handleUserLogin($event) {} + public function handleUserLogin(string $event): void {} /** * Handle user logout events. */ - public function handleUserLogout($event) {} + public function handleUserLogout(string $event): void {} /** * Register the listeners for the subscriber. - * - * @param \Illuminate\Events\Dispatcher $events - * @return void */ - public function subscribe($events) + public function subscribe(Dispatcher $events): void { $events->listen( Login::class, @@ -620,26 +582,26 @@ If your event listener methods are defined within the subscriber itself, you may use Illuminate\Auth\Events\Login; use Illuminate\Auth\Events\Logout; + use Illuminate\Events\Dispatcher; class UserEventSubscriber { /** * Handle user login events. */ - public function handleUserLogin($event) {} + public function handleUserLogin(string $event): void {} /** * Handle user logout events. */ - public function handleUserLogout($event) {} + public function handleUserLogout(string $event): void {} /** * Register the listeners for the subscriber. * - * @param \Illuminate\Events\Dispatcher $events - * @return array + * @return array */ - public function subscribe($events) + public function subscribe(Dispatcher $events): array { return [ Login::class => 'handleUserLogin', @@ -668,7 +630,7 @@ After writing the subscriber, you are ready to register it with the event dispat * @var array */ protected $listen = [ - // + // ... ]; /** diff --git a/facades.md b/facades.md index 0744ae0d0ed..4cb4db031c7 100644 --- a/facades.md +++ b/facades.md @@ -73,10 +73,8 @@ Using Laravel's facade testing methods, we can write the following test to verif /** * A basic functional test example. - * - * @return void */ - public function testBasicExample() + public function test_basic_example(): void { Cache::shouldReceive('get') ->with('key') @@ -108,10 +106,8 @@ The `cache` helper is going to call the `get` method on the class underlying the /** * A basic functional test example. - * - * @return void */ - public function testBasicExample() + public function test_basic_example(): void { Cache::shouldReceive('get') ->with('key') @@ -135,16 +131,14 @@ The `Facade` base class makes use of the `__callStatic()` magic-method to defer use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Cache; + use Illuminate\View\View; class UserController extends Controller { /** * Show the profile for the given user. - * - * @param int $id - * @return Response */ - public function showProfile($id) + public function showProfile(string $id): View { $user = Cache::get('user:'.$id); @@ -160,10 +154,11 @@ If we look at that `Illuminate\Support\Facades\Cache` class, you'll see that the { /** * Get the registered name of the component. - * - * @return string */ - protected static function getFacadeAccessor() { return 'cache'; } + protected static function getFacadeAccessor(): string + { + return 'cache'; + } } Instead, the `Cache` facade extends the base `Facade` class and defines the method `getFacadeAccessor()`. This method's job is to return the name of a service container binding. When a user references any static method on the `Cache` facade, Laravel resolves the `cache` binding from the [service container](/docs/{{version}}/container) and runs the requested method (in this case, `get`) against that object. @@ -184,11 +179,8 @@ Using real-time facades, you may treat any class in your application as if it wa { /** * Publish the podcast. - * - * @param Publisher $publisher - * @return void */ - public function publish(Publisher $publisher) + public function publish(Publisher $publisher): void { $this->update(['publishing' => now()]); @@ -209,10 +201,8 @@ Injecting a publisher implementation into the method allows us to easily test th { /** * Publish the podcast. - * - * @return void */ - public function publish() + public function publish(): void { $this->update(['publishing' => now()]); @@ -237,10 +227,8 @@ When the real-time facade is used, the publisher implementation will be resolved /** * A test example. - * - * @return void */ - public function test_podcast_can_be_published() + public function test_podcast_can_be_published(): void { $podcast = Podcast::factory()->create(); diff --git a/filesystem.md b/filesystem.md index c82b29ffe40..2ac2cb80c01 100644 --- a/filesystem.md +++ b/filesystem.md @@ -286,6 +286,7 @@ If you need to customize how temporary URLs are created for a specific storage d namespace App\Providers; + use DateTime; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; @@ -294,12 +295,10 @@ If you need to customize how temporary URLs are created for a specific storage d { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - Storage::disk('local')->buildTemporaryUrlsUsing(function ($path, $expiration, $options) { + Storage::disk('local')->buildTemporaryUrlsUsing(function (string $path, DateTime $expiration, array $options) { return URL::temporarySignedRoute( 'files.download', $expiration, @@ -425,11 +424,8 @@ In web applications, one of the most common use-cases for storing files is stori { /** * Update the avatar for the user. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function update(Request $request) + public function update(Request $request): string { $path = $request->file('avatar')->store('avatars'); @@ -613,6 +609,7 @@ Next, you can register the driver within the `boot` method of one of your applic namespace App\Providers; + use Illuminate\Contracts\Foundation\Application; use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Support\Facades\Storage; use Illuminate\Support\ServiceProvider; @@ -624,22 +621,18 @@ Next, you can register the driver within the `boot` method of one of your applic { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - Storage::extend('dropbox', function ($app, $config) { + Storage::extend('dropbox', function (Application $app, array $config) { $adapter = new DropboxAdapter(new DropboxClient( $config['authorization_token'] )); diff --git a/fortify.md b/fortify.md index f811d79bc97..ddb88916361 100644 --- a/fortify.md +++ b/fortify.md @@ -134,10 +134,8 @@ All of the authentication view's rendering logic may be customized using the app /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Fortify::loginView(function () { return view('auth.login'); @@ -167,10 +165,8 @@ use Laravel\Fortify\Fortify; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Fortify::authenticateUsing(function (Request $request) { $user = User::where('email', $request->email)->first(); @@ -226,16 +222,16 @@ If you need advanced customization of this behavior, you may bind implementation ```php use Laravel\Fortify\Contracts\LogoutResponse; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; /** * Register any application services. - * - * @return void */ -public function register() +public function register(): void { $this->app->instance(LogoutResponse::class, new class implements LogoutResponse { - public function toResponse($request) + public function toResponse(Request $request): RedirectResponse { return redirect('/'); } @@ -334,10 +330,8 @@ use Laravel\Fortify\Fortify; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Fortify::twoFactorChallengeView(function () { return view('auth.two-factor-challenge'); @@ -370,10 +364,8 @@ use Laravel\Fortify\Fortify; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Fortify::registerView(function () { return view('auth.register'); @@ -411,10 +403,8 @@ use Laravel\Fortify\Fortify; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Fortify::requestPasswordResetLinkView(function () { return view('auth.forgot-password'); @@ -454,15 +444,14 @@ All of Fortify's view rendering logic may be customized using the appropriate me ```php use Laravel\Fortify\Fortify; +use Illuminate\Http\Request; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { - Fortify::resetPasswordView(function ($request) { + Fortify::resetPasswordView(function (Request $request) { return view('auth.reset-password', ['request' => $request]); }); @@ -510,10 +499,8 @@ use Laravel\Fortify\Fortify; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Fortify::verifyEmailView(function () { return view('auth.verify-email'); @@ -567,10 +554,8 @@ use Laravel\Fortify\Fortify; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Fortify::confirmPasswordView(function () { return view('auth.confirm-password'); diff --git a/frontend.md b/frontend.md index 50174564a90..cce5b1a053f 100644 --- a/frontend.md +++ b/frontend.md @@ -128,16 +128,14 @@ namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Models\User; use Inertia\Inertia; +use Inertia\Response; class UserController extends Controller { /** * Show the profile for a given user. - * - * @param int $id - * @return \Inertia\Response */ - public function show($id) + public function show(string $id): Response { return Inertia::render('Users/Profile', [ 'user' => User::findOrFail($id) diff --git a/hashing.md b/hashing.md index d3f278d254f..91c5109c1f5 100644 --- a/hashing.md +++ b/hashing.md @@ -33,23 +33,23 @@ You may hash a password by calling the `make` method on the `Hash` facade: use App\Http\Controllers\Controller; use Illuminate\Http\Request; + use Illuminate\Http\Response; use Illuminate\Support\Facades\Hash; class PasswordController extends Controller { /** * Update the password for the user. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function update(Request $request) + public function update(Request $request): Response { // Validate the new password length... $request->user()->fill([ 'password' => Hash::make($request->newPassword) ])->save(); + + return response()->noContent(); } } diff --git a/helpers.md b/helpers.md index ef6082f0930..4e4ce7f7752 100644 --- a/helpers.md +++ b/helpers.md @@ -502,7 +502,7 @@ The `Arr::first` method returns the first element of an array passing a given tr $array = [100, 200, 300]; - $first = Arr::first($array, function ($value, $key) { + $first = Arr::first($array, function (int $value, int $key) { return $value >= 150; }); @@ -676,7 +676,7 @@ The `Arr::last` method returns the last element of an array passing a given trut $array = [100, 200, 300, 110]; - $last = Arr::last($array, function ($value, $key) { + $last = Arr::last($array, function (int $value, int $key) { return $value >= 150; }); @@ -697,7 +697,7 @@ The `Arr::map` method iterates through the array and passes each value and key t $array = ['first' => 'james', 'last' => 'kirk']; - $mapped = Arr::map($array, function ($value, $key) { + $mapped = Arr::map($array, function (string $value, string $key) { return ucfirst($value); }); @@ -892,7 +892,7 @@ You may also sort the array by the results of a given closure: ['name' => 'Chair'], ]; - $sorted = array_values(Arr::sort($array, function ($value) { + $sorted = array_values(Arr::sort($array, function (array $value) { return $value['name']; })); @@ -972,7 +972,7 @@ The `Arr::where` method filters an array using the given closure: $array = [100, '200', 300, '400', 500]; - $filtered = Arr::where($array, function ($value, $key) { + $filtered = Arr::where($array, function (string|int $value, int $key) { return is_string($value); }); @@ -2727,12 +2727,13 @@ The `padRight` method wraps PHP's `str_pad` function, padding the right side of The `pipe` method allows you to transform the string by passing its current value to the given callable: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; $hash = Str::of('Laravel')->pipe('md5')->prepend('Checksum: '); // 'Checksum: a5c95b86291ea299fcbe64458ed12702' - $closure = Str::of('foo')->pipe(function ($str) { + $closure = Str::of('foo')->pipe(function (Stringable $str) { return 'bar'; }); @@ -2849,8 +2850,9 @@ The `replaceMatches` method replaces all portions of a string matching a pattern The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given pattern, allowing you to perform the replacement logic within the closure and return the replaced value: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $replaced = Str::of('123')->replaceMatches('/\d/', function ($match) { + $replaced = Str::of('123')->replaceMatches('/\d/', function (Stringable $match) { return '['.$match[0].']'; }); @@ -3029,10 +3031,11 @@ The `swap` method replaces multiple values in the string using PHP's `strtr` fun The `tap` method passes the string to the given closure, allowing you to examine and interact with the string while not affecting the string itself. The original string is returned by the `tap` method regardless of what is returned by the closure: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; $string = Str::of('Laravel') ->append(' Framework') - ->tap(function ($string) { + ->tap(function (Stringable $string) { dump('String after append: '.$string); }) ->upper(); @@ -3115,9 +3118,10 @@ The `upper` method converts the given string to uppercase: The `when` method invokes the given closure if a given condition is `true`. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; $string = Str::of('Taylor') - ->when(true, function ($string) { + ->when(true, function (Stringable $string) { return $string->append(' Otwell'); }); @@ -3131,9 +3135,10 @@ If necessary, you may pass another closure as the third parameter to the `when` The `whenContains` method invokes the given closure if the string contains the given value. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; $string = Str::of('tony stark') - ->whenContains('tony', function ($string) { + ->whenContains('tony', function (Stringable $string) { return $string->title(); }); @@ -3144,9 +3149,10 @@ If necessary, you may pass another closure as the third parameter to the `when` You may also pass an array of values to determine if the given string contains any of the values in the array: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; $string = Str::of('tony stark') - ->whenContains(['tony', 'hulk'], function ($string) { + ->whenContains(['tony', 'hulk'], function (Stringable $string) { return $string->title(); }); @@ -3158,9 +3164,10 @@ You may also pass an array of values to determine if the given string contains a The `whenContainsAll` method invokes the given closure if the string contains all of the given sub-strings. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; $string = Str::of('tony stark') - ->whenContainsAll(['tony', 'stark'], function ($string) { + ->whenContainsAll(['tony', 'stark'], function (Stringable $string) { return $string->title(); }); @@ -3174,8 +3181,9 @@ If necessary, you may pass another closure as the third parameter to the `when` The `whenEmpty` method invokes the given closure if the string is empty. If the closure returns a value, that value will also be returned by the `whenEmpty` method. If the closure does not return a value, the fluent string instance will be returned: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of(' ')->whenEmpty(function ($string) { + $string = Str::of(' ')->whenEmpty(function (Stringable $string) { return $string->trim()->prepend('Laravel'); }); @@ -3187,8 +3195,9 @@ The `whenEmpty` method invokes the given closure if the string is empty. If the The `whenNotEmpty` method invokes the given closure if the string is not empty. If the closure returns a value, that value will also be returned by the `whenNotEmpty` method. If the closure does not return a value, the fluent string instance will be returned: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('Framework')->whenNotEmpty(function ($string) { + $string = Str::of('Framework')->whenNotEmpty(function (Stringable $string) { return $string->prepend('Laravel '); }); @@ -3200,8 +3209,9 @@ The `whenNotEmpty` method invokes the given closure if the string is not empty. The `whenStartsWith` method invokes the given closure if the string starts with the given sub-string. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('disney world')->whenStartsWith('disney', function ($string) { + $string = Str::of('disney world')->whenStartsWith('disney', function (Stringable $string) { return $string->title(); }); @@ -3213,8 +3223,9 @@ The `whenStartsWith` method invokes the given closure if the string starts with The `whenEndsWith` method invokes the given closure if the string ends with the given sub-string. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('disney world')->whenEndsWith('world', function ($string) { + $string = Str::of('disney world')->whenEndsWith('world', function (Stringable $string) { return $string->title(); }); @@ -3226,8 +3237,9 @@ The `whenEndsWith` method invokes the given closure if the string ends with the The `whenExactly` method invokes the given closure if the string exactly matches the given string. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('laravel')->whenExactly('laravel', function ($string) { + $string = Str::of('laravel')->whenExactly('laravel', function (Stringable $string) { return $string->title(); }); @@ -3239,8 +3251,9 @@ The `whenExactly` method invokes the given closure if the string exactly matches The `whenNotExactly` method invokes the given closure if the string does not exactly match the given string. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('framework')->whenNotExactly('laravel', function ($string) { + $string = Str::of('framework')->whenNotExactly('laravel', function (Stringable $string) { return $string->title(); }); @@ -3252,8 +3265,9 @@ The `whenNotExactly` method invokes the given closure if the string does not exa The `whenIs` method invokes the given closure if the string matches a given pattern. Asterisks may be used as wildcard values. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('foo/bar')->whenIs('foo/*', function ($string) { + $string = Str::of('foo/bar')->whenIs('foo/*', function (Stringable $string) { return $string->append('/baz'); }); @@ -3265,8 +3279,9 @@ The `whenIs` method invokes the given closure if the string matches a given patt The `whenIsAscii` method invokes the given closure if the string is 7 bit ASCII. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('laravel')->whenIsAscii(function ($string) { + $string = Str::of('laravel')->whenIsAscii(function (Stringable $string) { return $string->title(); }); @@ -3279,7 +3294,7 @@ The `whenIsUlid` method invokes the given closure if the string is a valid ULID. use Illuminate\Support\Str; - $string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function ($string) { + $string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function (Stringable $string) { return $string->substr(0, 8); }); @@ -3291,8 +3306,9 @@ The `whenIsUlid` method invokes the given closure if the string is a valid ULID. The `whenIsUuid` method invokes the given closure if the string is a valid UUID. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function ($string) { + $string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function (Stringable $string) { return $string->substr(0, 8); }); @@ -3304,8 +3320,9 @@ The `whenIsUuid` method invokes the given closure if the string is a valid UUID. The `whenTest` method invokes the given closure if the string matches the given regular expression. The closure will receive the fluent string instance: use Illuminate\Support\Str; + use Illuminate\Support\Stringable; - $string = Str::of('laravel framework')->whenTest('/laravel/', function ($string) { + $string = Str::of('laravel framework')->whenTest('/laravel/', function (Stringable $string) { return $string->title(); }); @@ -3756,7 +3773,7 @@ The `optional` function accepts any argument and allows you to access properties The `optional` function also accepts a closure as its second argument. The closure will be invoked if the value provided as the first argument is not null: - return optional(User::find($id), function ($user) { + return optional(User::find($id), function (User $user) { return $user->name; }); @@ -3864,9 +3881,11 @@ The `retry` function attempts to execute the given callback until the given maxi If you would like to manually calculate the number of milliseconds to sleep between attempts, you may pass a closure as the third argument to the `retry` function: + use Exception; + return retry(5, function () { // ... - }, function ($attempt, $exception) { + }, function (int $attempt, Exception $exception) { return $attempt * 100; }); @@ -3878,9 +3897,11 @@ For convenience, you may provide an array as the first argument to the `retry` f To only retry under specific conditions, you may pass a closure as the fourth argument to the `retry` function: + use Exception; + return retry(5, function () { // ... - }, 100, function ($exception) { + }, 100, function (Exception $exception) { return $exception instanceof RetryException; }); @@ -3906,7 +3927,7 @@ The session store will be returned if no value is passed to the function: The `tap` function accepts two arguments: an arbitrary `$value` and a closure. The `$value` will be passed to the closure and then be returned by the `tap` function. The return value of the closure is irrelevant: - $user = tap(User::first(), function ($user) { + $user = tap(User::first(), function (User $user) { $user->name = 'taylor'; $user->save(); @@ -3921,8 +3942,8 @@ If no closure is passed to the `tap` function, you may call any method on the gi To add a `tap` method to a class, you may add the `Illuminate\Support\Traits\Tappable` trait to the class. The `tap` method of this trait accepts a Closure as its only argument. The object instance itself will be passed to the Closure and then be returned by the `tap` method: - return $user->tap(function ($user) { - // + return $user->tap(function (User $user) { + // ... }); @@ -3970,7 +3991,7 @@ The `trait_uses_recursive` function returns all traits used by a trait: The `transform` function executes a closure on a given value if the value is not [blank](#method-blank) and then returns the return value of the closure: - $callback = function ($value) { + $callback = function (int $value) { return $value * 2; }; @@ -4018,7 +4039,7 @@ The `view` function retrieves a [view](/docs/{{version}}/views) instance: The `with` function returns the value it is given. If a closure is passed as the second argument to the function, the closure will be executed and its returned value will be returned: - $callback = function ($value) { + $callback = function (mixed $value) { return is_numeric($value) ? $value * 2 : 0; }; diff --git a/horizon.md b/horizon.md index b83ee868230..b94d401718d 100644 --- a/horizon.md +++ b/horizon.md @@ -129,12 +129,10 @@ Horizon exposes a dashboard at the `/horizon` URI. By default, you will only be * Register the Horizon gate. * * This gate determines who can access Horizon in non-local environments. - * - * @return void */ - protected function gate() + protected function gate(): void { - Gate::define('viewHorizon', function ($user) { + Gate::define('viewHorizon', function (User $user) { return in_array($user->email, [ 'taylor@laravel.com', ]); @@ -294,9 +292,6 @@ Horizon allows you to assign “tags” to jobs, including mailables, broadcast /** * Create a new job instance. - * - * @param \App\Models\Video $video - * @return void */ public function __construct(Video $video) { @@ -305,12 +300,10 @@ Horizon allows you to assign “tags” to jobs, including mailables, broadcast /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { - // + // ... } } @@ -333,9 +326,9 @@ If you would like to manually define the tags for one of your queueable objects, /** * Get the tags that should be assigned to the job. * - * @return array + * @return array */ - public function tags() + public function tags(): array { return ['render', 'video:'.$this->video->id]; } @@ -351,10 +344,8 @@ If you would like to be notified when one of your queues has a long wait time, y /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { parent::boot(); @@ -380,11 +371,8 @@ Horizon includes a metrics dashboard which provides information regarding your j /** * Define the application's command schedule. - * - * @param \Illuminate\Console\Scheduling\Schedule $schedule - * @return void */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { $schedule->command('horizon:snapshot')->everyFiveMinutes(); } diff --git a/http-client.md b/http-client.md index df368b092e9..c4380c3f9d4 100644 --- a/http-client.md +++ b/http-client.md @@ -183,13 +183,19 @@ If you would like the HTTP client to automatically retry the request if a client If needed, you may pass a third argument to the `retry` method. The third argument should be a callable that determines if the retries should actually be attempted. For example, you may wish to only retry the request if the initial request encounters an `ConnectionException`: - $response = Http::retry(3, 100, function ($exception, $request) { + use Exception; + use Illuminate\Http\Client\PendingRequest; + + $response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) { return $exception instanceof ConnectionException; })->post(/* ... */); If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying the request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: - $response = Http::withToken($this->getToken())->retry(2, 0, function ($exception, $request) { + use Exception; + use Illuminate\Http\Client\PendingRequest; + + $response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) { if (! $exception instanceof RequestException || $exception->response->status() !== 401) { return false; } @@ -231,6 +237,8 @@ Unlike Guzzle's default behavior, Laravel's HTTP client wrapper does not throw e If you have a response instance and would like to throw an instance of `Illuminate\Http\Client\RequestException` if the response status code indicates a client or server error, you may use the `throw` or `throwIf` methods: + use Illuminate\Http\Client\Response; + $response = Http::post(/* ... */); // Throw an exception if a client or server error occurred... @@ -240,13 +248,13 @@ If you have a response instance and would like to throw an instance of `Illumina $response->throwIf($condition); // Throw an exception if an error occurred and the given closure resolves to true... - $response->throwIf(fn ($response) => true); + $response->throwIf(fn (Response $response) => true); // Throw an exception if an error occurred and the given condition is false... $response->throwUnless($condition); // Throw an exception if an error occurred and the given closure resolves to false... - $response->throwUnless(fn ($response) => false); + $response->throwUnless(fn (Response $response) => false); return $response['user']['id']; @@ -258,8 +266,11 @@ The `throw` method returns the response instance if no error occurred, allowing If you would like to perform some additional logic before the exception is thrown, you may pass a closure to the `throw` method. The exception will be thrown automatically after the closure is invoked, so you do not need to re-throw the exception from within the closure: - return Http::post(/* ... */)->throw(function ($response, $e) { - // + use Illuminate\Http\Client\Response; + use Illuminate\Http\Client\RequestException; + + return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) { + // ... })->json(); @@ -347,10 +358,8 @@ use Illuminate\Support\Facades\Http; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Http::macro('github', function () { return Http::withHeaders([ diff --git a/http-tests.md b/http-tests.md index 8426cc1de8d..20d484b5552 100644 --- a/http-tests.md +++ b/http-tests.md @@ -33,10 +33,8 @@ Laravel provides a very fluent API for making HTTP requests to your application { /** * A basic test example. - * - * @return void */ - public function test_a_basic_request() + public function test_a_basic_request(): void { $response = $this->get('/'); @@ -65,10 +63,8 @@ Instead of returning an `Illuminate\Http\Response` instance, test request method { /** * A basic test example. - * - * @return void */ - public function test_a_basic_request() + public function test_a_basic_request(): void { $response = $this->get('/'); @@ -96,10 +92,8 @@ You may use the `withHeaders` method to customize the request's headers before i { /** * A basic functional test example. - * - * @return void */ - public function test_interacting_with_headers() + public function test_interacting_with_headers(): void { $response = $this->withHeaders([ 'X-Header' => 'Value', @@ -122,7 +116,7 @@ You may use the `withCookie` or `withCookies` methods to set cookie values befor class ExampleTest extends TestCase { - public function test_interacting_with_cookies() + public function test_interacting_with_cookies(): void { $response = $this->withCookie('color', 'blue')->get('/'); @@ -146,7 +140,7 @@ Laravel provides several helpers for interacting with the session during HTTP te class ExampleTest extends TestCase { - public function test_interacting_with_the_session() + public function test_interacting_with_the_session(): void { $response = $this->withSession(['banned' => false])->get('/'); } @@ -163,7 +157,7 @@ Laravel's session is typically used to maintain state for the currently authenti class ExampleTest extends TestCase { - public function test_an_action_that_requires_authentication() + public function test_an_action_that_requires_authentication(): void { $user = User::factory()->create(); @@ -192,10 +186,8 @@ After making a test request to your application, the `dump`, `dumpHeaders`, and { /** * A basic test example. - * - * @return void */ - public function test_basic_test() + public function test_basic_test(): void { $response = $this->get('/'); @@ -219,10 +211,8 @@ Alternatively, you may use the `dd`, `ddHeaders`, and `ddSession` methods to dum { /** * A basic test example. - * - * @return void */ - public function test_basic_test() + public function test_basic_test(): void { $response = $this->get('/'); @@ -260,10 +250,8 @@ Laravel also provides several helpers for testing JSON APIs and their responses. { /** * A basic functional test example. - * - * @return void */ - public function test_making_an_api_request() + public function test_making_an_api_request(): void { $response = $this->postJson('/api/user', ['name' => 'Sally']); @@ -297,10 +285,8 @@ As previously mentioned, the `assertJson` method may be used to assert that a fr { /** * A basic functional test example. - * - * @return void */ - public function test_asserting_an_exact_json_match() + public function test_asserting_an_exact_json_match(): void { $response = $this->postJson('/user', ['name' => 'Sally']); @@ -327,10 +313,8 @@ If you would like to verify that the JSON response contains the given data at a { /** * A basic functional test example. - * - * @return void */ - public function test_asserting_a_json_paths_value() + public function test_asserting_a_json_paths_value(): void { $response = $this->postJson('/user', ['name' => 'Sally']); @@ -342,7 +326,7 @@ If you would like to verify that the JSON response contains the given data at a The `assertJsonPath` method also accepts a closure, which may be used to dynamically determine if the assertion should pass: - $response->assertJsonPath('team.owner.name', fn ($name) => strlen($name) >= 3); + $response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3); ### Fluent JSON Testing @@ -353,10 +337,8 @@ Laravel also offers a beautiful way to fluently test your application's JSON res /** * A basic functional test example. - * - * @return void */ - public function test_fluent_json() + public function test_fluent_json(): void { $response = $this->getJson('/users/1'); @@ -364,7 +346,7 @@ Laravel also offers a beautiful way to fluently test your application's JSON res ->assertJson(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') - ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) ->whereNot('status', 'pending') ->missing('password') ->etc() @@ -417,10 +399,10 @@ In these situations, we may use the fluent JSON object's `has` method to make as $response ->assertJson(fn (AssertableJson $json) => $json->has(3) - ->first(fn ($json) => + ->first(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') - ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -444,10 +426,10 @@ When testing these routes, you may use the `has` method to assert against the nu ->assertJson(fn (AssertableJson $json) => $json->has('meta') ->has('users', 3) - ->has('users.0', fn ($json) => + ->has('users.0', fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') - ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -458,10 +440,10 @@ However, instead of making two separate calls to the `has` method to assert agai $response ->assertJson(fn (AssertableJson $json) => $json->has('meta') - ->has('users', 3, fn ($json) => + ->has('users', 3, fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') - ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -506,7 +488,7 @@ The `Illuminate\Http\UploadedFile` class provides a `fake` method which may be u class ExampleTest extends TestCase { - public function test_avatars_can_be_uploaded() + public function test_avatars_can_be_uploaded(): void { Storage::fake('avatars'); @@ -558,7 +540,7 @@ Laravel also allows you to render a view without making a simulated HTTP request class ExampleTest extends TestCase { - public function test_a_welcome_view_can_be_rendered() + public function test_a_welcome_view_can_be_rendered(): void { $view = $this->view('welcome', ['name' => 'Taylor']); diff --git a/localization.md b/localization.md index 784b53dc382..a94f45c60e7 100644 --- a/localization.md +++ b/localization.md @@ -41,14 +41,14 @@ You may modify the default language for a single HTTP request at runtime using t use Illuminate\Support\Facades\App; - Route::get('/greeting/{locale}', function ($locale) { + Route::get('/greeting/{locale}', function (string $locale) { if (! in_array($locale, ['en', 'es', 'fr'])) { abort(400); } App::setLocale($locale); - // + // ... }); You may configure a "fallback language", which will be used when the active language does not contain a given translation string. Like the default language, the fallback language is also configured in the `config/app.php` configuration file: @@ -65,7 +65,7 @@ You may use the `currentLocale` and `isLocale` methods on the `App` facade to de $locale = App::currentLocale(); if (App::isLocale('en')) { - // + // ... } @@ -77,10 +77,8 @@ You may instruct Laravel's "pluralizer", which is used by Eloquent and other por /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Pluralizer::useLanguage('spanish'); diff --git a/logging.md b/logging.md index 6986755e2b5..228f399fdba 100644 --- a/logging.md +++ b/logging.md @@ -179,16 +179,14 @@ You may call any of these methods to log a message for the corresponding level. use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Support\Facades\Log; + use Illuminate\View\View; class UserController extends Controller { /** * Show the profile for the given user. - * - * @param int $id - * @return \Illuminate\Http\Response */ - public function show($id) + public function show(string $id): View { Log::info('Showing the user profile for user: '.$id); @@ -214,19 +212,19 @@ Occasionally, you may wish to specify some contextual information that should be namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; + use Symfony\Component\HttpFoundation\Response; class AssignRequestId { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { $requestId = (string) Str::uuid(); @@ -247,10 +245,8 @@ If you would like to share contextual information across _all_ logging channels, { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Log::shareContext([ 'invocation-id' => (string) Str::uuid(), @@ -317,17 +313,15 @@ Once you have configured the `tap` option on your channel, you're ready to defin namespace App\Logging; + use Illuminate\Log\Logger; use Monolog\Formatter\LineFormatter; class CustomizeFormatter { /** * Customize the given logger instance. - * - * @param \Illuminate\Log\Logger $logger - * @return void */ - public function __invoke($logger) + public function __invoke(Logger $logger): void { foreach ($logger->getHandlers() as $handler) { $handler->setFormatter(new LineFormatter( @@ -402,11 +396,8 @@ Once you have configured the `custom` driver channel, you're ready to define the { /** * Create a custom Monolog instance. - * - * @param array $config - * @return \Monolog\Logger */ - public function __invoke(array $config) + public function __invoke(array $config): Logger { return new Logger(/* ... */); } diff --git a/mail.md b/mail.md index 51d681044f9..227662214fc 100644 --- a/mail.md +++ b/mail.md @@ -188,10 +188,8 @@ First, let's explore configuring the sender of the email. Or, in other words, wh /** * Get the message envelope. - * - * @return \Illuminate\Mail\Mailables\Envelope */ - public function envelope() + public function envelope(): Envelope { return new Envelope( from: new Address('jeffrey@example.com', 'Jeffrey Way'), @@ -227,10 +225,8 @@ Within a mailable class' `content` method, you may define the `view`, or which t /** * Get the message content definition. - * - * @return \Illuminate\Mail\Mailables\Content */ - public function content() + public function content(): Content { return new Content( view: 'emails.orders.shipped', @@ -247,10 +243,8 @@ If you would like to define a plain-text version of your email, you may specify /** * Get the message content definition. - * - * @return \Illuminate\Mail\Mailables\Content */ - public function content() + public function content(): Content { return new Content( view: 'emails.orders.shipped', @@ -296,9 +290,6 @@ Typically, you will want to pass some data to your view that you can utilize whe /** * Create a new message instance. - * - * @param \App\Models\Order $order - * @return void */ public function __construct(Order $order) { @@ -307,10 +298,8 @@ Typically, you will want to pass some data to your view that you can utilize whe /** * Get the message content definition. - * - * @return \Illuminate\Mail\Mailables\Content */ - public function content() + public function content(): Content { return new Content( view: 'emails.orders.shipped', @@ -352,9 +341,6 @@ If you would like to customize the format of your email's data before it is sent /** * Create a new message instance. - * - * @param \App\Models\Order $order - * @return void */ public function __construct(Order $order) { @@ -363,10 +349,8 @@ If you would like to customize the format of your email's data before it is sent /** * Get the message content definition. - * - * @return \Illuminate\Mail\Mailables\Content */ - public function content() + public function content(): Content { return new Content( view: 'emails.orders.shipped', @@ -394,9 +378,9 @@ To add attachments to an email, you will add attachments to the array returned b /** * Get the attachments for the message. * - * @return \Illuminate\Mail\Mailables\Attachment[] + * @return array */ - public function attachments() + public function attachments(): array { return [ Attachment::fromPath('/path/to/file'), @@ -408,9 +392,9 @@ When attaching files to a message, you may also specify the display name and / o /** * Get the attachments for the message. * - * @return \Illuminate\Mail\Mailables\Attachment[] + * @return array */ - public function attachments() + public function attachments(): array { return [ Attachment::fromPath('/path/to/file') @@ -427,9 +411,9 @@ If you have stored a file on one of your [filesystem disks](/docs/{{version}}/fi /** * Get the attachments for the message. * - * @return \Illuminate\Mail\Mailables\Attachment[] + * @return array */ - public function attachments() + public function attachments(): array { return [ Attachment::fromStorage('/path/to/file'), @@ -441,9 +425,9 @@ Of course, you may also specify the attachment's name and MIME type: /** * Get the attachments for the message. * - * @return \Illuminate\Mail\Mailables\Attachment[] + * @return array */ - public function attachments() + public function attachments(): array { return [ Attachment::fromStorage('/path/to/file') @@ -457,9 +441,9 @@ The `fromStorageDisk` method may be used if you need to specify a storage disk o /** * Get the attachments for the message. * - * @return \Illuminate\Mail\Mailables\Attachment[] + * @return array */ - public function attachments() + public function attachments(): array { return [ Attachment::fromStorageDisk('s3', '/path/to/file') @@ -476,9 +460,9 @@ The `fromData` attachment method may be used to attach a raw string of bytes as /** * Get the attachments for the message. * - * @return \Illuminate\Mail\Mailables\Attachment[] + * @return array */ - public function attachments() + public function attachments(): array { return [ Attachment::fromData(fn () => $this->pdf, 'Report.pdf') @@ -534,10 +518,8 @@ To get started, implement the `Illuminate\Contracts\Mail\Attachable` interface o { /** * Get the attachable representation of the model. - * - * @return \Illuminate\Mail\Attachment */ - public function toMailAttachment() + public function toMailAttachment(): Attachment { return Attachment::fromPath('/path/to/file'); } @@ -548,9 +530,9 @@ Once you have defined your attachable object, you may return an instance of that /** * Get the attachments for the message. * - * @return array + * @return array */ - public function attachments() + public function attachments(): array { return [$this->photo]; } @@ -584,10 +566,8 @@ To accomplish this, define a `headers` method on your mailable. The `headers` me /** * Get the message headers. - * - * @return \Illuminate\Mail\Mailables\Headers */ - public function headers() + public function headers(): Headers { return new Headers( messageId: 'custom-message-id@example.com', @@ -610,7 +590,7 @@ Some third-party email providers such as Mailgun and Postmark support message "t * * @return \Illuminate\Mail\Mailables\Envelope */ - public function envelope() + public function envelope(): Envelope { return new Envelope( subject: 'Order Shipped', @@ -635,10 +615,8 @@ Laravel's mail capabilities are powered by Symfony Mailer. Laravel allows you to /** * Get the message envelope. - * - * @return \Illuminate\Mail\Mailables\Envelope */ - public function envelope() + public function envelope(): Envelope { return new Envelope( subject: 'Order Shipped', @@ -670,10 +648,8 @@ Then, when configuring the mailable `Content` definition within its `content` me /** * Get the message content definition. - * - * @return \Illuminate\Mail\Mailables\Content */ - public function content() + public function content(): Content { return new Content( markdown: 'emails.orders.shipped', @@ -775,23 +751,23 @@ To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/ use App\Mail\OrderShipped; use App\Models\Order; use Illuminate\Http\Request; + use Illuminate\Http\Response; use Illuminate\Support\Facades\Mail; class OrderShipmentController extends Controller { /** * Ship the given order. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $order = Order::findOrFail($request->order_id); // Ship the order... Mail::to($request->user())->send(new OrderShipped($order)); + + return response()->noContent(); } } @@ -868,7 +844,7 @@ If you have mailable classes that you want to always be queued, you may implemen class OrderShipped extends Mailable implements ShouldQueue { - // + // ... } @@ -899,8 +875,6 @@ Alternatively, you may call the `afterCommit` method from your mailable's constr /** * Create a new message instance. - * - * @return void */ public function __construct() { @@ -959,10 +933,8 @@ Sometimes, applications store each user's preferred locale. By implementing the { /** * Get the user's preferred locale. - * - * @return string */ - public function preferredLocale() + public function preferredLocale(): string { return $this->locale; } @@ -982,7 +954,7 @@ As you might expect, the "HTML" assertions assert that the HTML version of your use App\Mail\InvoicePaid; use App\Models\User; - public function test_mailable_content() + public function test_mailable_content(): void { $user = User::factory()->create(); @@ -1042,10 +1014,8 @@ Finally, you may specify a global "to" address by invoking the `alwaysTo` method /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { if ($this->app->environment('local')) { Mail::alwaysTo('taylor@example.com'); @@ -1061,7 +1031,7 @@ Laravel fires two events during the process of sending mail messages. The `Messa use App\Listeners\LogSentMessage; use Illuminate\Mail\Events\MessageSending; use Illuminate\Mail\Events\MessageSent; - + /** * The event listener mappings for the application. * @@ -1098,9 +1068,6 @@ Laravel includes a variety of mail transports; however, you may wish to write yo /** * Create a new Mailchimp transport instance. - * - * @param \MailchimpTransactional\ApiClient $client - * @return void */ public function __construct(ApiClient $client) { @@ -1126,8 +1093,6 @@ Laravel includes a variety of mail transports; however, you may wish to write yo /** * Get the string representation of the transport. - * - * @return string */ public function __toString(): string { @@ -1142,10 +1107,8 @@ Once you've defined your custom transport, you may register it via the `extend` /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Mail::extend('mailchimp', function (array $config = []) { return new MailchimpTransport(/* ... */); @@ -1182,10 +1145,8 @@ Finally, you may use the `Mail` facade's `extend` method to register the transpo /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Mail::extend('sendinblue', function () { return (new SendinblueTransportFactory)->create( diff --git a/middleware.md b/middleware.md index 94061ba4d65..d71c797e4eb 100644 --- a/middleware.md +++ b/middleware.md @@ -33,17 +33,17 @@ This command will place a new `EnsureTokenIsValid` class within your `app/Http/M namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; class EnsureTokenIsValid { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { if ($request->input('token') !== 'my-secret-token') { return redirect('home'); @@ -71,10 +71,12 @@ Of course, a middleware can perform tasks before or after passing the request de namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; class BeforeMiddleware { - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { // Perform action @@ -89,10 +91,12 @@ However, this middleware would perform its task **after** the request is handled namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; class AfterMiddleware { - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { $response = $next($request); @@ -132,13 +136,13 @@ If you would like to assign middleware to specific routes, you should first assi Once the middleware has been defined in the HTTP kernel, you may use the `middleware` method to assign middleware to a route: Route::get('/profile', function () { - // + // ... })->middleware('auth'); You may assign multiple middleware to the route by passing an array of middleware names to the `middleware` method: Route::get('/', function () { - // + // ... })->middleware(['first', 'second']); When assigning middleware, you may also pass the fully qualified class name: @@ -146,7 +150,7 @@ When assigning middleware, you may also pass the fully qualified class name: use App\Http\Middleware\EnsureTokenIsValid; Route::get('/profile', function () { - // + // ... })->middleware(EnsureTokenIsValid::class); @@ -158,11 +162,11 @@ When assigning middleware to a group of routes, you may occasionally need to pre Route::middleware([EnsureTokenIsValid::class])->group(function () { Route::get('/', function () { - // + // ... }); Route::get('/profile', function () { - // + // ... })->withoutMiddleware([EnsureTokenIsValid::class]); }); @@ -172,7 +176,7 @@ You may also exclude a given set of middleware from an entire [group](/docs/{{ve Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () { Route::get('/profile', function () { - // + // ... }); }); @@ -209,11 +213,11 @@ Laravel includes predefined `web` and `api` middleware groups that contain commo Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware. Again, middleware groups make it more convenient to assign many middleware to a route at once: Route::get('/', function () { - // + // ... })->middleware('web'); Route::middleware(['web'])->group(function () { - // + // ... }); > **Note** @@ -256,18 +260,17 @@ Additional middleware parameters will be passed to the middleware after the `$ne namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; class EnsureUserHasRole { /** - * Handle the incoming request. + * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param string $role - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next, $role) + public function handle(Request $request, Closure $next, string $role): Response { if (! $request->user()->hasRole($role)) { // Redirect... @@ -280,8 +283,8 @@ Additional middleware parameters will be passed to the middleware after the `$ne Middleware parameters may be specified when defining the route by separating the middleware name and parameters with a `:`. Multiple parameters should be delimited by commas: - Route::put('/post/{id}', function ($id) { - // + Route::put('/post/{id}', function (string $id) { + // ... })->middleware('role:editor'); @@ -294,29 +297,25 @@ Sometimes a middleware may need to do some work after the HTTP response has been namespace Illuminate\Session\Middleware; use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; class TerminatingMiddleware { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { return $next($request); } /** * Handle tasks after the response has been sent to the browser. - * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Http\Response $response - * @return void */ - public function terminate($request, $response) + public function terminate(Request $request, Response $response): void { // ... } @@ -330,10 +329,8 @@ When calling the `terminate` method on your middleware, Laravel will resolve a f /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { $this->app->singleton(TerminatingMiddleware::class); } diff --git a/migrations.md b/migrations.md index a207f28965d..1bb2f509faf 100644 --- a/migrations.md +++ b/migrations.md @@ -90,10 +90,8 @@ Within both of these methods, you may use the Laravel schema builder to expressi { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('flights', function (Blueprint $table) { $table->id(); @@ -105,10 +103,8 @@ Within both of these methods, you may use the Laravel schema builder to expressi /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('flights'); } @@ -128,12 +124,10 @@ If your migration will be interacting with a database connection other than your /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { - // + // ... } @@ -992,10 +986,8 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('flights', function (Blueprint $table) { $table->id(); @@ -1013,7 +1005,7 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi When using the MySQL database, the `after` method may be used to add columns after an existing column in the schema: - $table->after('password', function ($table) { + $table->after('password', function (Blueprint $table) { $table->string('address_line1'); $table->string('address_line2'); $table->string('city'); @@ -1170,10 +1162,8 @@ By default, Laravel uses the `utf8mb4` character set. If you are running a versi /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Schema::defaultStringLength(191); } diff --git a/mocking.md b/mocking.md index 70f1c3510b3..6fb684a7cda 100644 --- a/mocking.md +++ b/mocking.md @@ -33,7 +33,7 @@ When mocking an object that is going to be injected into your application via La use Mockery; use Mockery\MockInterface; - public function test_something_can_be_mocked() + public function test_something_can_be_mocked(): void { $this->instance( Service::class, @@ -86,14 +86,14 @@ Unlike traditional static method calls, [facades](/docs/{{version}}/facades) (in { /** * Retrieve a list of all users of the application. - * - * @return \Illuminate\Http\Response */ - public function index() + public function index(): array { $value = Cache::get('key'); - // + return [ + // ... + ]; } } @@ -110,7 +110,7 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, class UserControllerTest extends TestCase { - public function testGetIndex() + public function test_get_index(): void { Cache::shouldReceive('get') ->once() @@ -133,7 +133,7 @@ If you would like to [spy](http://docs.mockery.io/en/latest/reference/spies.html use Illuminate\Support\Facades\Cache; - public function test_values_are_be_stored_in_cache() + public function test_values_are_be_stored_in_cache(): void { Cache::spy(); @@ -163,7 +163,7 @@ You may use the `Bus` facade's `fake` method to prevent jobs from being dispatch class ExampleTest extends TestCase { - public function test_orders_can_be_shipped() + public function test_orders_can_be_shipped(): void { Bus::fake(); @@ -291,7 +291,7 @@ When testing code that dispatches events, you may wish to instruct Laravel to no /** * Test order shipping. */ - public function test_orders_can_be_shipped() + public function test_orders_can_be_shipped(): void { Event::fake(); @@ -335,7 +335,7 @@ If you only want to fake event listeners for a specific set of events, you may p /** * Test order process. */ - public function test_orders_can_be_processed() + public function test_orders_can_be_processed(): void { Event::fake([ OrderCreated::class, @@ -376,7 +376,7 @@ If you only want to fake event listeners for a portion of your test, you may use /** * Test order process. */ - public function test_orders_can_be_processed() + public function test_orders_can_be_processed(): void { $order = Event::fakeFor(function () { $order = Order::factory()->create(); @@ -415,7 +415,7 @@ After calling the `Mail` facade's `fake` method, you may then assert that [maila class ExampleTest extends TestCase { - public function test_orders_can_be_shipped() + public function test_orders_can_be_shipped(): void { Mail::fake(); @@ -451,7 +451,7 @@ You may pass a closure to the `assertSent`, `assertNotSent`, `assertQueued`, or When calling the `Mail` facade's assertion methods, the mailable instance accepted by the provided closure exposes helpful methods for examining the mailable: - Mail::assertSent(OrderShipped::class, function ($mail) use ($user) { + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) { return $mail->hasTo($user->email) && $mail->hasCc('...') && $mail->hasBcc('...') && @@ -464,7 +464,7 @@ The mailable instance also includes several helpful methods for examining the at use Illuminate\Mail\Mailables\Attachment; - Mail::assertSent(OrderShipped::class, function ($mail) { + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { return $mail->hasAttachment( Attachment::fromPath('/path/to/file') ->as('name.pdf') @@ -472,13 +472,13 @@ The mailable instance also includes several helpful methods for examining the at ); }); - Mail::assertSent(OrderShipped::class, function ($mail) { + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { return $mail->hasAttachment( Attachment::fromStorageDisk('s3', '/path/to/file') ); }); - Mail::assertSent(OrderShipped::class, function ($mail) use ($pdfData) { + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($pdfData) { return $mail->hasAttachment( Attachment::fromData(fn () => $pdfData, 'name.pdf') ); @@ -516,7 +516,7 @@ After calling the `Notification` facade's `fake` method, you may then assert tha class ExampleTest extends TestCase { - public function test_orders_can_be_shipped() + public function test_orders_can_be_shipped(): void { Notification::fake(); @@ -544,7 +544,7 @@ You may pass a closure to the `assertSentTo` or `assertNotSentTo` methods in ord Notification::assertSentTo( $user, - function (OrderShipped $notification, $channels) use ($order) { + function (OrderShipped $notification, array $channels) use ($order) { return $notification->order->id === $order->id; } ); @@ -560,7 +560,7 @@ By passing a closure as the second argument to the `assertSentOnDemand` method, Notification::assertSentOnDemand( OrderShipped::class, - function ($notification, $channels, $notifiable) use ($user) { + function (OrderShipped $notification, array $channels, object $notifiable) use ($user) { return $notifiable->routes['mail'] === $user->email; } ); @@ -586,7 +586,7 @@ After calling the `Queue` facade's `fake` method, you may then assert that the a class ExampleTest extends TestCase { - public function test_orders_can_be_shipped() + public function test_orders_can_be_shipped(): void { Queue::fake(); @@ -614,7 +614,7 @@ You may pass a closure to the `assertPushed` or `assertNotPushed` methods in ord If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `fake` method: - public function test_orders_can_be_shipped() + public function test_orders_can_be_shipped(): void { Queue::fake([ ShipOrder::class, @@ -669,7 +669,7 @@ The `Storage` facade's `fake` method allows you to easily generate a fake disk t class ExampleTest extends TestCase { - public function test_albums_can_be_uploaded() + public function test_albums_can_be_uploaded(): void { Storage::fake('photos'); @@ -703,7 +703,7 @@ When testing, you may occasionally need to modify the time returned by helpers s use Illuminate\Support\Carbon; - public function testTimeCanBeManipulated() + public function test_time_can_be_manipulated(): void { // Travel into the future... $this->travel(5)->milliseconds(); diff --git a/notifications.md b/notifications.md index 0f0119f8acb..8a26a2ec4a4 100644 --- a/notifications.md +++ b/notifications.md @@ -122,10 +122,9 @@ The `via` method receives a `$notifiable` instance, which will be an instance of /** * Get the notification's delivery channels. * - * @param mixed $notifiable - * @return array + * @return array */ - public function via($notifiable) + public function via(object $notifiable): array { return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database']; } @@ -183,10 +182,9 @@ Alternatively, you may define a `withDelay` method on the notification class its /** * Determine the notification's delivery delay. * - * @param mixed $notifiable - * @return array + * @return array */ - public function withDelay($notifiable) + public function withDelay(object $notifiable): array { return [ 'mail' => now()->addMinutes(5), @@ -229,9 +227,9 @@ If you would like to specify a specific queue that should be used for each notif /** * Determine which queues should be used for each notification channel. * - * @return array + * @return array */ - public function viaQueues() + public function viaQueues(): array { return [ 'mail' => 'mail-queue', @@ -266,8 +264,6 @@ Alternatively, you may call the `afterCommit` method from your notification's co /** * Create a new notification instance. - * - * @return void */ public function __construct() { @@ -287,12 +283,8 @@ However, if you would like to make the final determination on whether the queued /** * Determine if the notification should be sent. - * - * @param mixed $notifiable - * @param string $channel - * @return bool */ - public function shouldSend($notifiable, $channel) + public function shouldSend(object $notifiable, string $channel): bool { return $this->invoice->isPaid(); } @@ -328,11 +320,8 @@ The `MailMessage` class contains a few simple methods to help you build transact /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { $url = url('https://github.com/invoice/'.$this-%3Einvoice-%3Eid); @@ -361,11 +350,8 @@ Some notifications inform users of errors, such as a failed invoice payment. You /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->error() @@ -380,11 +366,8 @@ Instead of defining the "lines" of text in the notification class, you may use t /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage)->view( 'emails.name', ['invoice' => $this->invoice] @@ -395,11 +378,8 @@ You may specify a plain-text view for the mail message by passing the view name /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage)->view( ['emails.name.html', 'emails.name.plain'], @@ -414,11 +394,8 @@ By default, the email's sender / from address is defined in the `config/mail.php /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->from('barrett@example.com', 'Barrett Blair') @@ -436,6 +413,7 @@ When sending notifications via the `mail` channel, the notification system will use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; + use Illuminate\Notifications\Notification; class User extends Authenticatable { @@ -444,10 +422,9 @@ When sending notifications via the `mail` channel, the notification system will /** * Route notifications for the mail channel. * - * @param \Illuminate\Notifications\Notification $notification - * @return array|string + * @return array|string */ - public function routeNotificationForMail($notification) + public function routeNotificationForMail(Notification $notification): array|string { // Return email address only... return $this->email_address; @@ -464,11 +441,8 @@ By default, the email's subject is the class name of the notification formatted /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->subject('Notification Subject') @@ -482,11 +456,8 @@ By default, the email notification will be sent using the default mailer defined /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->mailer('postmark') @@ -509,11 +480,8 @@ To add attachments to an email notification, use the `attach` method while build /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->greeting('Hello!') @@ -527,11 +495,8 @@ When attaching files to a message, you may also specify the display name and / o /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->greeting('Hello!') @@ -547,11 +512,8 @@ Unlike attaching files in mailable objects, you may not attach a file directly f /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return Mailable */ - public function toMail($notifiable) + public function toMail(object $notifiable): Mailable { return (new InvoicePaidMailable($this->invoice)) ->to($notifiable->email) @@ -562,11 +524,8 @@ When necessary, multiple files may be attached to a message using the `attachMan /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->greeting('Hello!') @@ -586,11 +545,8 @@ The `attachData` method may be used to attach a raw string of bytes as an attach /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->greeting('Hello!') @@ -606,11 +562,8 @@ Some third-party email providers such as Mailgun and Postmark support message "t /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->greeting('Comment Upvoted!') @@ -631,11 +584,8 @@ The `withSymfonyMessage` method of the `MailMessage` class allows you to registe /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->withSymfonyMessage(function (Email $message) { @@ -651,14 +601,12 @@ The `withSymfonyMessage` method of the `MailMessage` class allows you to registe If needed, you may return a full [mailable object](/docs/{{version}}/mail) from your notification's `toMail` method. When returning a `Mailable` instead of a `MailMessage`, you will need to specify the message recipient using the mailable object's `to` method: use App\Mail\InvoicePaid as InvoicePaidMailable; + use Illuminate\Mail\Mailable; /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return Mailable */ - public function toMail($notifiable) + public function toMail(object $notifiable): Mailable { return (new InvoicePaidMailable($this->invoice)) ->to($notifiable->email); @@ -671,14 +619,12 @@ If you are sending an [on-demand notification](#on-demand-notifications), the `$ use App\Mail\InvoicePaid as InvoicePaidMailable; use Illuminate\Notifications\AnonymousNotifiable; + use Illuminate\Mail\Mailable; /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return Mailable */ - public function toMail($notifiable) + public function toMail(object $notifiable): Mailable { $address = $notifiable instanceof AnonymousNotifiable ? $notifiable->routeNotificationFor('mail') @@ -721,11 +667,8 @@ Like all other mail notifications, notifications that use Markdown templates sho /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { $url = url('https://github.com/invoice/'.$this-%3Einvoice-%3Eid); @@ -812,11 +755,8 @@ To customize the theme for an individual notification, you may call the `theme` /** * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage */ - public function toMail($notifiable) + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->theme('invoice') @@ -848,10 +788,9 @@ If a notification supports being stored in a database table, you should define a /** * Get the array representation of the notification. * - * @param mixed $notifiable - * @return array + * @return array */ - public function toArray($notifiable) + public function toArray(object $notifiable): array { return [ 'invoice_id' => $this->invoice->id, @@ -928,11 +867,8 @@ The `broadcast` channel broadcasts notifications using Laravel's [event broadcas /** * Get the broadcastable representation of the notification. - * - * @param mixed $notifiable - * @return BroadcastMessage */ - public function toBroadcast($notifiable) + public function toBroadcast(object $notifiable): BroadcastMessage { return new BroadcastMessage([ 'invoice_id' => $this->invoice->id, @@ -954,14 +890,10 @@ All broadcast notifications are queued for broadcasting. If you would like to co 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; - /** * Get the type of the notification being broadcast. - * - * @return string */ - public function broadcastType() + public function broadcastType(): string { return 'broadcast.message'; } @@ -995,10 +927,8 @@ If you would like to customize which channel that an entity's broadcast notifica /** * The channels the user receives notification broadcasts on. - * - * @return string */ - public function receivesBroadcastNotificationsOn() + public function receivesBroadcastNotificationsOn(): string { return 'users.'.$this->id; } @@ -1025,13 +955,12 @@ After defining your keys, you may set a `VONAGE_SMS_FROM` environment variable t If a notification supports being sent as an SMS, you should define a `toVonage` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\VonageMessage` instance: + use Illuminate\Notifications\Messages\VonageMessage; + /** * Get the Vonage / SMS representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\VonageMessage */ - public function toVonage($notifiable) + public function toVonage(object $notifiable): VonageMessage { return (new VonageMessage) ->content('Your SMS message content'); @@ -1042,13 +971,12 @@ If a notification supports being sent as an SMS, you should define a `toVonage` If your SMS message will contain unicode characters, you should call the `unicode` method when constructing the `VonageMessage` instance: + use Illuminate\Notifications\Messages\VonageMessage; + /** * Get the Vonage / SMS representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\VonageMessage */ - public function toVonage($notifiable) + public function toVonage(object $notifiable): VonageMessage { return (new VonageMessage) ->content('Your unicode message') @@ -1060,13 +988,12 @@ If your SMS message will contain unicode characters, you should call the `unicod If you would like to send some notifications from a phone number that is different from the phone number specified by your `VONAGE_SMS_FROM` environment variable, you may call the `from` method on a `VonageMessage` instance: + use Illuminate\Notifications\Messages\VonageMessage; + /** * Get the Vonage / SMS representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\VonageMessage */ - public function toVonage($notifiable) + public function toVonage(object $notifiable): VonageMessage { return (new VonageMessage) ->content('Your SMS message content') @@ -1078,13 +1005,12 @@ If you would like to send some notifications from a phone number that is differe If you would like to keep track of costs per user, team, or client, you may add a "client reference" to the notification. Vonage will allow you to generate reports using this client reference so that you can better understand a particular customer's SMS usage. The client reference can be any string up to 40 characters: + use Illuminate\Notifications\Messages\VonageMessage; + /** * Get the Vonage / SMS representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\VonageMessage */ - public function toVonage($notifiable) + public function toVonage(object $notifiable): VonageMessage { return (new VonageMessage) ->clientReference((string) $notifiable->id) @@ -1102,6 +1028,7 @@ To route Vonage notifications to the proper phone number, define a `routeNotific use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; + use Illuminate\Notifications\Notification; class User extends Authenticatable { @@ -1109,11 +1036,8 @@ To route Vonage notifications to the proper phone number, define a `routeNotific /** * Route notifications for the Vonage channel. - * - * @param \Illuminate\Notifications\Notification $notification - * @return string */ - public function routeNotificationForVonage($notification) + public function routeNotificationForVonage(Notification $notification): string { return $this->phone_number; } @@ -1138,13 +1062,12 @@ You will also need to create a [Slack App](https://api.slack.com/apps?new_app=1) If a notification supports being sent as a Slack message, you should define a `toSlack` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\SlackMessage` instance. Slack messages may contain text content as well as an "attachment" that formats additional text or an array of fields. Let's take a look at a basic `toSlack` example: + use Illuminate\Notifications\Messages\SlackMessage; + /** * Get the Slack representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\SlackMessage */ - public function toSlack($notifiable) + public function toSlack(object $notifiable): SlackMessage { return (new SlackMessage) ->content('One of your invoices has been paid!'); @@ -1155,20 +1078,20 @@ If a notification supports being sent as a Slack message, you should define a `t You may also add "attachments" to Slack messages. Attachments provide richer formatting options than simple text messages. In this example, we will send an error notification about an exception that occurred in an application, including a link to view more details about the exception: + use Illuminate\Notifications\Messages\SlackAttachment; + use Illuminate\Notifications\Messages\SlackMessage; + /** * Get the Slack representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\SlackMessage */ - public function toSlack($notifiable) + public function toSlack(object $notifiable): SlackMessage { $url = url('https://github.com/exceptions/'.$this-%3Eexception-%3Eid); return (new SlackMessage) ->error() ->content('Whoops! Something went wrong.') - ->attachment(function ($attachment) use ($url) { + ->attachment(function (SlackAttachment $attachment) use ($url) { $attachment->title('Exception: File Not Found', $url) ->content('File [background.jpg] was not found.'); }); @@ -1176,20 +1099,20 @@ You may also add "attachments" to Slack messages. Attachments provide richer for Attachments also allow you to specify an array of data that should be presented to the user. The given data will be presented in a table-style format for easy reading: + use Illuminate\Notifications\Messages\SlackAttachment; + use Illuminate\Notifications\Messages\SlackMessage; + /** * Get the Slack representation of the notification. - * - * @param mixed $notifiable - * @return SlackMessage */ - public function toSlack($notifiable) + public function toSlack(object $notifiable): SlackMessage { $url = url('https://github.com/invoices/'.$this-%3Einvoice-%3Eid); return (new SlackMessage) ->success() ->content('One of your invoices has been paid!') - ->attachment(function ($attachment) use ($url) { + ->attachment(function (SlackAttachment $attachment) use ($url) { $attachment->title('Invoice 1322', $url) ->fields([ 'Title' => 'Server Expenses', @@ -1205,20 +1128,20 @@ Attachments also allow you to specify an array of data that should be presented If some of your attachment fields contain Markdown, you may use the `markdown` method to instruct Slack to parse and display the given attachment fields as Markdown formatted text. The values accepted by this method are: `pretext`, `text`, and / or `fields`. For more information about Slack attachment formatting, check out the [Slack API documentation](https://api.slack.com/docs/message-formatting#message_formatting): + use Illuminate\Notifications\Messages\SlackAttachment; + use Illuminate\Notifications\Messages\SlackMessage; + /** * Get the Slack representation of the notification. - * - * @param mixed $notifiable - * @return SlackMessage */ - public function toSlack($notifiable) + public function toSlack(object $notifiable): SlackMessage { $url = url('https://github.com/exceptions/'.$this-%3Eexception-%3Eid); return (new SlackMessage) ->error() ->content('Whoops! Something went wrong.') - ->attachment(function ($attachment) use ($url) { + ->attachment(function (SlackAttachment $attachment) use ($url) { $attachment->title('Exception: File Not Found', $url) ->content('File [background.jpg] was *not found*.') ->markdown(['text']); @@ -1236,6 +1159,7 @@ To route Slack notifications to the proper Slack team and channel, define a `rou use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; + use Illuminate\Notifications\Notification; class User extends Authenticatable { @@ -1243,11 +1167,8 @@ To route Slack notifications to the proper Slack team and channel, define a `rou /** * Route notifications for the Slack channel. - * - * @param \Illuminate\Notifications\Notification $notification - * @return string */ - public function routeNotificationForSlack($notification) + public function routeNotificationForSlack(Notification $notification): string { return 'https://hooks.slack.com/services/...'; } @@ -1279,10 +1200,8 @@ Sometimes, applications store each user's preferred locale. By implementing the { /** * Get the user's preferred locale. - * - * @return string */ - public function preferredLocale() + public function preferredLocale(): string { return $this->locale; } @@ -1320,11 +1239,8 @@ The notification will not be sent if an event listener for the `NotificationSend /** * Handle the event. - * - * @param \Illuminate\Notifications\Events\NotificationSending $event - * @return void */ - public function handle(NotificationSending $event) + public function handle(NotificationSending $event): void { return false; } @@ -1333,11 +1249,8 @@ Within an event listener, you may access the `notifiable`, `notification`, and ` /** * Handle the event. - * - * @param \Illuminate\Notifications\Events\NotificationSending $event - * @return void */ - public function handle(NotificationSending $event) + public function handle(NotificationSending $event): void { // $event->channel // $event->notifiable @@ -1370,11 +1283,8 @@ Within an event listener, you may access the `notifiable`, `notification`, `chan /** * Handle the event. - * - * @param \Illuminate\Notifications\Events\NotificationSent $event - * @return void */ - public function handle(NotificationSent $event) + public function handle(NotificationSent $event): void { // $event->channel // $event->notifiable @@ -1399,12 +1309,8 @@ Within the `send` method, you may call methods on the notification to retrieve a { /** * Send the given notification. - * - * @param mixed $notifiable - * @param \Illuminate\Notifications\Notification $notification - * @return void */ - public function send($notifiable, Notification $notification) + public function send(object $notifiable, Notification $notification): void { $message = $notification->toVoice($notifiable); @@ -1430,22 +1336,16 @@ Once your notification channel class has been defined, you may return the class /** * Get the notification channels. - * - * @param mixed $notifiable - * @return array|string */ - public function via($notifiable) + public function via(object $notifiable): string { - return [VoiceChannel::class]; + return VoiceChannel::class; } /** * Get the voice representation of the notification. - * - * @param mixed $notifiable - * @return VoiceMessage */ - public function toVoice($notifiable) + public function toVoice(object $notifiable): VoiceMessage { // ... } diff --git a/octane.md b/octane.md index c4086d8f539..ef6f596e0bb 100644 --- a/octane.md +++ b/octane.md @@ -309,15 +309,14 @@ In general, you should avoid injecting the application service container or HTTP ```php use App\Service; +use Illuminate\Contracts\Foundation\Application; /** * Register any application services. - * - * @return void */ -public function register() +public function register(): void { - $this->app->singleton(Service::class, function ($app) { + $this->app->singleton(Service::class, function (Application $app) { return new Service($app); }); } @@ -330,8 +329,9 @@ As a work-around, you could either stop registering the binding as a singleton, ```php use App\Service; use Illuminate\Container\Container; +use Illuminate\Contracts\Foundation\Application; -$this->app->bind(Service::class, function ($app) { +$this->app->bind(Service::class, function (Application $app) { return new Service($app); }); @@ -349,15 +349,14 @@ In general, you should avoid injecting the application service container or HTTP ```php use App\Service; +use Illuminate\Contracts\Foundation\Application; /** * Register any application services. - * - * @return void */ -public function register() +public function register(): void { - $this->app->singleton(Service::class, function ($app) { + $this->app->singleton(Service::class, function (Application $app) { return new Service($app['request']); }); } @@ -369,12 +368,13 @@ As a work-around, you could either stop registering the binding as a singleton, ```php use App\Service; +use Illuminate\Contracts\Foundation\Application; -$this->app->bind(Service::class, function ($app) { +$this->app->bind(Service::class, function (Application $app) { return new Service($app['request']); }); -$this->app->singleton(Service::class, function ($app) { +$this->app->singleton(Service::class, function (Application $app) { return new Service(fn () => $app['request']); }); @@ -395,15 +395,14 @@ In general, you should avoid injecting the configuration repository instance int ```php use App\Service; +use Illuminate\Contracts\Foundation\Application; /** * Register any application services. - * - * @return void */ -public function register() +public function register(): void { - $this->app->singleton(Service::class, function ($app) { + $this->app->singleton(Service::class, function (Application $app) { return new Service($app->make('config')); }); } @@ -416,8 +415,9 @@ As a work-around, you could either stop registering the binding as a singleton, ```php use App\Service; use Illuminate\Container\Container; +use Illuminate\Contracts\Foundation\Application; -$this->app->bind(Service::class, function ($app) { +$this->app->bind(Service::class, function (Application $app) { return new Service($app->make('config')); }); @@ -440,15 +440,14 @@ use Illuminate\Support\Str; /** * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @return void */ -public function index(Request $request) +public function index(Request $request): array { Service::$data[] = Str::random(10); - // ... + return [ + // ... + ]; } ``` diff --git a/packages.md b/packages.md index 427459334e9..5b558b92ac0 100644 --- a/packages.md +++ b/packages.md @@ -94,10 +94,8 @@ Typically, you will need to publish your package's configuration file to the app /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->publishes([ __DIR__.'/../config/courier.php' => config_path('courier.php'), @@ -120,10 +118,8 @@ The `mergeConfigFrom` method accepts the path to your package's configuration fi /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { $this->mergeConfigFrom( __DIR__.'/../config/courier.php', 'courier' @@ -140,10 +136,8 @@ If your package contains routes, you may load them using the `loadRoutesFrom` me /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); } @@ -155,10 +149,8 @@ If your package contains [database migrations](/docs/{{version}}/migrations), yo /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); } @@ -172,10 +164,8 @@ If your package contains [translation files](/docs/{{version}}/localization), yo /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); } @@ -191,10 +181,8 @@ If you would like to publish your package's translations to the application's `l /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); @@ -212,10 +200,8 @@ To register your package's [views](/docs/{{version}}/views) with Laravel, you ne /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); } @@ -238,10 +224,8 @@ If you would like to make your views available for publishing to the application /** * Bootstrap the package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); @@ -262,10 +246,8 @@ If you are building a package that utilizes Blade components or placing componen /** * Bootstrap your package's services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::component('package-alert', AlertComponent::class); } @@ -285,10 +267,8 @@ Alternatively, you may use the `componentNamespace` method to autoload component /** * Bootstrap your package's services. - * - * @return void */ - public function boot() + public function boot(): void { Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); } @@ -320,10 +300,8 @@ Laravel's built-in `about` Artisan command provides a synopsis of the applicatio /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']); } @@ -338,10 +316,8 @@ To register your package's Artisan commands with Laravel, you may use the `comma /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { if ($this->app->runningInConsole()) { $this->commands([ @@ -358,10 +334,8 @@ Your package may have assets such as JavaScript, CSS, and images. To publish the /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->publishes([ __DIR__.'/../public' => public_path('vendor/courier'), @@ -381,10 +355,8 @@ You may want to publish groups of package assets and resources separately. For i /** * Bootstrap any package services. - * - * @return void */ - public function boot() + public function boot(): void { $this->publishes([ __DIR__.'/../config/package.php' => config_path('package.php') diff --git a/pagination.md b/pagination.md index a8c231b217f..9d8783fd746 100644 --- a/pagination.md +++ b/pagination.md @@ -52,15 +52,14 @@ In this example, the only argument passed to the `paginate` method is the number use App\Http\Controllers\Controller; use Illuminate\Support\Facades\DB; + use Illuminate\View\View; class UserController extends Controller { /** * Show all application users. - * - * @return \Illuminate\Http\Response */ - public function index() + public function index(): View { return view('user.index', [ 'users' => DB::table('users')->paginate(15) @@ -178,7 +177,7 @@ By default, links generated by the paginator will match the current request's UR $users->withPath('/admin/users'); - // + // ... }); @@ -193,7 +192,7 @@ You may append to the query string of pagination links using the `appends` metho $users->appends(['sort' => 'votes']); - // + // ... }); 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: @@ -303,10 +302,8 @@ If you would like to designate a different file as the default pagination view, { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Paginator::defaultView('view-name'); @@ -323,10 +320,8 @@ Laravel includes pagination views built using [Bootstrap CSS](https://getbootstr /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Paginator::useBootstrapFive(); Paginator::useBootstrapFour(); diff --git a/passport.md b/passport.md index 8ae80964ab2..53a46f98948 100644 --- a/passport.md +++ b/passport.md @@ -135,10 +135,8 @@ If necessary, you may define the path where Passport's keys should be loaded fro /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -201,10 +199,8 @@ By default, Passport issues long-lived access tokens that expire after one year. /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -237,10 +233,8 @@ After defining your model, you may instruct Passport to use your custom model vi /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -375,7 +369,7 @@ This route is used to delete clients: ```js axios.delete('/oauth/clients/' + clientId) .then(response => { - // + // ... }); ``` @@ -437,10 +431,8 @@ Sometimes you may wish to skip the authorization prompt, such as when authorizin { /** * Determine if the client should skip the authorization prompt. - * - * @return bool */ - public function skipsAuthorization() + public function skipsAuthorization(): bool { return $this->firstParty(); } @@ -560,11 +552,8 @@ You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your a /** * Define the application's command schedule. - * - * @param \Illuminate\Console\Scheduling\Schedule $schedule - * @return void */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { $schedule->command('passport:purge')->hourly(); } @@ -741,11 +730,8 @@ When authenticating using the password grant, Passport will use the `email` attr /** * Find the user instance for the given username. - * - * @param string $username - * @return \App\Models\User */ - public function findForPassport($username) + public function findForPassport(string $username): User { return $this->where('username', $username)->first(); } @@ -771,11 +757,8 @@ When authenticating using the password grant, Passport will use the `password` a /** * Validate the password of the user for the Passport password grant. - * - * @param string $password - * @return bool */ - public function validateForPassportPasswordGrant($password) + public function validateForPassportPasswordGrant(string $password): bool { return Hash::check($password, $this->password); } @@ -791,10 +774,8 @@ The implicit grant is similar to the authorization code grant; however, the toke /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -978,7 +959,7 @@ axios.delete('/oauth/personal-access-tokens/' + tokenId); 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 () { - // + // ... })->middleware('auth:api'); > **Warning** @@ -1002,7 +983,7 @@ If your application authenticates different types of users that perhaps use enti The following route will utilize the `api-customers` guard, which uses the `customers` user provider, to authenticate incoming requests: Route::get('/customer', function () { - // + // ... })->middleware('auth:api-customers'); > **Note** @@ -1034,10 +1015,8 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -1128,7 +1107,7 @@ Once an access token authenticated request has entered your application, you may Route::get('/orders', function (Request $request) { if ($request->user()->tokenCan('place-orders')) { - // + // ... } }); @@ -1182,10 +1161,8 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); @@ -1228,7 +1205,7 @@ Passport's `actingAs` method may be used to specify the currently authenticated use App\Models\User; use Laravel\Passport\Passport; - public function test_servers_can_be_created() + public function test_servers_can_be_created(): void { Passport::actingAs( User::factory()->create(), @@ -1245,7 +1222,7 @@ Passport's `actingAsClient` method may be used to specify the currently authenti use Laravel\Passport\Client; use Laravel\Passport\Passport; - public function test_orders_can_be_retrieved() + public function test_orders_can_be_retrieved(): void { Passport::actingAsClient( Client::factory()->create(), diff --git a/passwords.md b/passwords.md index fb1efd6cc17..31993dbf810 100644 --- a/passwords.md +++ b/passwords.md @@ -99,7 +99,7 @@ You may be wondering how Laravel knows how to retrieve the user record from your Next, we will define the routes necessary to actually reset the password once the user clicks on the password reset link that has been emailed to them and provides a new password. First, let's define the route that will display the reset password form that is displayed when the user clicks the reset password link. This route will receive a `token` parameter that we will use later to verify the password reset request: - Route::get('/reset-password/{token}', function ($token) { + Route::get('/reset-password/{token}', function (string $token) { return view('auth.reset-password', ['token' => $token]); })->middleware('guest')->name('password.reset'); @@ -110,6 +110,7 @@ The view that is returned by this route should display a form containing an `ema Of course, we need to define a route to actually handle the password reset form submission. This route will be responsible for validating the incoming request and updating the user's password in the database: + use App\Models\User; use Illuminate\Auth\Events\PasswordReset; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; @@ -125,7 +126,7 @@ Of course, we need to define a route to actually handle the password reset form $status = Password::reset( $request->only('email', 'password', 'password_confirmation', 'token'), - function ($user, $password) { + function (User $user, string $password) { $user->forceFill([ 'password' => Hash::make($password) ])->setRememberToken(Str::random(60)); @@ -170,18 +171,17 @@ If you would like to automate this process, consider adding the command to your 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 App\Models\User; use Illuminate\Auth\Notifications\ResetPassword; /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { $this->registerPolicies(); - ResetPassword::createUrlUsing(function ($user, string $token) { + ResetPassword::createUrlUsing(function (User $user, string $token) { return 'https://example.com/reset-password?token='.$token; }); } @@ -197,9 +197,8 @@ You may easily modify the notification class used to send the password reset lin * Send a password reset notification to the user. * * @param string $token - * @return void */ - public function sendPasswordResetNotification($token) + public function sendPasswordResetNotification($token): void { $url = 'https://example.com/reset-password?token='.$token; diff --git a/providers.md b/providers.md index 69aa92f8ffa..2d507902568 100644 --- a/providers.md +++ b/providers.md @@ -44,18 +44,17 @@ 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\Contracts\Foundation\Application; use Illuminate\Support\ServiceProvider; class RiakServiceProvider extends ServiceProvider { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - $this->app->singleton(Connection::class, function ($app) { + $this->app->singleton(Connection::class, function (Application $app) { return new Connection(config('riak')); }); } @@ -117,13 +116,11 @@ So, what if we need to register a [view composer](/docs/{{version}}/views#view-c { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { View::composer('view', function () { - // + // ... }); } } @@ -137,14 +134,11 @@ You may type-hint dependencies for your service provider's `boot` method. The [s /** * Bootstrap any application services. - * - * @param \Illuminate\Contracts\Routing\ResponseFactory $response - * @return void */ - public function boot(ResponseFactory $response) + public function boot(ResponseFactory $response): void { - $response->macro('serialized', function ($value) { - // + $response->macro('serialized', function (mixed $value) { + // ... }); } @@ -175,6 +169,7 @@ To defer the loading of a provider, implement the `\Illuminate\Contracts\Support namespace App\Providers; use App\Services\Riak\Connection; + use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\ServiceProvider; @@ -182,12 +177,10 @@ To defer the loading of a provider, implement the `\Illuminate\Contracts\Support { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - $this->app->singleton(Connection::class, function ($app) { + $this->app->singleton(Connection::class, function (Application $app) { return new Connection($app['config']['riak']); }); } @@ -195,9 +188,9 @@ To defer the loading of a provider, implement the `\Illuminate\Contracts\Support /** * Get the services provided by the provider. * - * @return array + * @return array */ - public function provides() + public function provides(): array { return [Connection::class]; } diff --git a/queries.md b/queries.md index d6739a652ba..fdd7fadd5fa 100644 --- a/queries.md +++ b/queries.md @@ -58,15 +58,14 @@ You may use the `table` method provided by the `DB` facade to begin a query. The use App\Http\Controllers\Controller; use Illuminate\Support\Facades\DB; + use Illuminate\View\View; class UserController extends Controller { /** * Show a list of all of the application's users. - * - * @return \Illuminate\Http\Response */ - public function index() + public function index(): View { $users = DB::table('users')->get(); @@ -130,17 +129,18 @@ If you would like to retrieve an `Illuminate\Support\Collection` instance contai 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\Collection; use Illuminate\Support\Facades\DB; - DB::table('users')->orderBy('id')->chunk(100, function ($users) { + DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { foreach ($users as $user) { - // + // ... } }); You may stop further chunks from being processed by returning `false` from the closure: - DB::table('users')->orderBy('id')->chunk(100, function ($users) { + DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { // Process the records... return false; @@ -149,7 +149,7 @@ You may stop further chunks from being processed by returning `false` from the c 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) { + ->chunkById(100, function (Collection $users) { foreach ($users as $user) { DB::table('users') ->where('id', $user->id) @@ -168,8 +168,8 @@ The `lazy` method works similarly to [the `chunk` method](#chunking-results) in ```php use Illuminate\Support\Facades\DB; -DB::table('users')->orderBy('id')->lazy()->each(function ($user) { - // +DB::table('users')->orderBy('id')->lazy()->each(function (object $user) { + // ... }); ``` @@ -177,7 +177,7 @@ Once again, if you plan to update the retrieved records while iterating over the ```php DB::table('users')->where('active', false) - ->lazyById()->each(function ($user) { + ->lazyById()->each(function (object $user) { DB::table('users') ->where('id', $user->id) ->update(['active' => true]); @@ -352,7 +352,7 @@ You may use the `crossJoin` method to perform a "cross join". Cross joins genera 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) { + ->join('contacts', function (JoinClause $join) { $join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */); }) ->get(); @@ -360,7 +360,7 @@ You may also specify more advanced join clauses. To get started, pass a closure 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) { + ->join('contacts', function (JoinClause $join) { $join->on('users.id', '=', 'contacts.user_id') ->where('contacts.user_id', '>', 5); }) @@ -377,7 +377,7 @@ You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a q ->groupBy('user_id'); $users = DB::table('users') - ->joinSub($latestPosts, 'latest_posts', function ($join) { + ->joinSub($latestPosts, 'latest_posts', function (JoinClause $join) { $join->on('users.id', '=', 'latest_posts.user_id'); })->get(); @@ -455,7 +455,7 @@ If you need to group an "or" condition within parentheses, you may pass a closur $users = DB::table('users') ->where('votes', '>', 100) - ->orWhere(function($query) { + ->orWhere(function(Builder $query) { $query->where('name', 'Abigail') ->where('votes', '>', 50); }) @@ -476,7 +476,7 @@ select * from users where votes > 100 or (name = 'Abigail' and votes > 50) The `whereNot` and `orWhereNot` methods may be used to negate a given group of query constraints. For example, the following query excludes products that are on clearance or which have a price that is less than ten: $products = DB::table('products') - ->whereNot(function ($query) { + ->whereNot(function (Builder $query) { $query->where('clearance', true) ->orWhere('price', '<', 10); }) @@ -638,7 +638,7 @@ Sometimes you may need to group several "where" clauses within parentheses in or $users = DB::table('users') ->where('name', '=', 'John') - ->where(function ($query) { + ->where(function (Builder $query) { $query->where('votes', '>', 100) ->orWhere('title', '=', 'Admin'); }) @@ -662,7 +662,7 @@ select * from users where name = 'John' and (votes > 100 or title = 'Admin') 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) { + ->whereExists(function (Builder $query) { $query->select(DB::raw(1)) ->from('orders') ->whereColumn('orders.user_id', 'users.id'); @@ -686,8 +686,9 @@ where exists ( 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; + use Illuminate\Database\Query\Builder; - $users = User::where(function ($query) { + $users = User::where(function (Builder $query) { $query->select('type') ->from('membership') ->whereColumn('membership.user_id', 'users.id') @@ -698,8 +699,9 @@ Sometimes you may need to construct a "where" clause that compares the results o Or, you may need to construct a "where" clause that compares a column to the results of a subquery. You may accomplish this by passing a column, operator, and closure to the `where` method. For example, the following query will retrieve all income records where the amount is less than average; use App\Models\Income; + use Illuminate\Database\Query\Builder; - $incomes = Income::where('amount', '<', function ($query) { + $incomes = Income::where('amount', '<', function (Builder $query) { $query->selectRaw('avg(i.amount)')->from('incomes as i'); })->get(); @@ -822,10 +824,10 @@ Alternatively, you may use the `limit` and `offset` methods. These methods are f 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'); + $role = $request->string('role'); $users = DB::table('users') - ->when($role, function ($query, $role) { + ->when($role, function (Builder $query, string $role) { $query->where('role_id', $role); }) ->get(); @@ -834,12 +836,12 @@ The `when` method only executes the given closure when the first argument is `tr 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: - $sortByVotes = $request->input('sort_by_votes'); + $sortByVotes = $request->boolean('sort_by_votes'); $users = DB::table('users') - ->when($sortByVotes, function ($query, $sortByVotes) { + ->when($sortByVotes, function (Builder $query, bool $sortByVotes) { $query->orderBy('votes'); - }, function ($query) { + }, function (Builder $query) { $query->orderBy('name'); }) ->get(); diff --git a/queues.md b/queues.md index 7b250d8c058..f21e7334f78 100644 --- a/queues.md +++ b/queues.md @@ -190,9 +190,6 @@ Job classes are very simple, normally containing only a `handle` method that is /** * Create a new job instance. - * - * @param App\Models\Podcast $podcast - * @return void */ public function __construct(Podcast $podcast) { @@ -201,11 +198,8 @@ Job classes are very simple, normally containing only a `handle` method that is /** * Execute the job. - * - * @param App\Services\AudioProcessor $processor - * @return void */ - public function handle(AudioProcessor $processor) + public function handle(AudioProcessor $processor): void { // Process uploaded podcast... } @@ -224,8 +218,9 @@ If you would like to take total control over how the container injects dependenc use App\Jobs\ProcessPodcast; use App\Services\AudioProcessor; + use Illuminate\Contracts\Foundation\Application; - $this->app->bindMethod([ProcessPodcast::class, 'handle'], function ($job, $app) { + $this->app->bindMethod([ProcessPodcast::class, 'handle'], function (ProcessPodcast $job, Application $app) { return $job->handle($app->make(AudioProcessor::class)); }); @@ -239,9 +234,6 @@ Because loaded relationships also get serialized, the serialized job string can /** * Create a new job instance. - * - * @param \App\Models\Podcast $podcast - * @return void */ public function __construct(Podcast $podcast) { @@ -296,10 +288,8 @@ In certain cases, you may want to define a specific "key" that makes the job uni /** * The unique ID of the job. - * - * @return string */ - public function uniqueId() + public function uniqueId(): string { return $this->product->id; } @@ -331,6 +321,7 @@ By default, unique jobs are "unlocked" after a job completes processing or fails 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 that returns the cache driver that should be used: + use Illuminate\Contracts\Cache\Repository; use Illuminate\Support\Facades\Cache; class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique @@ -339,10 +330,8 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t /** * Get the cache driver for the unique job lock. - * - * @return \Illuminate\Contracts\Cache\Repository */ - public function uniqueVia() + public function uniqueVia(): Repository { return Cache::driver('redis'); } @@ -360,10 +349,8 @@ Job middleware allow you to wrap custom logic around the execution of queued job /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () { info('Lock obtained...'); @@ -384,6 +371,7 @@ Instead of rate limiting in the handle method, we could define a job middleware namespace App\Jobs\Middleware; + use Closure; use Illuminate\Support\Facades\Redis; class RateLimited @@ -391,15 +379,13 @@ Instead of rate limiting in the handle method, we could define a job middleware /** * Process the queued job. * - * @param mixed $job - * @param callable $next - * @return mixed + * @param \Closure(object): void $next */ - public function handle($job, $next) + public function handle(object $job, Closure $next): void { Redis::throttle('key') ->block(0)->allow(1)->every(5) - ->then(function () use ($job, $next) { + ->then(function () use (object $job, Closure $next) { // Lock obtained... $next($job); @@ -420,9 +406,9 @@ After creating job middleware, they may be attached to a job by returning them f /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [new RateLimited]; } @@ -442,12 +428,10 @@ For example, you may wish to allow users to backup their data once per hour whil /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - RateLimiter::for('backups', function ($job) { + RateLimiter::for('backups', function (object $job) { return $job->user->vipCustomer() ? Limit::none() : Limit::perHour(1)->by($job->user->id); @@ -465,9 +449,9 @@ Once you have defined your rate limit, you may attach the rate limiter to your b /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [new RateLimited('backups')]; } @@ -479,9 +463,9 @@ If you do not want a job to be retried when it is rate limited, you may use the /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [(new RateLimited('backups'))->dontRelease()]; } @@ -501,9 +485,9 @@ For example, let's imagine you have a queued job that updates a user's credit sc /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [new WithoutOverlapping($this->user->id)]; } @@ -513,9 +497,9 @@ Any overlapping jobs of the same type will be released back to the queue. You ma /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [(new WithoutOverlapping($this->order->id))->releaseAfter(60)]; } @@ -525,9 +509,9 @@ If you wish to immediately delete any overlapping jobs so that they will not be /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [(new WithoutOverlapping($this->order->id))->dontRelease()]; } @@ -537,9 +521,9 @@ The `WithoutOverlapping` middleware is powered by Laravel's atomic lock feature. /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; } @@ -560,7 +544,7 @@ class ProviderIsDown // ... - public function middleware() + public function middleware(): array { return [ (new WithoutOverlapping("status:{$this->provider}"))->shared(), @@ -573,7 +557,7 @@ class ProviderIsUp // ... - public function middleware() + public function middleware(): array { return [ (new WithoutOverlapping("status:{$this->provider}"))->shared(), @@ -589,24 +573,23 @@ Laravel includes a `Illuminate\Queue\Middleware\ThrottlesExceptions` middleware For example, let's imagine a queued job that interacts with a third-party API that begins throwing exceptions. To throttle exceptions, you can return the `ThrottlesExceptions` middleware from your job's `middleware` method. Typically, this middleware should be paired with a job that implements [time based attempts](#time-based-attempts): + use DateTime; use Illuminate\Queue\Middleware\ThrottlesExceptions; /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [new ThrottlesExceptions(10, 5)]; } /** * Determine the time at which the job should timeout. - * - * @return \DateTime */ - public function retryUntil() + public function retryUntil(): DateTime { return now()->addMinutes(5); } @@ -620,9 +603,9 @@ When a job throws an exception but the exception threshold has not yet been reac /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [(new ThrottlesExceptions(10, 5))->backoff(5)]; } @@ -634,9 +617,9 @@ Internally, this middleware uses Laravel's cache system to implement rate limiti /** * Get the middleware the job should pass through. * - * @return array + * @return array */ - public function middleware() + public function middleware(): array { return [(new ThrottlesExceptions(10, 10))->by('key')]; } @@ -657,22 +640,22 @@ Once you have written your job class, you may dispatch it using the `dispatch` m use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\Request; + use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $podcast = Podcast::create(/* ... */); // ... ProcessPodcast::dispatch($podcast); + + return response()->noContent(); } } @@ -697,16 +680,14 @@ If you would like to specify that a job should not be immediately available for use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\Request; + use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $podcast = Podcast::create(/* ... */); @@ -714,6 +695,8 @@ If you would like to specify that a job should not be immediately available for ProcessPodcast::dispatch($podcast) ->delay(now()->addMinutes(10)); + + return response()->noContent(); } } @@ -751,22 +734,22 @@ If you would like to dispatch a job immediately (synchronously), you may use the use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\Request; + use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $podcast = Podcast::create(/* ... */); // Create podcast... ProcessPodcast::dispatchSync($podcast); + + return response()->noContent(); } } @@ -877,22 +860,22 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\Request; + use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $podcast = Podcast::create(/* ... */); // Create podcast... ProcessPodcast::dispatch($podcast)->onQueue('processing'); + + return response()->noContent(); } } @@ -914,8 +897,6 @@ Alternatively, you may specify the job's queue by calling the `onQueue` method w /** * Create a new job instance. - * - * @return void */ public function __construct() { @@ -936,22 +917,22 @@ If your application interacts with multiple queue connections, you may specify w use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\Request; + use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $podcast = Podcast::create(/* ... */); // Create podcast... ProcessPodcast::dispatch($podcast)->onConnection('sqs'); + + return response()->noContent(); } } @@ -979,10 +960,8 @@ Alternatively, you may specify the job's connection by calling the `onConnection /** * Create a new job instance. - * - * @return void */ - public function __construct() + public function __construct(): void { $this->onConnection('sqs'); } @@ -1025,12 +1004,12 @@ You may take a more granular approach by defining the maximum number of times a 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: + use DateTime; + /** * Determine the time at which the job should timeout. - * - * @return \DateTime */ - public function retryUntil() + public function retryUntil(): DateTime { return now()->addMinutes(10); } @@ -1067,10 +1046,8 @@ Sometimes you may wish to specify that a job may be attempted many times, but sh /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { Redis::throttle('key')->allow(10)->every(60)->then(function () { // Lock obtained, process the podcast... @@ -1143,10 +1120,8 @@ Sometimes you may wish to manually release a job back onto the queue so that it /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { // ... @@ -1164,10 +1139,8 @@ Occasionally you may need to manually mark a job as "failed". To do so, you may /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { // ... @@ -1214,10 +1187,8 @@ To define a batchable job, you should [create a queueable job](#creating-jobs) a /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { if ($this->batch()->cancelled()) { // Determine if the batch has been cancelled... @@ -1325,10 +1296,8 @@ In this example, we will use the `LoadImportBatch` job to hydrate the batch with /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { if ($this->batch()->cancelled()) { return; @@ -1398,10 +1367,8 @@ Sometimes you may need to cancel a given batch's execution. This can be accompli /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { if ($this->user->exceedsImportLimit()) { return $this->batch()->cancel(); @@ -1416,10 +1383,8 @@ As you may have noticed in previous examples, batched jobs should typically chec /** * Execute the job. - * - * @return void */ - public function handle() + public function handle(): void { if ($this->batch()->cancelled()) { return; @@ -1741,10 +1706,8 @@ If you require more complex logic for determining the job's backoff time, you ma /** * Calculate the number of seconds to wait before retrying the job. - * - * @return int */ - public function backoff() + public function backoff(): int { return 3; } @@ -1754,9 +1717,9 @@ You may easily configure "exponential" backoffs by returning an array of backoff /** * Calculate the number of seconds to wait before retrying the job. * - * @return array + * @return array */ - public function backoff() + public function backoff(): array { return [1, 5, 10]; } @@ -1791,9 +1754,6 @@ When a particular job fails, you may want to send an alert to your users or reve /** * Create a new job instance. - * - * @param \App\Models\Podcast $podcast - * @return void */ public function __construct(Podcast $podcast) { @@ -1802,22 +1762,16 @@ When a particular job fails, you may want to send an alert to your users or reve /** * Execute the job. - * - * @param \App\Services\AudioProcessor $processor - * @return void */ - public function handle(AudioProcessor $processor) + public function handle(AudioProcessor $processor): void { // Process uploaded podcast... } /** * Handle a job failure. - * - * @param \Throwable $exception - * @return void */ - public function failed(Throwable $exception) + public function failed(Throwable $exception): void { // Send user notification of failure, etc... } @@ -1954,20 +1908,16 @@ If you would like to register an event listener that will be invoked when a job { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Queue::failing(function (JobFailed $event) { // $event->connectionName @@ -2019,10 +1969,8 @@ use Illuminate\Support\Facades\Notification; /** * Register any other events for your application. - * - * @return void */ -public function boot() +public function boot(): void { Event::listen(function (QueueBusy $event) { Notification::route('mail', 'dev@example.com') @@ -2053,20 +2001,16 @@ Using the `before` and `after` methods on the `Queue` [facade](/docs/{{version}} { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Queue::before(function (JobProcessing $event) { // $event->connectionName diff --git a/rate-limiting.md b/rate-limiting.md index 6800b986016..b659e3778f9 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -88,11 +88,8 @@ You may reset the number of attempts for a given rate limiter key using the `cle /** * Mark the message as read. - * - * @param \App\Models\Message $message - * @return \App\Models\Message */ - public function read(Message $message) + public function read(Message $message): Message { $message->markAsRead(); diff --git a/redirects.md b/redirects.md index 260f6729336..faf87ebc15c 100644 --- a/redirects.md +++ b/redirects.md @@ -48,10 +48,8 @@ If you would like to customize the value that is placed in the route parameter, /** * Get the value of the model's route key. - * - * @return mixed */ - public function getRouteKey() + public function getRouteKey(): mixed { return $this->slug; } diff --git a/redis.md b/redis.md index 7f46e144571..0bd05fb9638 100644 --- a/redis.md +++ b/redis.md @@ -212,16 +212,14 @@ You may interact with Redis by calling various methods on the `Redis` [facade](/ use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Redis; + use Illuminate\View\View; class UserController extends Controller { /** * Show the profile for the given user. - * - * @param int $id - * @return \Illuminate\Http\Response */ - public function show($id) + public function show(string $id): View { return view('user.profile', [ 'user' => Redis::get('user:profile:'.$id) @@ -257,9 +255,10 @@ To obtain an instance of the default Redis connection, you may call the `connect 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; + use Redis; + use Illuminate\Support\Facades; - Redis::transaction(function ($redis) { + Facades\Redis::transaction(function (Redis $redis) { $redis->incr('user_visits', 1); $redis->incr('total_visits', 1); }); @@ -293,9 +292,10 @@ In this example, we will increment a counter, inspect its new value, and increme 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; + use Redis; + use Illuminate\Support\Facades; - Redis::pipeline(function ($pipe) { + Facades\Redis::pipeline(function (Redis $pipe) { for ($i = 0; $i < 1000; $i++) { $pipe->set("key:$i", $i); } @@ -333,12 +333,10 @@ First, let's setup a channel listener using the `subscribe` method. We'll place /** * Execute the console command. - * - * @return mixed */ - public function handle() + public function handle(): void { - Redis::subscribe(['test-channel'], function ($message) { + Redis::subscribe(['test-channel'], function (string $message) { echo $message; }); } @@ -361,10 +359,10 @@ Now we may publish messages to the channel using the `publish` method: 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) { + Redis::psubscribe(['*'], function (string $message, string $channel) { echo $message; }); - Redis::psubscribe(['users.*'], function ($message, $channel) { + Redis::psubscribe(['users.*'], function (string $message, string $channel) { echo $message; }); diff --git a/releases.md b/releases.md index 7b40db04f9d..2e49441fde3 100644 --- a/releases.md +++ b/releases.md @@ -333,10 +333,8 @@ Laravel now includes pagination views built using [Bootstrap 5](https://getboots /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Paginator::useBootstrapFive(); } diff --git a/requests.md b/requests.md index 191d41c1dd9..45991c6fa29 100644 --- a/requests.md +++ b/requests.md @@ -39,20 +39,20 @@ To obtain an instance of the current HTTP request via dependency injection, you namespace App\Http\Controllers; use Illuminate\Http\Request; + use Illuminate\Http\Response; class UserController extends Controller { /** * Store a new user. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $name = $request->input('name'); - // + // ... + + return response()->noContent(); } } @@ -61,7 +61,7 @@ As mentioned, you may also type-hint the `Illuminate\Http\Request` class on a ro use Illuminate\Http\Request; Route::get('/', function (Request $request) { - // + // ... }); @@ -80,19 +80,18 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` route namespace App\Http\Controllers; use Illuminate\Http\Request; + use Illuminate\Http\Response; class UserController extends Controller { /** * Update the specified user. - * - * @param \Illuminate\Http\Request $request - * @param string $id - * @return \Illuminate\Http\Response */ - public function update(Request $request, $id) + public function update(Request $request, string $id): Response { - // + // ... + + return response()->noContent(); } } @@ -114,13 +113,13 @@ The `path` method returns the request's path information. So, if the incoming re 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.*')) { - // + // ... } @@ -153,7 +152,7 @@ The `method` method will return the HTTP verb for the request. You may use the ` $method = $request->method(); if ($request->isMethod('post')) { - // + // ... } @@ -168,7 +167,7 @@ You may retrieve a request header from the `Illuminate\Http\Request` instance us 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` method may be used to retrieve a bearer token from the `Authorization` header. If no such header is present, an empty string will be returned: @@ -220,7 +219,7 @@ Once you have installed these libraries, you may obtain a PSR-7 request by type- use Psr\Http\Message\ServerRequestInterface; Route::get('/', function (ServerRequestInterface $request) { - // + // ... }); > **Note** @@ -245,7 +244,7 @@ Using the `collect` method, you may retrieve all of the incoming request's input The `collect` method also allows you to retrieve a subset of the incoming request input as a collection: - $request->collect('users')->each(function ($user) { + $request->collect('users')->each(function (string $user) { // ... }); @@ -359,24 +358,24 @@ If you need to retrieve a subset of the input data, you may use the `only` and ` 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')) { - // + // ... } When given an array, the `has` method will determine if all of the specified values are present: if ($request->has(['name', 'email'])) { - // + // ... } The `whenHas` method will execute the given closure if a value is present on the request: - $request->whenHas('name', function ($input) { - // + $request->whenHas('name', function (string $input) { + // ... }); A second closure may be passed to the `whenHas` method that will be executed if the specified value is not present on the request: - $request->whenHas('name', function ($input) { + $request->whenHas('name', function (string $input) { // The "name" value is present... }, function () { // The "name" value is not present... @@ -385,24 +384,24 @@ A second closure may be passed to the `whenHas` method that will be executed if The `hasAny` method returns `true` if any of the specified values are present: if ($request->hasAny(['name', 'email'])) { - // + // ... } If you would like to determine if a value is present on the request and is not an empty string, you may use the `filled` method: if ($request->filled('name')) { - // + // ... } The `whenFilled` method will execute the given closure if a value is present on the request and is not an empty string: - $request->whenFilled('name', function ($input) { - // + $request->whenFilled('name', function (string $input) { + // ... }); A second closure may be passed to the `whenFilled` method that will be executed if the specified value is not "filled": - $request->whenFilled('name', function ($input) { + $request->whenFilled('name', function (string $input) { // The "name" value is filled... }, function () { // The "name" value is not filled... @@ -411,10 +410,10 @@ A second closure may be passed to the `whenFilled` method that will be executed To determine if a given key is absent from the request, you may use the `missing` and `whenMissing` methods: if ($request->missing('name')) { - // + // ... } - $request->whenMissing('name', function ($input) { + $request->whenMissing('name', function (array $input) { // The "name" value is missing... }, function () { // The "name" value is present... @@ -496,20 +495,19 @@ If you would like to disable string trimming and empty string conversion for a s ```php use App\Http\Middleware\TrimStrings; +use Illuminate\Http\Request; use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { - TrimStrings::skipWhen(function ($request) { + TrimStrings::skipWhen(function (Request $request) { return $request->is('admin/*'); }); - ConvertEmptyStringsToNull::skipWhen(function ($request) { + ConvertEmptyStringsToNull::skipWhen(function (Request $request) { // ... }); } @@ -530,7 +528,7 @@ You may retrieve uploaded files from an `Illuminate\Http\Request` instance using You may determine if a file is present on the request using the `hasFile` method: if ($request->hasFile('photo')) { - // + // ... } @@ -539,7 +537,7 @@ You may determine if a file is present on the request using the `hasFile` method In addition to checking if the file is present, you may verify that there were no problems uploading the file via the `isValid` method: if ($request->file('photo')->isValid()) { - // + // ... } @@ -639,9 +637,9 @@ The `TrustHosts` middleware is already included in the `$middleware` stack of yo /** * Get the host patterns that should be trusted. * - * @return array + * @return array */ - public function hosts() + public function hosts(): array { return [ 'laravel.test', diff --git a/responses.md b/responses.md index 06b232abd95..0981a591710 100644 --- a/responses.md +++ b/responses.md @@ -192,10 +192,8 @@ If you would like to customize the value that is placed in the route parameter, /** * Get the value of the model's route key. - * - * @return mixed */ - public function getRouteKey() + public function getRouteKey(): mixed { return $this->slug; } @@ -330,12 +328,10 @@ If you would like to define a custom response that you can re-use in a variety o { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - Response::macro('caps', function ($value) { + Response::macro('caps', function (string $value) { return Response::make(strtoupper($value)); }); } diff --git a/routing.md b/routing.md index 4aa397aab80..82dffc5d3dc 100644 --- a/routing.md +++ b/routing.md @@ -67,11 +67,11 @@ The router allows you to register routes that respond to any HTTP verb: Sometimes you may need to register a route that responds to multiple HTTP verbs. You may do so using the `match` method. Or, you may even register a route that responds to all HTTP verbs using the `any` method: Route::match(['get', 'post'], '/', function () { - // + // ... }); Route::any('/', function () { - // + // ... }); > **Note** @@ -169,14 +169,14 @@ php artisan route:list --only-vendor Sometimes you will need to capture segments of the URI within your route. For example, you may need to capture a user's ID from the URL. You may do so by defining route parameters: - Route::get('/user/{id}', function ($id) { + Route::get('/user/{id}', function (string $id) { return 'User '.$id; }); You may define as many route parameters as required by your route: - Route::get('/posts/{post}/comments/{comment}', function ($postId, $commentId) { - // + Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) { + // ... }); Route parameters are always encased within `{}` braces and should consist of alphabetic characters. Underscores (`_`) are also acceptable within route parameter names. Route parameters are injected into route callbacks / controllers based on their order - the names of the route callback / controller arguments do not matter. @@ -188,7 +188,7 @@ If your route has dependencies that you would like the Laravel service container use Illuminate\Http\Request; - Route::get('/user/{id}', function (Request $request, $id) { + Route::get('/user/{id}', function (Request $request, string $id) { return 'User '.$id; }); @@ -197,11 +197,11 @@ If your route has dependencies that you would like the Laravel service container Occasionally you may need to specify a route parameter that may not always be present in the URI. You may do so by placing a `?` mark after the parameter name. Make sure to give the route's corresponding variable a default value: - Route::get('/user/{name?}', function ($name = null) { + Route::get('/user/{name?}', function (string $name = null) { return $name; }); - Route::get('/user/{name?}', function ($name = 'John') { + Route::get('/user/{name?}', function (string $name = 'John') { return $name; }); @@ -210,38 +210,38 @@ Occasionally you may need to specify a route parameter that may not always be pr You may constrain the format of your route parameters using the `where` method on a route instance. The `where` method accepts the name of the parameter and a regular expression defining how the parameter should be constrained: - Route::get('/user/{name}', function ($name) { - // + Route::get('/user/{name}', function (string $name) { + // ... })->where('name', '[A-Za-z]+'); - Route::get('/user/{id}', function ($id) { - // + Route::get('/user/{id}', function (string $id) { + // ... })->where('id', '[0-9]+'); - Route::get('/user/{id}/{name}', function ($id, $name) { - // + Route::get('/user/{id}/{name}', function (string $id, string $name) { + // ... })->where(['id' => '[0-9]+', 'name' => '[a-z]+']); For convenience, some commonly used regular expression patterns have helper methods that allow you to quickly add pattern constraints to your routes: - Route::get('/user/{id}/{name}', function ($id, $name) { - // + Route::get('/user/{id}/{name}', function (string $id, string $name) { + // ... })->whereNumber('id')->whereAlpha('name'); - Route::get('/user/{name}', function ($name) { - // + Route::get('/user/{name}', function (string $name) { + // ... })->whereAlphaNumeric('name'); - Route::get('/user/{id}', function ($id) { - // + Route::get('/user/{id}', function (string $id) { + // ... })->whereUuid('id'); - Route::get('/user/{id}', function ($id) { + Route::get('/user/{id}', function (string $id) { // })->whereUlid('id'); - Route::get('/category/{category}', function ($category) { - // + Route::get('/category/{category}', function (string $category) { + // ... })->whereIn('category', ['movie', 'song', 'painting']); If the incoming request does not match the route pattern constraints, a 404 HTTP response will be returned. @@ -253,17 +253,15 @@ If you would like a route parameter to always be constrained by a given regular /** * Define your route model bindings, pattern filters, etc. - * - * @return void */ - public function boot() + public function boot(): void { Route::pattern('id', '[0-9]+'); } Once the pattern has been defined, it is automatically applied to all routes using that parameter name: - Route::get('/user/{id}', function ($id) { + Route::get('/user/{id}', function (string $id) { // Only executed if {id} is numeric... }); @@ -272,7 +270,7 @@ Once the pattern has been defined, it is automatically applied to all routes usi The Laravel routing component allows all characters except `/` to be present within route parameter values. You must explicitly allow `/` to be part of your placeholder using a `where` condition regular expression: - Route::get('/search/{search}', function ($search) { + Route::get('/search/{search}', function (string $search) { return $search; })->where('search', '.*'); @@ -285,7 +283,7 @@ The Laravel routing component allows all characters except `/` to be present wit Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the `name` method onto the route definition: Route::get('/user/profile', function () { - // + // ... })->name('profile'); You may also specify route names for controller actions: @@ -313,16 +311,16 @@ Once you have assigned a name to a given route, you may use the route's name whe If the named route defines parameters, you may pass the parameters as the second argument to the `route` function. The given parameters will automatically be inserted into the generated URL in their correct positions: - Route::get('/user/{id}/profile', function ($id) { - // + Route::get('/user/{id}/profile', function (string $id) { + // ... })->name('profile'); $url = route('profile', ['id' => 1]); If you pass additional parameters in the array, those key / value pairs will automatically be added to the generated URL's query string: - Route::get('/user/{id}/profile', function ($id) { - // + Route::get('/user/{id}/profile', function (string $id) { + // ... })->name('profile'); $url = route('profile', ['id' => 1, 'photos' => 'yes']); @@ -337,17 +335,19 @@ If you pass additional parameters in the array, those key / value pairs will aut If you would like to determine if the current request was routed to a given named route, you may use the `named` method on a Route instance. For example, you may check the current route name from a route middleware: + use Closure; + use Illuminate\Http\Request; + use Symfony\Component\HttpFoundation\Response; + /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { if ($request->route()->named('profile')) { - // + // ... } return $next($request); @@ -393,8 +393,8 @@ If a group of routes all utilize the same [controller](/docs/{{version}}/control Route groups may also be used to handle subdomain routing. Subdomains may be assigned route parameters just like route URIs, allowing you to capture a portion of the subdomain for usage in your route or controller. The subdomain may be specified by calling the `domain` method before defining the group: Route::domain('{account}.example.com')->group(function () { - Route::get('user/{id}', function ($account, $id) { - // + Route::get('user/{id}', function (string $account, string $id) { + // ... }); }); @@ -482,10 +482,8 @@ If you would like model binding to always use a database column other than `id` /** * Get the route key for the model. - * - * @return string */ - public function getRouteKeyName() + public function getRouteKeyName(): string { return 'slug'; } @@ -580,10 +578,8 @@ You are not required to use Laravel's implicit, convention based model resolutio /** * Define your route model bindings, pattern filters, etc. - * - * @return void */ - public function boot() + public function boot(): void { Route::model('user', User::class); @@ -595,7 +591,7 @@ Next, define a route that contains a `{user}` parameter: use App\Models\User; Route::get('/users/{user}', function (User $user) { - // + // ... }); Since we have bound all `{user}` parameters to the `App\Models\User` model, an instance of that class will be injected into the route. So, for example, a request to `users/1` will inject the `User` instance from the database which has an ID of `1`. @@ -612,12 +608,10 @@ If you wish to define your own model binding resolution logic, you may use the ` /** * Define your route model bindings, pattern filters, etc. - * - * @return void */ - public function boot() + public function boot(): void { - Route::bind('user', function ($value) { + Route::bind('user', function (string $value) { return User::where('name', $value)->firstOrFail(); }); @@ -659,7 +653,7 @@ If a route is utilizing [implicit binding scoping](#implicit-model-binding-scopi Using the `Route::fallback` method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you would typically define the `fallback` route within your `routes/web.php` file, all middleware in the `web` middleware group will apply to the route. You are free to add additional middleware to this route as needed: Route::fallback(function () { - // + // ... }); > **Warning** @@ -681,10 +675,8 @@ Rate limiters are defined using the `RateLimiter` facade's `for` method. The `fo /** * Configure the rate limiters for the application. - * - * @return void */ - protected function configureRateLimiting() + protected function configureRateLimiting(): void { RateLimiter::for('global', function (Request $request) { return Limit::perMinute(1000); @@ -745,11 +737,11 @@ Rate limiters may be attached to routes or route groups using the `throttle` [mi Route::middleware(['throttle:uploads'])->group(function () { Route::post('/audio', function () { - // + // ... }); Route::post('/video', function () { - // + // ... }); }); diff --git a/sanctum.md b/sanctum.md index 0d901c8ee3c..88571900424 100644 --- a/sanctum.md +++ b/sanctum.md @@ -110,10 +110,8 @@ Then, you may instruct Sanctum to use your custom model via the `usePersonalAcce /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); } @@ -151,7 +149,7 @@ To issue a token, you may use the `createToken` method. The `createToken` method You may access all of the user's tokens using the `tokens` Eloquent relationship provided by the `HasApiTokens` trait: foreach ($user->tokens as $token) { - // + // ... } @@ -164,7 +162,7 @@ Sanctum allows you to assign "abilities" to tokens. Abilities serve a similar pu When handling an incoming request authenticated by Sanctum, you may determine if the token has a given ability using the `tokenCan` method: if ($user->tokenCan('server:update')) { - // + // ... } @@ -436,7 +434,7 @@ While testing, the `Sanctum::actingAs` method may be used to authenticate a user use App\Models\User; use Laravel\Sanctum\Sanctum; - public function test_task_list_can_be_retrieved() + public function test_task_list_can_be_retrieved(): void { Sanctum::actingAs( User::factory()->create(), diff --git a/scheduling.md b/scheduling.md index 3ff6f1bbdeb..6c0e313b9f0 100644 --- a/scheduling.md +++ b/scheduling.md @@ -41,11 +41,8 @@ You may define all of your scheduled tasks in the `schedule` method of your appl { /** * Define the application's command schedule. - * - * @param \Illuminate\Console\Scheduling\Schedule $schedule - * @return void */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { $schedule->call(function () { DB::table('recent_users')->delete(); @@ -142,7 +139,7 @@ These methods may be combined with additional constraints to create even more fi // Run once per week on Monday at 1 PM... $schedule->call(function () { - // + // ... })->weekly()->mondays()->at('13:00'); // Run hourly from 8 AM to 5 PM on weekdays... @@ -240,12 +237,12 @@ Using the `timezone` method, you may specify that a scheduled task's time should 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: + use DateTimeZone; + /** * Get the timezone that should be used by default for scheduled events. - * - * @return \DateTimeZone|string|null */ - protected function scheduleTimezone() + protected function scheduleTimezone(): DateTimeZone|string|null { return 'America/Chicago'; } diff --git a/scout.md b/scout.md index a391bf20c1f..66642ef9811 100644 --- a/scout.md +++ b/scout.md @@ -141,10 +141,8 @@ Each Eloquent model is synced with a given search "index", which contains all of /** * Get the name of the index associated with the model. - * - * @return string */ - public function searchableAs() + public function searchableAs(): string { return 'posts_index'; } @@ -169,9 +167,9 @@ By default, the entire `toArray` form of a given model will be persisted to its /** * Get the indexable data array for the model. * - * @return array + * @return array */ - public function toSearchableArray() + public function toSearchableArray(): array { $array = $this->toArray(); @@ -252,20 +250,16 @@ By default, Scout will use the primary key of the model as the model's unique ID /** * Get the value used to index the model. - * - * @return mixed */ - public function getScoutKey() + public function getScoutKey(): mixed { return $this->email; } /** * Get the key name used to index the model. - * - * @return mixed */ - public function getScoutKeyName() + public function getScoutKeyName(): mixed { return 'email'; } @@ -281,6 +275,7 @@ When searching, Scout will typically use the default search engine specified in namespace App\Models; use Illuminate\Database\Eloquent\Model; + use Laravel\Scout\Engines\Engine; use Laravel\Scout\EngineManager; use Laravel\Scout\Searchable; @@ -290,10 +285,8 @@ When searching, Scout will typically use the default search engine specified in /** * Get the engine used to index the model. - * - * @return \Laravel\Scout\Engines\Engine */ - public function searchableUsing() + public function searchableUsing(): Engine { return app(EngineManager::class)->engine('meilisearch'); } @@ -342,11 +335,11 @@ use Laravel\Scout\Attributes\SearchUsingPrefix; /** * Get the indexable data array for the model. * - * @return array + * @return array */ #[SearchUsingPrefix(['id', 'email'])] #[SearchUsingFullText(['bio'])] -public function toSearchableArray() +public function toSearchableArray(): array { return [ 'id' => $this->id, @@ -404,11 +397,8 @@ If you would like to modify the query that is used to retrieve all of your model /** * Modify the query used to retrieve models when making all of the models searchable. - * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder */ - protected function makeAllSearchableUsing($query) + protected function makeAllSearchableUsing(Builder $query): Builder { return $query->with('author'); } @@ -512,10 +502,8 @@ Sometimes you may need to only make a model searchable under certain conditions. /** * Determine if the model should be searchable. - * - * @return bool */ - public function shouldBeSearchable() + public function shouldBeSearchable(): bool { return $this->isPublished(); } @@ -660,9 +648,10 @@ After Scout retrieves a list of matching Eloquent models from your application's ```php use App\Models\Order; +use Illuminate\Database\Eloquent\Builder; $orders = Order::search('Star Trek') - ->query(fn ($query) => $query->with('invoices')) + ->query(fn (Builder $query) => $query->with('invoices')) ->get(); ``` @@ -694,15 +683,13 @@ You may find it helpful to review the implementations of these methods on the `L 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 App\ScoutExtensions\MySqlSearchEngine; use Laravel\Scout\EngineManager; /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { resolve(EngineManager::class)->extend('mysql', function () { return new MySqlSearchEngine; @@ -724,10 +711,8 @@ If you would like to define a custom Scout search builder method, you may use th /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Builder::macro('count', function () { return $this->engine()->getTotalCount( diff --git a/seeding.md b/seeding.md index aa3cee553dd..5f305e2a1b7 100644 --- a/seeding.md +++ b/seeding.md @@ -41,10 +41,8 @@ As an example, let's modify the default `DatabaseSeeder` class and add a databas { /** * Run the database seeders. - * - * @return void */ - public function run() + public function run(): void { DB::table('users')->insert([ 'name' => Str::random(10), @@ -68,10 +66,8 @@ For example, let's create 50 users that each has one related post: /** * Run the database seeders. - * - * @return void */ - public function run() + public function run(): void { User::factory() ->count(50) @@ -86,10 +82,8 @@ Within the `DatabaseSeeder` class, you may use the `call` method to execute addi /** * Run the database seeders. - * - * @return void */ - public function run() + public function run(): void { $this->call([ UserSeeder::class, @@ -116,10 +110,8 @@ While running seeds, you may want to prevent models from dispatching events. You /** * Run the database seeders. - * - * @return void */ - public function run() + public function run(): void { $this->call([ UserSeeder::class, diff --git a/session.md b/session.md index 086ef9f070d..5b6cc64e12c 100644 --- a/session.md +++ b/session.md @@ -50,7 +50,10 @@ The session `driver` configuration option defines where session data will be sto 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) { + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Support\Facades\Schema; + + Schema::create('sessions', function (Blueprint $table) { $table->string('id')->primary(); $table->foreignId('user_id')->nullable()->index(); $table->string('ip_address', 45)->nullable(); @@ -89,21 +92,22 @@ There are two primary ways of working with session data in Laravel: the global ` use App\Http\Controllers\Controller; use Illuminate\Http\Request; + use Illuminate\View\View; class UserController extends Controller { /** * Show the profile for the given user. - * - * @param Request $request - * @param int $id - * @return Response */ - public function show(Request $request, $id) + public function show(Request $request, string $id): View { $value = $request->session()->get('key'); - // + // ... + + $user = $this->users->find($id); + + return view('user.profile', ['user' => $user]); } } @@ -147,19 +151,19 @@ If you would like to retrieve all the data in the session, you may use the `all` To determine if an item is present in the session, you may use the `has` method. The `has` method returns `true` if the item is present and is not `null`: if ($request->session()->has('users')) { - // + // ... } 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')) { - // + // ... } To determine if an item is not present in the session, you may use the `missing` method. The `missing` method returns `true` if the item is not present: if ($request->session()->missing('users')) { - // + // ... } @@ -254,11 +258,11 @@ By default, Laravel allows requests using the same session to execute concurrent To mitigate this, Laravel provides functionality that allows you to limit concurrent requests for a given session. To get started, you may simply chain the `block` method onto your route definition. In this example, an incoming request to the `/profile` endpoint would acquire a session lock. While this lock is being held, any incoming requests to the `/profile` or `/order` endpoints which share the same session ID will wait for the first request to finish executing before continuing their execution: Route::post('/profile', function () { - // + // ... })->block($lockSeconds = 10, $waitSeconds = 10) Route::post('/order', function () { - // + // ... })->block($lockSeconds = 10, $waitSeconds = 10) The `block` method accepts two optional arguments. The first argument accepted by the `block` method is the maximum number of seconds the session lock should be held for before it is released. Of course, if the request finishes executing before this time the lock will be released earlier. @@ -268,7 +272,7 @@ The second argument accepted by the `block` method is the number of seconds a re If neither of these arguments is passed, the lock will be obtained for a maximum of 10 seconds and requests will wait a maximum of 10 seconds while attempting to obtain a lock: Route::post('/profile', function () { - // + // ... })->block() @@ -319,6 +323,7 @@ Once your driver has been implemented, you are ready to register it with Laravel namespace App\Providers; use App\Extensions\MongoSessionHandler; + use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\Session; use Illuminate\Support\ServiceProvider; @@ -326,22 +331,18 @@ Once your driver has been implemented, you are ready to register it with Laravel { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - Session::extend('mongo', function ($app) { + Session::extend('mongo', function (Application $app) { // Return an implementation of SessionHandlerInterface... return new MongoSessionHandler; }); diff --git a/telescope.md b/telescope.md index 77d2fd39fe4..a9f0858400a 100644 --- a/telescope.md +++ b/telescope.md @@ -78,10 +78,8 @@ After running `telescope:install`, you should remove the `TelescopeServiceProvid /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { if ($this->app->environment('local')) { $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); @@ -126,16 +124,16 @@ By default, all entries older than 24 hours will be pruned. You may use the `hou 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: + use App\Models\User; + /** * Register the Telescope gate. * * This gate determines who can access Telescope in non-local environments. - * - * @return void */ - protected function gate() + protected function gate(): void { - Gate::define('viewTelescope', function ($user) { + Gate::define('viewTelescope', function (User $user) { return in_array($user->email, [ 'taylor@laravel.com', ]); @@ -181,10 +179,8 @@ You may filter the data that is recorded by Telescope via the `filter` closure t /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { $this->hideSensitiveRequestDetails(); @@ -207,14 +203,13 @@ You may filter the data that is recorded by Telescope via the `filter` closure t 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\IncomingEntry; use Laravel\Telescope\Telescope; /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { $this->hideSensitiveRequestDetails(); @@ -223,7 +218,7 @@ While the `filter` closure filters data for individual entries, you may use the return true; } - return $entries->contains(function ($entry) { + return $entries->contains(function (IncomingEntry $entry) { return $entry->isReportableException() || $entry->isFailedJob() || $entry->isScheduledTask() || @@ -243,10 +238,8 @@ Telescope allows you to search entries by "tag". Often, tags are Eloquent model /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { $this->hideSensitiveRequestDetails(); @@ -429,14 +422,12 @@ The Telescope dashboard displays the user avatar for the user that was authentic /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { // ... - Telescope::avatar(function ($id, $email) { + Telescope::avatar(function (string $id, string $email) { return '/avatars/'.User::find($id)->avatar_path; }); } diff --git a/testing.md b/testing.md index 886cc133f5b..00572e4881f 100644 --- a/testing.md +++ b/testing.md @@ -72,10 +72,8 @@ Once the test has been generated, you may define test methods as you normally wo { /** * A basic test example. - * - * @return void */ - public function test_basic_test() + public function test_basic_test(): void { $this->assertTrue(true); } @@ -148,34 +146,33 @@ Using the `ParallelTesting` facade, you may specify code to be executed on the ` use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\ServiceProvider; + use PHPUnit\Framework\TestCase; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - ParallelTesting::setUpProcess(function ($token) { + ParallelTesting::setUpProcess(function (int $token) { // ... }); - ParallelTesting::setUpTestCase(function ($token, $testCase) { + ParallelTesting::setUpTestCase(function (int $token, TestCase $testCase) { // ... }); // Executed when a test database is created... - ParallelTesting::setUpTestDatabase(function ($database, $token) { + ParallelTesting::setUpTestDatabase(function (string $database, int $token) { Artisan::call('db:seed'); }); - ParallelTesting::tearDownTestCase(function ($token, $testCase) { + ParallelTesting::tearDownTestCase(function (int $token, TestCase $testCase) { // ... }); - ParallelTesting::tearDownProcess(function ($token) { + ParallelTesting::tearDownProcess(function (int $token) { // ... }); } diff --git a/urls.md b/urls.md index 504081eb9f4..26c655fc46d 100644 --- a/urls.md +++ b/urls.md @@ -54,7 +54,7 @@ Each of these methods may also be accessed via the `URL` [facade](/docs/{{versio 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 (Post $post) { - // + // ... })->name('post.show'); To generate a URL to this route, you may use the `route` helper like so: @@ -66,7 +66,7 @@ To generate a URL to this route, you may use the `route` helper like so: Of course, the `route` helper may also be used to generate URLs for routes with multiple parameters: Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) { - // + // ... })->name('comment.show'); echo route('comment.show', ['post' => 1, 'comment' => 3]); @@ -154,10 +154,8 @@ When someone visits a signed URL that has expired, they will receive a generic e /** * Register the exception handling callbacks for the application. - * - * @return void */ - public function register() + public function register(): void { $this->renderable(function (InvalidSignatureException $e) { return response()->view('error.link-expired', [], 403); @@ -183,7 +181,7 @@ If the controller method accepts route parameters, you may pass an associative a For some applications, you may wish to specify request-wide default values for certain URL parameters. For example, imagine many of your routes define a `{locale}` parameter: Route::get('/{locale}/posts', function () { - // + // ... })->name('post.index'); It is cumbersome to always pass the `locale` every time you call the `route` helper. So, you may use the `URL::defaults` method to define a default value for this parameter that will always be applied during the current request. You may wish to call this method from a [route middleware](/docs/{{version}}/middleware#assigning-middleware-to-routes) so that you have access to the current request: @@ -193,18 +191,18 @@ It is cumbersome to always pass the `locale` every time you call the `route` hel namespace App\Http\Middleware; use Closure; + use Illuminate\Http\Request; use Illuminate\Support\Facades\URL; + use Symfony\Component\HttpFoundation\Response; class SetDefaultLocaleForUrls { /** - * Handle the incoming request. + * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return \Illuminate\Http\Response + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { URL::defaults(['locale' => $request->user()->locale]); diff --git a/validation.md b/validation.md index 71d22e45726..518cb7da21a 100644 --- a/validation.md +++ b/validation.md @@ -70,29 +70,30 @@ Next, let's take a look at a simple controller that handles incoming requests to namespace App\Http\Controllers; use App\Http\Controllers\Controller; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; + use Illuminate\View\View; class PostController extends Controller { /** * Show the form to create a new blog post. - * - * @return \Illuminate\View\View */ - public function create() + public function create(): View { return view('post.create'); } /** * Store a new blog post. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): RedirectResponse { // Validate and store the blog post... + + $post = /** ... */ + + return to_route('post.show', ['post' => $post->id]); } } @@ -107,11 +108,8 @@ To get a better understanding of the `validate` method, let's jump back into the /** * Store a new blog post. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(Request $request): Response { $validated = $request->validate([ 'title' => 'required|unique:posts|max:255', @@ -119,6 +117,8 @@ To get a better understanding of the `validate` method, let's jump back into the ]); // The blog post is valid... + + return response()->noContent(); } 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. @@ -307,9 +307,9 @@ As you might have guessed, the `authorize` method is responsible for determining /** * Get the validation rules that apply to the request. * - * @return array + * @return array */ - public function rules() + public function rules(): array { return [ 'title' => 'required|unique:posts|max:255', @@ -324,11 +324,8 @@ So, how are the validation rules evaluated? All you need to do is type-hint the /** * Store a new blog post. - * - * @param \App\Http\Requests\StorePostRequest $request - * @return Illuminate\Http\Response */ - public function store(StorePostRequest $request) + public function store(StorePostRequest $request): Response { // The incoming request is valid... @@ -338,6 +335,10 @@ So, how are the validation rules evaluated? All you need to do is type-hint the // Retrieve a portion of the validated input data... $validated = $request->safe()->only(['name', 'email']); $validated = $request->safe()->except(['name', 'email']); + + // Store the blog post... + + return response()->noContent(); } 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](#validation-error-response-format). @@ -347,15 +348,14 @@ If validation fails, a redirect response will be generated to send the user back 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: + use Illuminate\Validation\Validator; + /** * Configure the validator instance. - * - * @param \Illuminate\Validation\Validator $validator - * @return void */ - public function withValidator($validator) + public function withValidator(Validator $validator): void { - $validator->after(function ($validator) { + $validator->after(function (Validator $validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } @@ -405,10 +405,8 @@ The form request class also contains an `authorize` method. Within this method, /** * Determine if the user is authorized to make this request. - * - * @return bool */ - public function authorize() + public function authorize(): bool { $comment = Comment::find($this->route('comment')); @@ -429,10 +427,8 @@ If you plan to handle authorization logic for the request in another part of you /** * Determine if the user is authorized to make this request. - * - * @return bool */ - public function authorize() + public function authorize(): bool { return true; } @@ -448,9 +444,9 @@ You may customize the error messages used by the form request by overriding the /** * Get the error messages for the defined validation rules. * - * @return array + * @return array */ - public function messages() + public function messages(): array { return [ 'title.required' => 'A title is required', @@ -466,9 +462,9 @@ Many of Laravel's built-in validation rule error messages contain an `:attribute /** * Get custom attributes for validator errors. * - * @return array + * @return array */ - public function attributes() + public function attributes(): array { return [ 'email' => 'email address', @@ -484,10 +480,8 @@ If you need to prepare or sanitize any data from the request before you apply yo /** * Prepare the data for validation. - * - * @return void */ - protected function prepareForValidation() + protected function prepareForValidation(): void { $this->merge([ 'slug' => Str::slug($this->slug), @@ -505,17 +499,15 @@ If you do not want to use the `validate` method on the request, you may create a use App\Http\Controllers\Controller; use Illuminate\Http\Request; + use Illuminate\Http\Response; use Illuminate\Support\Facades\Validator; class PostController extends Controller { /** * Store a new blog post. - * - * @param Request $request - * @return Response */ - public function store(Request $request) + public function store(Request $request): Response { $validator = Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', @@ -536,6 +528,8 @@ If you do not want to use the `validate` method on the request, you may create a $validated = $validator->safe()->except(['name', 'email']); // Store the blog post... + + return response()->noContent(); } } @@ -622,9 +616,12 @@ Many of Laravel's built-in error messages include an `:attribute` placeholder th 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(/* ... */); + use Illuminate\Support\Facades; + use Illuminate\Validation\Validator; - $validator->after(function ($validator) { + $validator = Facades\Validator::make(/* ... */); + + $validator->after(function (Validator $validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add( 'field', 'Something is wrong with this field!' @@ -633,7 +630,7 @@ You may also attach callbacks to be run after validation is completed. This allo }); if ($validator->fails()) { - // + // ... } @@ -657,7 +654,7 @@ In addition, the `Illuminate\Support\ValidatedInput` instance may be iterated ov // Validated data may be iterated... foreach ($request->safe() as $key => $value) { - // + // ... } // Validated data may be accessed as an array... @@ -693,13 +690,13 @@ To retrieve the first error message for a given field, use the `first` method: If you need to retrieve an array of all the messages for a given field, use the `get` method: foreach ($errors->get('email') as $message) { - // + // ... } If you are validating an array form field, you may retrieve all of the messages for each of the array elements using the `*` character: foreach ($errors->get('attachments.*') as $message) { - // + // ... } @@ -708,7 +705,7 @@ If you are validating an array form field, you may retrieve all of the messages To retrieve an array of all messages for all fields, use the `all` method: foreach ($errors->all() as $message) { - // + // ... } @@ -717,7 +714,7 @@ To retrieve an array of all messages for all fields, use the `all` method: The `has` method may be used to determine if any error messages exist for a given field: if ($errors->has('email')) { - // + // ... } @@ -1220,13 +1217,14 @@ 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\Database\Query\Builder; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; Validator::make($data, [ 'email' => [ 'required', - Rule::exists('staff')->where(function ($query) { + Rule::exists('staff')->where(function (Builder $query) { return $query->where('account_id', 1); }), ], @@ -1613,6 +1611,7 @@ Sometimes, you may wish to ignore a given ID during unique validation. For examp 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\Database\Eloquent\Builder; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; @@ -1636,13 +1635,13 @@ If your table uses a primary key column name other than `id`, you may specify th By default, the `unique` rule will check the uniqueness of the column matching the name of the attribute being validated. However, you may pass a different column name as the second argument to the `unique` method: - Rule::unique('users', 'email_address')->ignore($user->id), + Rule::unique('users', 'email_address')->ignore($user->id) **Adding Additional Where Clauses:** 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(fn ($query) => $query->where('account_id', 1)) + 'email' => Rule::unique('users')->where(fn (Builder $query) => $query->where('account_id', 1)) #### uppercase @@ -1716,13 +1715,15 @@ Sometimes you may wish to add validation rules based on more complex conditional 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. - $validator->sometimes('reason', 'required|max:500', function ($input) { + use Illuminate\Support\Fluent; + + $validator->sometimes('reason', 'required|max:500', function (Fluent $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: - $validator->sometimes(['reason', 'cost'], 'required', function ($input) { + $validator->sometimes(['reason', 'cost'], 'required', function (Fluent $input) { return $input->games >= 100; }); @@ -1747,11 +1748,11 @@ Sometimes you may want to validate a field based on another field in the same ne ], ]; - $validator->sometimes('channels.*.address', 'email', function ($input, $item) { + $validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) { return $item->type === 'email'; }); - $validator->sometimes('channels.*.address', 'url', function ($input, $item) { + $validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) { return $item->type !== 'email'; }); @@ -1814,7 +1815,7 @@ Sometimes you may need to access the value for a given nested array element when use Illuminate\Validation\Rule; $validator = Validator::make($request->all(), [ - 'companies.*.id' => Rule::forEach(function ($value, $attribute) { + 'companies.*.id' => Rule::forEach(function (string|null $value, string $attribute) { return [ Rule::exists(Company::class, 'id'), new HasPermission('manage-company', $value), @@ -1951,10 +1952,8 @@ use Illuminate\Validation\Rules\Password; /** * Bootstrap any application services. - * - * @return void */ -public function boot() +public function boot(): void { Password::defaults(function () { $rule = Password::min(8); @@ -1998,19 +1997,15 @@ Once the rule has been created, we are ready to define its behavior. A rule obje namespace App\Rules; + use Closure; use Illuminate\Contracts\Validation\InvokableRule; class Uppercase implements InvokableRule { /** * Run the validation rule. - * - * @param string $attribute - * @param mixed $value - * @param \Closure $fail - * @return void */ - public function __invoke($attribute, $value, $fail) + public function __invoke(string $attribute, mixed $value, Closure $fail): void { if (strtoupper($value) !== $value) { $fail('The :attribute must be uppercase.'); @@ -2056,7 +2051,7 @@ If your custom validation rule class needs to access all of the other data under /** * All of the data under validation. * - * @var array + * @var array */ protected $data = []; @@ -2065,10 +2060,10 @@ If your custom validation rule class needs to access all of the other data under /** * Set the data under validation. * - * @param array $data + * @param array $data * @return $this */ - public function setData($data) + public function setData(array $data): static { $this->data = $data; @@ -2084,6 +2079,7 @@ Or, if your validation rule requires access to the validator instance performing use Illuminate\Contracts\Validation\InvokableRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; + use Illuminate\Validation\Validator; class Uppercase implements InvokableRule, ValidatorAwareRule { @@ -2098,11 +2094,8 @@ Or, if your validation rule requires access to the validator instance performing /** * Set the current validator. - * - * @param \Illuminate\Validation\Validator $validator - * @return $this */ - public function setValidator($validator) + public function setValidator(Validator $validator): static { $this->validator = $validator; @@ -2121,7 +2114,7 @@ If you only need the functionality of a custom rule once throughout your applica 'title' => [ 'required', 'max:255', - function ($attribute, $value, $fail) { + function (string $attribute, string|null $value, Closure $fail) { if ($value === 'foo') { $fail('The '.$attribute.' is invalid.'); } diff --git a/verification.md b/verification.md index 3b489041d27..2e0c85b2c64 100644 --- a/verification.md +++ b/verification.md @@ -135,14 +135,12 @@ To get started, pass a closure to the `toMailUsing` method provided by the `Illu /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { // ... - VerifyEmail::toMailUsing(function ($notifiable, $url) { + VerifyEmail::toMailUsing(function (object $notifiable, string $url) { return (new MailMessage) ->subject('Verify Email Address') ->line('Click the button below to verify your email address.') diff --git a/views.md b/views.md index f3c0da56b50..e0ad54f993a 100644 --- a/views.md +++ b/views.md @@ -91,7 +91,7 @@ If you need to determine if a view exists, you may use the `View` facade. The `e use Illuminate\Support\Facades\View; if (View::exists('emails.customer')) { - // + // ... } @@ -124,20 +124,16 @@ Occasionally, you may need to share data with all views that are rendered by you { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { View::share('key', 'value'); } @@ -157,34 +153,35 @@ We'll use the `View` facade's `composer` method to register the view composer. L namespace App\Providers; use App\View\Composers\ProfileComposer; - use Illuminate\Support\Facades\View; + use Illuminate\Support\Facades; use Illuminate\Support\ServiceProvider; + use Illuminate\View\View; class ViewServiceProvider extends ServiceProvider { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + // ... } /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { // Using class based composers... - View::composer('profile', ProfileComposer::class); + Facades\View::composer('profile', ProfileComposer::class); // Using closure based composers... - View::composer('dashboard', function ($view) { - // + Facades\View::composer('welcome', function (View $view) { + // ... + }); + + Facades\View::composer('dashboard', function (View $view) { + // ... }); } } @@ -212,9 +209,6 @@ Now that we have registered the composer, the `compose` method of the `App\View\ /** * Create a new profile composer. - * - * @param \App\Repositories\UserRepository $users - * @return void */ public function __construct(UserRepository $users) { @@ -223,11 +217,8 @@ Now that we have registered the composer, the `compose` method of the `App\View\ /** * Bind data to the view. - * - * @param \Illuminate\View\View $view - * @return void */ - public function compose(View $view) + public function compose(View $view): void { $view->with('count', $this->users->count()); } @@ -241,6 +232,7 @@ As you can see, all view composers are resolved via the [service container](/doc 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\Views\Composers\MultiComposer; + use Illuminate\Support\Facades\View; View::composer( ['profile', 'dashboard'], @@ -249,8 +241,11 @@ You may attach a view composer to multiple views at once by passing an array of The `composer` method also accepts the `*` character as a wildcard, allowing you to attach a composer to all views: - View::composer('*', function ($view) { - // + use Illuminate\Support\Facades; + use Illuminate\View\View; + + Facades\View::composer('*', function (View $view) { + // ... }); diff --git a/vite.md b/vite.md index df2b312df80..7e1214c109e 100644 --- a/vite.md +++ b/vite.md @@ -471,12 +471,10 @@ It is common in JavaScript applications to [create aliases](#aliases) to regular /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { - Vite::macro('image', fn ($asset) => $this->asset("resources/images/{$asset}")); + Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}")); } Once a macro has been defined, it can be invoked within your templates. For example, we can use the `image` macro defined above to reference an asset located at `resources/images/logo.png`: @@ -529,7 +527,7 @@ use Tests\TestCase; class ExampleTest extends TestCase { - public function test_without_vite_example() + public function test_without_vite_example(): void { $this->withoutVite(); @@ -613,18 +611,18 @@ If you wish to include a [`nonce` attribute](https://developer.mozilla.org/en-US namespace App\Http\Middleware; use Closure; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Vite; +use Symfony\Component\HttpFoundation\Response; class AddContentSecurityPolicyHeaders { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next, string $role): Response { Vite::useCspNonce(); From ea42af1f824f5422c1dff41d766bf0eb3ef4992b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Jan 2023 09:17:42 -0600 Subject: [PATCH 0569/2609] whenHas --- eloquent-resources.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eloquent-resources.md b/eloquent-resources.md index 978d7fb6737..f994f01f466 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -528,6 +528,10 @@ The `when` method also accepts a closure as its second argument, allowing you to return 'secret-value'; }), +The `whenHas` method may be used to include an attribute if it is actually present on the underlying model: + + 'name' => $this->whenHas('name'), + Additionally, the `whenNotNull` method may be used to include an attribute in the resource response if the attribute is not null: 'name' => $this->whenNotNull($this->name), From 2221357bb3ca6ddf54a8dbad1db4547b6029dcf4 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 3 Jan 2023 17:15:06 +0000 Subject: [PATCH 0570/2609] [10.x] Adds upgrade guide skeleton (#8433) * Adds upgrade guide skeleton * Adds missing section --- upgrade.md | 701 ++--------------------------------------------------- 1 file changed, 15 insertions(+), 686 deletions(-) diff --git a/upgrade.md b/upgrade.md index 9af2e0b4edb..e575d969162 100644 --- a/upgrade.md +++ b/upgrade.md @@ -1,6 +1,6 @@ # Upgrade Guide -- [Upgrading To 9.0 From 8.x](#upgrade-9.0) +- [Upgrading To 10.0 From 9.x](#upgrade-10.0) ## High Impact Changes @@ -8,8 +8,6 @@
    - [Updating Dependencies](#updating-dependencies) -- [Flysystem 3.x](#flysystem-3) -- [Symfony Mailer](#symfony-mailer)
    @@ -18,24 +16,20 @@
    -- [Belongs To Many `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods](#belongs-to-many-first-or-new) -- [Custom Casts & `null`](#custom-casts-and-null) -- [Default HTTP Client Timeout](#http-client-default-timeout) -- [PHP Return Types](#php-return-types) -- [Postgres "Schema" Configuration](#postgres-schema-configuration) -- [The `assertDeleted` Method](#the-assert-deleted-method) -- [The `lang` Directory](#the-lang-directory) -- [The `password` Rule](#the-password-rule) -- [The `when` / `unless` Methods](#when-and-unless-methods) -- [Unvalidated Array Keys](#unvalidated-array-keys) +
    + + +## Low Impact Changes + +
    - -## Upgrading To 9.0 From 8.x + +## Upgrading To 10.0 From 9.x - -#### Estimated Upgrade Time: 30 Minutes + +#### Estimated Upgrade Time: ?? Minutes > **Note** > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. @@ -45,9 +39,9 @@ **Likelihood Of Impact: High** -#### PHP 8.0.2 Required +#### PHP 8.1.0 Required -Laravel now requires PHP 8.0.2 or greater. +Laravel now requires PHP 8.1.0 or greater. #### Composer Dependencies @@ -55,676 +49,11 @@ You should update the following dependencies in your application's `composer.jso
    -- `laravel/framework` to `^9.0` -- `nunomaduro/collision` to `^6.1` - -
    - -In addition, please replace `facade/ignition` with `"spatie/laravel-ignition": "^1.0"` and `pusher/pusher-php-server` (if applicable) with `"pusher/pusher-php-server": "^5.0"` in your application's `composer.json` file. - -Furthermore, the following first-party packages have received new major releases to support Laravel 9.x. If applicable, you should read their individual upgrade guides before upgrading: - -
    - -- [Vonage Notification Channel (v3.0)](https://github.com/laravel/vonage-notification-channel/blob/3.x/UPGRADE.md) (Replaces Nexmo) +- `laravel/framework` to `^10.0`
    -Finally, examine any other third-party packages consumed by your application and verify you are using the proper version for Laravel 9 support. - - -#### PHP Return Types - -PHP is beginning to transition to requiring return type definitions on PHP methods such as `offsetGet`, `offsetSet`, etc. In light of this, Laravel 9 has implemented these return types in its code base. Typically, this should not affect user written code; however, if you are overriding one of these methods by extending Laravel's core classes, you will need to add these return types to your own application or package code: - -
    - -- `count(): int` -- `getIterator(): Traversable` -- `getSize(): int` -- `jsonSerialize(): array` -- `offsetExists($key): bool` -- `offsetGet($key): mixed` -- `offsetSet($key, $value): void` -- `offsetUnset($key): void` - -
    - -In addition, return types were added to methods implementing PHP's `SessionHandlerInterface`. Again, it is unlikely that this change affects your own application or package code: - -
    - -- `open($savePath, $sessionName): bool` -- `close(): bool` -- `read($sessionId): string|false` -- `write($sessionId, $data): bool` -- `destroy($sessionId): bool` -- `gc($lifetime): int` - -
    - - -### Application - - -#### The `Application` Contract - -**Likelihood Of Impact: Low** - -The `storagePath` method of the `Illuminate\Contracts\Foundation\Application` interface has been updated to accept a `$path` argument. If you are implementing this interface you should update your implementation accordingly: - - public function storagePath($path = ''); - -Similarly, the `langPath` method of the `Illuminate\Foundation\Application` class has been updated to accept a `$path` argument: - - public function langPath($path = ''); - -#### Exception Handler `ignore` Method - -**Likelihood Of Impact: Low** - -The exception handler's `ignore` method is now `public` instead of `protected`. This method is not included in the default application skeleton; however, if you have manually defined this method you should update its visibility to `public`: - -```php -public function ignore(string $class); -``` - -#### Exception Handler Contract Binding - -**Likelihood Of Impact: Very Low** - -Previously, in order to override the default Laravel exception handler, custom implementations were bound into the service container using the `\App\Exceptions\Handler::class` type. However, you should now bind custom implementations using the `\Illuminate\Contracts\Debug\ExceptionHandler::class` type. - -### Blade - -#### Lazy Collections & The `$loop` Variable - -**Likelihood Of Impact: Low** - -When iterating over a `LazyCollection` instance within a Blade template, the `$loop` variable is no longer available, as accessing this variable causes the entire `LazyCollection` to be loaded into memory, thus rendering the usage of lazy collections pointless in this scenario. - -#### Checked / Disabled / Selected Blade Directives - -**Likelihood Of Impact: Low** - -The new `@checked`, `@disabled`, and `@selected` Blade directives may conflict with Vue events of the same name. You may use `@@` to escape the directives and avoid this conflict: `@@selected`. - -### Collections - -#### The `Enumerable` Contract - -**Likelihood Of Impact: Low** - -The `Illuminate\Support\Enumerable` contract now defines a `sole` method. If you are manually implementing this interface, you should update your implementation to reflect this new method: - -```php -public function sole($key = null, $operator = null, $value = null); -``` - -#### The `reduceWithKeys` Method - -The `reduceWithKeys` method has been removed as the `reduce` method provides the same functionality. You may simply update your code to call `reduce` instead of `reduceWithKeys`. - -#### The `reduceMany` Method - -The `reduceMany` method has been renamed to `reduceSpread` for naming consistency with other similar methods. - -### Container - -#### The `Container` Contract - -**Likelihood Of Impact: Very Low** - -The `Illuminate\Contracts\Container\Container` contract has received two method definitions: `scoped` and `scopedIf`. If you are manually implementing this contract, you should update your implementation to reflect these new methods. - -#### The `ContextualBindingBuilder` Contract - -**Likelihood Of Impact: Very Low** - -The `Illuminate\Contracts\Container\ContextualBindingBuilder` contract now defines a `giveConfig` method. If you are manually implementing this interface, you should update your implementation to reflect this new method: - -```php -public function giveConfig($key, $default = null); -``` - -### Database - - -#### Postgres "Schema" Configuration - -**Likelihood Of Impact: Medium** - -The `schema` configuration option used to configure Postgres connection search paths in your application's `config/database.php` configuration file should be renamed to `search_path`. - - -#### Schema Builder `registerCustomDoctrineType` Method - -**Likelihood Of Impact: Low** - -The `registerCustomDoctrineType` method has been removed from the `Illuminate\Database\Schema\Builder` class. You may use the `registerDoctrineType` method on the `DB` facade instead, or register custom Doctrine types in the `config/database.php` configuration file. - -### Eloquent - - -#### Custom Casts & `null` - -**Likelihood Of Impact: Medium** - -In previous releases of Laravel, the `set` method of custom cast classes was not invoked if the cast attribute was being set to `null`. However, this behavior was inconsistent with the Laravel documentation. In Laravel 9.x, the `set` method of the cast class will be invoked with `null` as the provided `$value` argument. Therefore, you should ensure your custom casts are able to sufficiently handle this scenario: - -```php -/** - * Prepare the given value for storage. - * - * @param \Illuminate\Database\Eloquent\Model $model - * @param string $key - * @param AddressModel $value - * @param array $attributes - * @return array - */ -public function set($model, $key, $value, $attributes) -{ - if (! $value instanceof AddressModel) { - throw new InvalidArgumentException('The given value is not an Address instance.'); - } - - return [ - 'address_line_one' => $value->lineOne, - 'address_line_two' => $value->lineTwo, - ]; -} -``` - - -#### Belongs To Many `firstOrNew`, `firstOrCreate`, and `updateOrCreate` Methods - -**Likelihood Of Impact: Medium** - -The `belongsToMany` relationship's `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods all accept an array of attributes as their first argument. In previous releases of Laravel, this array of attributes was compared against the "pivot" / intermediate table for existing records. - -However, this behavior was unexpected and typically unwanted. Instead, these methods now compare the array of attributes against the table of the related model: - -```php -$user->roles()->updateOrCreate([ - 'name' => 'Administrator', -]); -``` - -In addition, the `firstOrCreate` method now accepts a `$values` array as its second argument. This array will be merged with the first argument to the method (`$attributes`) when creating the related model if one does not already exist. This change makes this method consistent with the `firstOrCreate` methods offered by other relationship types: - -```php -$user->roles()->firstOrCreate([ - 'name' => 'Administrator', -], [ - 'created_by' => $user->id, -]); -``` - -#### The `touch` Method - -**Likelihood Of Impact: Low** - -The `touch` method now accepts an attribute to touch. If you were previously overwriting this method, you should update your method signature to reflect this new argument: - -```php -public function touch($attribute = null); -``` - -### Encryption - -#### The Encrypter Contract - -**Likelihood Of Impact: Low** - -The `Illuminate\Contracts\Encryption\Encrypter` contract now defines a `getKey` method. If you are manually implementing this interface, you should update your implementation accordingly: - -```php -public function getKey(); -``` - -### Facades - -#### The `getFacadeAccessor` Method - -**Likelihood Of Impact: Low** - -The `getFacadeAccessor` method must always return a container binding key. In previous releases of Laravel, this method could return an object instance; however, this behavior is no longer supported. If you have written your own facades, you should ensure that this method returns a container binding string: - -```php -/** - * Get the registered name of the component. - * - * @return string - */ -protected static function getFacadeAccessor() -{ - return Example::class; -} -``` - -### Filesystem - -#### The `FILESYSTEM_DRIVER` Environment Variable - -**Likelihood Of Impact: Low** - -The `FILESYSTEM_DRIVER` environment variable has been renamed to `FILESYSTEM_DISK` to more accurately reflect its usage. This change only affects the application skeleton; however, you are welcome to update your own application's environment variables to reflect this change if you wish. - -#### The "Cloud" Disk - -**Likelihood Of Impact: Low** - -The `cloud` disk configuration option was removed from the default application skeleton in November of 2020. This change only affects the application skeleton. If you are using the `cloud` disk within your application, you should leave this configuration value in your own application's skeleton. - - -### Flysystem 3.x - -**Likelihood Of Impact: High** - -Laravel 9.x has migrated from [Flysystem](https://flysystem.thephpleague.com/v2/docs/) 1.x to 3.x. Under the hood, Flysystem powers all of the file manipulation methods provided by the `Storage` facade. In light of this, some changes may be required within your application; however, we have tried to make this transition as seamless as possible. - -#### Driver Prerequisites - -Before using the S3, FTP, or SFTP drivers, you will need to install the appropriate package via the Composer package manager: - -- Amazon S3: `composer require -W league/flysystem-aws-s3-v3 "^3.0"` -- FTP: `composer require league/flysystem-ftp "^3.0"` -- SFTP: `composer require league/flysystem-sftp-v3 "^3.0"` - -#### Overwriting Existing Files - -Write operations such as `put`, `write`, and `writeStream` now overwrite existing files by default. If you do not want to overwrite existing files, you should manually check for the file's existence before performing the write operation. - -#### Write Exceptions - -Write operations such as `put`, `write`, and `writeStream` no longer throw an exception when a write operation fails. Instead, `false` is returned. If you would like to preserve the previous behavior which threw exceptions, you may define the `throw` option within a filesystem disk's configuration array: - -```php -'public' => [ - 'driver' => 'local', - // ... - 'throw' => true, -], -``` - -#### Reading Missing Files - -Attempting to read from a file that does not exist now returns `null`. In previous releases of Laravel, an `Illuminate\Contracts\Filesystem\FileNotFoundException` would have been thrown. - -#### Deleting Missing Files - -Attempting to `delete` a file that does not exist now returns `true`. - -#### Cached Adapters - -Flysystem no longer supports "cached adapters". Thus, they have been removed from Laravel and any relevant configuration (such as the `cache` key within disk configurations) can be removed. - -#### Custom Filesystems - -Slight changes have been made to the steps required to register custom filesystem drivers. Therefore, if you were defining your own custom filesystem drivers, or using packages that define custom drivers, you should update your code and dependencies. - -For example, in Laravel 8.x, a custom filesystem driver might be registered like so: - -```php -use Illuminate\Support\Facades\Storage; -use League\Flysystem\Filesystem; -use Spatie\Dropbox\Client as DropboxClient; -use Spatie\FlysystemDropbox\DropboxAdapter; - -Storage::extend('dropbox', function ($app, $config) { - $client = new DropboxClient( - $config['authorization_token'] - ); - - return new Filesystem(new DropboxAdapter($client)); -}); -``` - -However, in Laravel 9.x, the callback given to the `Storage::extend` method should return an instance of `Illuminate\Filesystem\FilesystemAdapter` directly: - -```php -use Illuminate\Filesystem\FilesystemAdapter; -use Illuminate\Support\Facades\Storage; -use League\Flysystem\Filesystem; -use Spatie\Dropbox\Client as DropboxClient; -use Spatie\FlysystemDropbox\DropboxAdapter; - -Storage::extend('dropbox', function ($app, $config) { - $adapter = new DropboxAdapter( - new DropboxClient($config['authorization_token']) - ); - - return new FilesystemAdapter( - new Filesystem($adapter, $config), - $adapter, - $config - ); -}); -``` - -### Helpers - - -#### The `data_get` Helper & Iterable Objects - -**Likelihood Of Impact: Very Low** - -Previously, the `data_get` helper could be used to retrieve nested data on arrays and `Collection` instances; however, this helper can now retrieve nested data on all iterable objects. - - -#### The `str` Helper - -**Likelihood Of Impact: Very Low** - -Laravel 9.x now includes a global `str` [helper function](/docs/{{version}}/helpers#method-str). If you are defining a global `str` helper in your application, you should rename or remove it so that it does not conflict with Laravel's own `str` helper. - - -#### The `when` / `unless` Methods - -**Likelihood Of Impact: Medium** - -As you may know, `when` and `unless` methods are offered by various classes throughout the framework. These methods can be used to conditionally perform an action if the boolean value of the first argument to the method evaluates to `true` or `false`: - -```php -$collection->when(true, function ($collection) { - $collection->merge([1, 2, 3]); -}); -``` - -Therefore, in previous releases of Laravel, passing a closure to the `when` or `unless` methods meant that the conditional operation would always execute, since a loose comparison against a closure object (or any other object) always evaluates to `true`. This often led to unexpected outcomes because developers expect the **result** of the closure to be used as the boolean value that determines if the conditional action executes. - -So, in Laravel 9.x, any closures passed to the `when` or `unless` methods will be executed and the value returned by the closure will be considered the boolean value used by the `when` and `unless` methods: - -```php -$collection->when(function ($collection) { - // This closure is executed... - return false; -}, function ($collection) { - // Not executed since first closure returned "false"... - $collection->merge([1, 2, 3]); -}); -``` - -### HTTP Client - - -#### Default Timeout - -**Likelihood Of Impact: Medium** - -The [HTTP client](/docs/{{version}}/http-client) now has a default timeout of 30 seconds. In other words, if the server does not respond within 30 seconds, an exception will be thrown. Previously, no default timeout length was configured on the HTTP client, causing requests to sometimes "hang" indefinitely. - -If you wish to specify a longer timeout for a given request, you may do so using the `timeout` method: - - $response = Http::timeout(120)->get(/* ... */); - -#### HTTP Fake & Middleware - -**Likelihood Of Impact: Low** - -Previously, Laravel would not execute any provided Guzzle HTTP middleware when the [HTTP client](/docs/{{version}}/http-client) was "faked". However, in Laravel 9.x, Guzzle HTTP middleware will be executed even when the HTTP client is faked. - -#### HTTP Fake & Dependency Injection - -**Likelihood Of Impact: Low** - -In previous releases of Laravel, invoking the `Http::fake()` method would not affect instances of the `Illuminate\Http\Client\Factory` that were injected into class constructors. However, in Laravel 9.x, `Http::fake()` will ensure fake responses are returned by HTTP clients injected into other services via dependency injection. This behavior is more consistent with the behavior of other facades and fakes. - - -### Symfony Mailer - -**Likelihood Of Impact: High** - -One of the largest changes in Laravel 9.x is the transition from SwiftMailer, which is no longer maintained as of December 2021, to Symfony Mailer. However, we have tried to make this transition as seamless as possible for your applications. That being said, please thoroughly review the list of changes below to ensure your application is fully compatible. - -#### Driver Prerequisites - -To continue using the Mailgun transport, your application should require the `symfony/mailgun-mailer` and `symfony/http-client` Composer packages: - -```shell -composer require symfony/mailgun-mailer symfony/http-client -``` - -The `wildbit/swiftmailer-postmark` Composer package should be removed from your application. Instead, your application should require the `symfony/postmark-mailer` and `symfony/http-client` Composer packages: - -```shell -composer require symfony/postmark-mailer symfony/http-client -``` - -#### Updated Return Types - -The `send`, `html`, `raw`, and `plain` methods on `Illuminate\Mail\Mailer` no longer return `void`. Instead, an instance of `Illuminate\Mail\SentMessage` is returned. This object contains an instance of `Symfony\Component\Mailer\SentMessage` that is accessible via the `getSymfonySentMessage` method or by dynamically invoking methods on the object. - -#### Renamed "Swift" Methods - -Various SwiftMailer related methods, some of which were undocumented, have been renamed to their Symfony Mailer counterparts. For example, the `withSwiftMessage` method has been renamed to `withSymfonyMessage`: - - // Laravel 8.x... - $this->withSwiftMessage(function ($message) { - $message->getHeaders()->addTextHeader( - 'Custom-Header', 'Header Value' - ); - }); - - // Laravel 9.x... - use Symfony\Component\Mime\Email; - - $this->withSymfonyMessage(function (Email $message) { - $message->getHeaders()->addTextHeader( - 'Custom-Header', 'Header Value' - ); - }); - -> **Warning** -> Please thoroughly review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#creating-sending-messages) for all possible interactions with the `Symfony\Component\Mime\Email` object. - -The list below contains a more thorough overview of renamed methods. Many of these methods are low-level methods used to interact with SwiftMailer / Symfony Mailer directly, so may not be commonly used within most Laravel applications: - - Message::getSwiftMessage(); - Message::getSymfonyMessage(); - - Mailable::withSwiftMessage($callback); - Mailable::withSymfonyMessage($callback); - - MailMessage::withSwiftMessage($callback); - MailMessage::withSymfonyMessage($callback); - - Mailer::getSwiftMailer(); - Mailer::getSymfonyTransport(); - - Mailer::setSwiftMailer($swift); - Mailer::setSymfonyTransport(TransportInterface $transport); - - MailManager::createTransport($config); - MailManager::createSymfonyTransport($config); - -#### Proxied `Illuminate\Mail\Message` Methods - -The `Illuminate\Mail\Message` typically proxied missing methods to the underlying `Swift_Message` instance. However, missing methods are now proxied to an instance of `Symfony\Component\Mime\Email` instead. So, any code that was previously relying on missing methods to be proxied to SwiftMailer should be updated to their corresponding Symfony Mailer counterparts. - -Again, many applications may not be interacting with these methods, as they are not documented within the Laravel documentation: - - // Laravel 8.x... - $message - ->setFrom('taylor@laravel.com') - ->setTo('example@example.org') - ->setSubject('Order Shipped') - ->setBody('

    HTML

    ', 'text/html') - ->addPart('Plain Text', 'text/plain'); - - // Laravel 9.x... - $message - ->from('taylor@laravel.com') - ->to('example@example.org') - ->subject('Order Shipped') - ->html('

    HTML

    ') - ->text('Plain Text'); - -#### Generated Messages IDs - -SwiftMailer offered the ability to define a custom domain to include in generated Message IDs via the `mime.idgenerator.idright` configuration option. This is not supported by Symfony Mailer. Instead, Symfony Mailer will automatically generate a Message ID based on the sender. - -#### `MessageSent` Event Changes - -The `message` property of the `Illuminate\Mail\Events\MessageSent` event now contains an instance of `Symfony\Component\Mime\Email` instead of an instance of `Swift_Message`. This message represents the email **before** it is sent. - -Additionally, a new `sent` property has been added to the `MessageSent` event. This property contains an instance of `Illuminate\Mail\SentMessage` and contains information about the sent email, such as the message ID. - -#### Forced Reconnections - -It is no longer possible to force a transport reconnection (for example when the mailer is running via a daemon process). Instead, Symfony Mailer will attempt to reconnect to the transport automatically and throw an exception if the reconnection fails. - -#### SMTP Stream Options - -Defining stream options for the SMTP transport is no longer supported. Instead, you must define the relevant options directly within the configuration if they are supported. For example, to disable TLS peer verification: - - 'smtp' => [ - // Laravel 8.x... - 'stream' => [ - 'ssl' => [ - 'verify_peer' => false, - ], - ], - - // Laravel 9.x... - 'verify_peer' => false, - ], - -To learn more about the available configuration options, please review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#transport-setup). - -> **Warning** -> In spite of the example above, you are not generally advised to disable SSL verification since it introduces the possibility of "man-in-the-middle" attacks. - -#### SMTP `auth_mode` - -Defining the SMTP `auth_mode` in the `mail` configuration file is no longer required. The authentication mode will be automatically negotiated between Symfony Mailer and the SMTP server. - -#### Failed Recipients - -It is no longer possible to retrieve a list of failed recipients after sending a message. Instead, a `Symfony\Component\Mailer\Exception\TransportExceptionInterface` exception will be thrown if a message fails to send. Instead of relying on retrieving invalid email addresses after sending a message, we recommend that you validate email addresses before sending the message instead. - -### Packages - - -#### The `lang` Directory - -**Likelihood Of Impact: Medium** - -In new Laravel applications, the `resources/lang` directory is now located in the root project directory (`lang`). If your package is publishing language files to this directory, you should ensure that your package is publishing to `app()->langPath()` instead of a hard-coded path. - - -### Queue - - -#### The `opis/closure` Library - -**Likelihood Of Impact: Low** - -Laravel's dependency on `opis/closure` has been replaced by `laravel/serializable-closure`. This should not cause any breaking change in your application unless you are interacting with the `opis/closure` library directly. In addition, the previously deprecated `Illuminate\Queue\SerializableClosureFactory` and `Illuminate\Queue\SerializableClosure` classes have been removed. If you are interacting with `opis/closure` library directly or using any of the removed classes, you may use [Laravel Serializable Closure](https://github.com/laravel/serializable-closure) instead. - -#### The Failed Job Provider `flush` Method - -**Likelihood Of Impact: Low** - -The `flush` method defined by the `Illuminate\Queue\Failed\FailedJobProviderInterface` interface now accepts an `$hours` argument which determines how old a failed job must be (in hours) before it is flushed by the `queue:flush` command. If you are manually implementing the `FailedJobProviderInterface` you should ensure that your implementation is updated to reflect this new argument: - -```php -public function flush($hours = null); -``` - -### Session - -#### The `getSession` Method - -**Likelihood Of Impact: Low** - -The `Symfony\Component\HttpFoundaton\Request` class that is extended by Laravel's own `Illuminate\Http\Request` class offers a `getSession` method to get the current session storage handler. This method is not documented by Laravel as most Laravel applications interact with the session through Laravel's own `session` method. - -The `getSession` method previously returned an instance of `Illuminate\Session\Store` or `null`; however, due to the Symfony 6.x release enforcing a return type of `Symfony\Component\HttpFoundation\Session\SessionInterface`, the `getSession` now correctly returns a `SessionInterface` implementation or throws an `\Symfony\Component\HttpFoundation\Exception\SessionNotFoundException` exception when no session is available. - -### Testing - - -#### The `assertDeleted` Method - -**Likelihood Of Impact: Medium** - -All calls to the `assertDeleted` method should be updated to `assertModelMissing`. - -### Trusted Proxies - -**Likelihood Of Impact: Low** - -If you are upgrading your Laravel 8 project to Laravel 9 by importing your existing application code into a totally new Laravel 9 application skeleton, you may need to update your application's "trusted proxy" middleware. - -Within your `app/Http/Middleware/TrustProxies.php` file, update `use Fideloper\Proxy\TrustProxies as Middleware` to `use Illuminate\Http\Middleware\TrustProxies as Middleware`. - -Next, within `app/Http/Middleware/TrustProxies.php`, you should update the `$headers` property definition: - -```php -// Before... -protected $headers = Request::HEADER_X_FORWARDED_ALL; - -// After... -protected $headers = - Request::HEADER_X_FORWARDED_FOR | - Request::HEADER_X_FORWARDED_HOST | - Request::HEADER_X_FORWARDED_PORT | - Request::HEADER_X_FORWARDED_PROTO | - Request::HEADER_X_FORWARDED_AWS_ELB; -``` - -Finally, you can remove the `fideloper/proxy` Composer dependency from your application: - -```shell -composer remove fideloper/proxy -``` - -### Validation - -#### Form Request `validated` Method - -**Likelihood Of Impact: Low** - -The `validated` method offered by form requests now accepts `$key` and `$default` arguments. If you are manually overwriting the definition of this method, you should update your method's signature to reflect these new arguments: - -```php -public function validated($key = null, $default = null) -``` - - -#### The `password` Rule - -**Likelihood Of Impact: Medium** - -The `password` rule, which validates that the given input value matches the authenticated user's current password, has been renamed to `current_password`. - - -#### Unvalidated Array Keys - -**Likelihood Of Impact: Medium** - -In previous releases of Laravel, you were required to manually instruct Laravel's validator to exclude unvalidated array keys from the "validated" data it returns, especially in combination with an `array` rule that does not specify a list of allowed keys. - -However, in Laravel 9.x, unvalidated array keys are always excluded from the "validated" data even when no allowed keys have been specified via the `array` rule. Typically, this behavior is the most expected behavior and the previous `excludeUnvalidatedArrayKeys` method was only added to Laravel 8.x as a temporary measure in order to preserve backwards compatibility. - -Although it is not recommended, you may opt-in to the previous Laravel 8.x behavior by invoking a new `includeUnvalidatedArrayKeys` method within the `boot` method of one of your application's service providers: - -```php -use Illuminate\Support\Facades\Validator; - -/** - * Register any application services. - * - * @return void - */ -public function boot() -{ - Validator::includeUnvalidatedArrayKeys(); -} -``` - ### Miscellaneous -We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/8.x...9.x) and choose which updates are important to you. +We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/9.x...10.x) and choose which updates are important to you. From 1da3baba9703cf7bbce7ecf56366f8adc6225529 Mon Sep 17 00:00:00 2001 From: Jori Stein <44996807+stein-j@users.noreply.github.com> Date: Wed, 4 Jan 2023 09:40:47 -0500 Subject: [PATCH 0571/2609] Update scout.md (#8435) --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index a391bf20c1f..cc1a57ba6d3 100644 --- a/scout.md +++ b/scout.md @@ -220,7 +220,7 @@ use App\Models\Flight; ], ``` -If the model underlying a given index is soft deletable and is included in the `index-settings` array, Scout will automatically include support for filtering on soft deleted models on that index. If you have no other filterable or sortable attributes to define for a soft deletable model index, you may simple add an empty entry to the `index-settings` array for that model: +If the model underlying a given index is soft deletable and is included in the `index-settings` array, Scout will automatically include support for filtering on soft deleted models on that index. If you have no other filterable or sortable attributes to define for a soft deletable model index, you may simply add an empty entry to the `index-settings` array for that model: ```php 'index-settings' => [ From 8293e222c6b69c8251a8599da67647104c842215 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 4 Jan 2023 17:06:28 +0100 Subject: [PATCH 0572/2609] [9.x] Improve validation placeholder replacements (#8434) * [9.x] Improve validation placeholder description * Update validation.md * Update validation.md * Update validation.md Co-authored-by: Taylor Otwell --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 58397d74ba4..74646955fb9 100644 --- a/validation.md +++ b/validation.md @@ -1825,7 +1825,7 @@ Sometimes you may need to access the value for a given nested array element when ### Error Message Indexes & Positions -When validating arrays, you may want to reference the index or position of a particular item that failed validation within the error message displayed by your application. To accomplish this, you may include the `:index` and `:position` place-holders within your [custom validation message](#manual-customizing-the-error-messages): +When validating arrays, you may want to reference the index or position of a particular item that failed validation within the error message displayed by your application. To accomplish this, you may include the `:index` (starts from `0`) and `:position` (starts from `1`) placeholders within your [custom validation message](#manual-customizing-the-error-messages): use Illuminate\Support\Facades\Validator; From c4a69d85a0157ae7102ec322f722a39f4752743e Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 4 Jan 2023 17:37:51 +0100 Subject: [PATCH 0573/2609] [9.x] Document multiple subscriptions in Cashier (#8427) * Document multiple subscriptions * Document multiple subscriptions in Paddle * Update billing.md Co-authored-by: Jayan Ratna * Update billing.md Co-authored-by: Jayan Ratna * Update cashier-paddle.md Co-authored-by: Jayan Ratna * Update cashier-paddle.md Co-authored-by: Jayan Ratna * formatting * formatting * formatting * fix bad example * formatting Co-authored-by: Jayan Ratna Co-authored-by: Taylor Otwell --- billing.md | 26 ++++++++++++++++++++++++++ cashier-paddle.md | 26 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/billing.md b/billing.md index 06cd49ec7dc..500fe21c46b 100644 --- a/billing.md +++ b/billing.md @@ -32,6 +32,7 @@ - [Changing Prices](#changing-prices) - [Subscription Quantity](#subscription-quantity) - [Subscriptions With Multiple Products](#subscriptions-with-multiple-products) + - [Multiple Subscriptions](#multiple-subscriptions) - [Metered Billing](#metered-billing) - [Subscription Taxes](#subscription-taxes) - [Subscription Anchor Date](#subscription-anchor-date) @@ -1106,6 +1107,31 @@ You can also retrieve a specific price using the `findItemOrFail` method: $subscriptionItem = $user->subscription('default')->findItemOrFail('price_chat'); + +### Multiple Subscriptions + +Stripe allows your customers to have multiple subscriptions simultaneously. For example, you may run a gym that offers a swimming subscription and a weight-lifting subscription, and each subscription may have different pricing. Of course, customers should be able to subscribe to either or both plans. + +When your application creates subscriptions, you may provide the name of the subscription to the `newSubscription` method. The name may be any string that represents the type of subscription the user is initiating: + + use Illuminate\Http\Request; + + Route::post('/swimming/subscribe', function (Request $request) { + $request->user()->newSubscription('swimming') + ->price('price_swimming_monthly') + ->create($request->paymentMethodId); + + // ... + }); + +In this example, we initiated a monthly swimming subscription for the customer. However, they may want to swap to a yearly subscription at a later time. When adjusting the customer's subscription, we can simply swap the price on the `swimming` subscription: + + $user->subscription('swimming')->swap('price_swimming_yearly'); + +Of course, you may also cancel the subscription entirely: + + $user->subscription('swimming')->cancel(); + ### Metered Billing diff --git a/cashier-paddle.md b/cashier-paddle.md index 372218529ab..0f1cbd6d9b7 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -26,6 +26,7 @@ - [Changing Plans](#changing-plans) - [Subscription Quantity](#subscription-quantity) - [Subscription Modifiers](#subscription-modifiers) + - [Multiple Subscriptions](#multiple-subscriptions) - [Pausing Subscriptions](#pausing-subscriptions) - [Cancelling Subscriptions](#cancelling-subscriptions) - [Subscription Trials](#subscription-trials) @@ -811,6 +812,31 @@ Modifiers may be deleted by invoking the `delete` method on a `Laravel\Paddle\Mo $modifier->delete(); + +### Multiple Subscriptions + +Paddle allows your customers to have multiple subscriptions simultaneously. For example, you may run a gym that offers a swimming subscription and a weight-lifting subscription, and each subscription may have different pricing. Of course, customers should be able to subscribe to either or both plans. + +When your application creates subscriptions, you may provide the name of the subscription to the `newSubscription` method. The name may be any string that represents the type of subscription the user is initiating: + + use Illuminate\Http\Request; + + Route::post('/swimming/subscribe', function (Request $request) { + $request->user() + ->newSubscription('swimming', $swimmingMonthly = 12345) + ->create($request->paymentMethodId); + + // ... + }); + +In this example, we initiated a monthly swimming subscription for the customer. However, they may want to swap to a yearly subscription at a later time. When adjusting the customer's subscription, we can simply swap the price on the `swimming` subscription: + + $user->subscription('swimming')->swap($swimmingYearly = 34567); + +Of course, you may also cancel the subscription entirely: + + $user->subscription('swimming')->cancel(); + ### Pausing Subscriptions From 51c2212d3c6523cda7773d672314ecbd41939c20 Mon Sep 17 00:00:00 2001 From: Mohannad Najjar Date: Wed, 4 Jan 2023 23:03:41 +0300 Subject: [PATCH 0574/2609] [9.x] change upgrading valet command (#8436) ref: https://github.com/laravel/valet/issues/1322 This PR is a change suggestion for the "Upgrading/Resetting Valet" command from `composer global update` to `composer global require laravel/valet` `composer global update` for some reason won't bump to a major version (from 2.18.10 to 3.2.2)..even `composer global update laravel/valet` won't do it. Using the same installation command might be confusing for some users; however, it seems to be the safest way to upgrade. --- valet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/valet.md b/valet.md index a38d5a5b18f..01726e0d8fc 100644 --- a/valet.md +++ b/valet.md @@ -129,12 +129,12 @@ If your application needs a database, check out [DBngin](https://dbngin.com). DB #### 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`. +If you are having trouble getting your Valet installation to run properly, executing the `composer global require laravel/valet` 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 Valet -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. +You may update your Valet installation by executing the `composer global require laravel/valet` 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 From 1368138ef83ff0c148c3d329e5cb420c552dd41a Mon Sep 17 00:00:00 2001 From: Jonny Nott Date: Thu, 5 Jan 2023 16:36:51 +0000 Subject: [PATCH 0575/2609] change 0 to 1 denoting user active = clearer for most devs (#8439) --- queries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queries.md b/queries.md index d7d221c17b4..90e4c2a2582 100644 --- a/queries.md +++ b/queries.md @@ -562,7 +562,7 @@ The `whereNotIn` method verifies that the given column's value is not contained You may also provide a query object as the `whereIn` method's second argument: - $activeUsers = DB::table('users')->select('id')->where('is_active', 0); + $activeUsers = DB::table('users')->select('id')->where('is_active', 1); $users = DB::table('comments') ->whereIn('user_id', $activeUsers) @@ -574,7 +574,7 @@ The example above will produce the following SQL: select * from comments where user_id in ( select id from users - where is_active = 0 + where is_active = 1 ) ``` From 8b20d5a64016a711ce786f9498fd3b97a0a46a7c Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Thu, 5 Jan 2023 10:37:12 -0600 Subject: [PATCH 0576/2609] [9.x] Updates for Homestead 14 (#8438) Related links: Homestead 14: https://github.com/laravel/homestead/releases/tag/v14.0.0 Settler 13: https://github.com/laravel/settler/releases/tag/v13.0.0 --- homestead.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homestead.md b/homestead.md index ad9dcf22efd..6a6e4a5aec0 100644 --- a/homestead.md +++ b/homestead.md @@ -60,6 +60,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Ubuntu 20.04 - Git +- PHP 8.2 - PHP 8.1 - PHP 8.0 - PHP 7.4 @@ -72,7 +73,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - MySQL 8.0 - lmm - Sqlite3 -- PostgreSQL 13 +- PostgreSQL 15 - Composer - Docker - Node (With Yarn, Bower, Grunt, and Gulp) @@ -555,7 +556,7 @@ Below is a list of additional Homestead service ports that you may wish to map f ### PHP Versions -Homestead 6 introduced support for running multiple versions of PHP on the same virtual machine. You may specify which version of PHP to use for a given site within your `Homestead.yaml` file. The available PHP versions are: "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0" (the default), and "8.1": +Homestead supports running multiple versions of PHP on the same virtual machine. You may specify which version of PHP to use for a given site within your `Homestead.yaml` file. The available PHP versions are: "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", and "8.2" (the default): ```yaml sites: @@ -575,6 +576,7 @@ php7.3 artisan list php7.4 artisan list php8.0 artisan list php8.1 artisan list +php8.2 artisan list ``` You may change the default version of PHP used by the CLI by issuing the following commands from within your Homestead virtual machine: @@ -588,6 +590,7 @@ php73 php74 php80 php81 +php82 ``` From 8c9a9cd3cc2358790d5f5a134af3e141bf1fc3c3 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 6 Jan 2023 14:16:31 +0000 Subject: [PATCH 0577/2609] [10.x] Types upgrade guide (#8442) * Note on types * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index e575d969162..60604145614 100644 --- a/upgrade.md +++ b/upgrade.md @@ -56,4 +56,6 @@ You should update the following dependencies in your application's `composer.jso ### Miscellaneous -We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/9.x...10.x) and choose which updates are important to you. +We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. + +You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/9.x...10.x) and choose which updates are important to you. However, many of the changes shown by the GitHub comparison tool are due to our organization's adoption of PHP native types. These changes are backwards compatible and the adoption of them during the migration to Laravel 10 is optional. From 98e8fa464def60287baf376e156bf6850ed891a5 Mon Sep 17 00:00:00 2001 From: Michal Kortas Date: Mon, 9 Jan 2023 15:35:32 +0100 Subject: [PATCH 0578/2609] Update default node version to 18 (#8447) --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index bc5ad6c9b22..d0fbfaada47 100644 --- a/sail.md +++ b/sail.md @@ -386,7 +386,7 @@ sail up ## Node Versions -Sail installs Node 16 by default. To change the Node version that is installed when building your images, you may update the `build.args` definition of the `laravel.test` service in your application's `docker-compose.yml` file: +Sail installs Node 18 by default. To change the Node version that is installed when building your images, you may update the `build.args` definition of the `laravel.test` service in your application's `docker-compose.yml` file: ```yaml build: From a0191a19adf6ca34f74dd32fa3b8ecb7e6a6b211 Mon Sep 17 00:00:00 2001 From: Zep Fietje Date: Mon, 9 Jan 2023 15:40:29 +0100 Subject: [PATCH 0579/2609] [9.x] Remove Stripe Tax beta note from Cashier Stripe docs (#8445) * Remove Stripe Tax beta note from Cashier Stripe docs * Update billing.md Co-authored-by: Taylor Otwell --- billing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/billing.md b/billing.md index 500fe21c46b..5c56a0706d1 100644 --- a/billing.md +++ b/billing.md @@ -212,7 +212,7 @@ Once tax calculation has been enabled, any new subscriptions and any one-off inv For this feature to work properly, your customer's billing details, such as the customer's name, address, and tax ID, need to be synced to Stripe. You may use the [customer data synchronization](#syncing-customer-data-with-stripe) and [Tax ID](#tax-ids) methods offered by Cashier to accomplish this. > **Warning** -> Unfortunately, for now, no tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). In addition, Stripe Tax is currently "invite-only" during its beta period. You can request access to Stripe Tax via the [Stripe Tax website](https://stripe.com/tax#request-access). +> No tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). ### Logging From 04f78fcc04e214c4568ecaff518abc19db63a3a8 Mon Sep 17 00:00:00 2001 From: Zep Fietje Date: Mon, 9 Jan 2023 15:40:51 +0100 Subject: [PATCH 0580/2609] Add missing Cashier Stripe webhook event (#8444) --- billing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/billing.md b/billing.md index 5c56a0706d1..cbb890ff5e2 100644 --- a/billing.md +++ b/billing.md @@ -1468,6 +1468,7 @@ To ensure your application can handle Stripe webhooks, be sure to configure the - `customer.subscription.deleted` - `customer.updated` - `customer.deleted` +- `invoice.payment_succeeded` - `invoice.payment_action_required` For convenience, Cashier includes a `cashier:webhook` Artisan command. This command will create a webhook in Stripe that listens to all of the events required by Cashier: From de551871ccbd5cde9c51f71078eb05be7ac5d43e Mon Sep 17 00:00:00 2001 From: Hichem Fantar Date: Tue, 10 Jan 2023 14:58:10 +0100 Subject: [PATCH 0581/2609] fix table overflow (#8448) --- authorization.md | 4 ++++ facades.md | 4 ++++ logging.md | 12 ++++++++++++ releases.md | 5 +++++ scheduling.md | 8 ++++++++ valet.md | 4 ++++ 6 files changed, 37 insertions(+) diff --git a/authorization.md b/authorization.md index 58367dcbb7b..121cbe51f13 100644 --- a/authorization.md +++ b/authorization.md @@ -654,6 +654,8 @@ The `authorizeResource` method accepts the model's class name as its first argum 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 | | --- | --- | | index | viewAny | @@ -664,6 +666,8 @@ The following controller methods will be mapped to their corresponding policy me | update | update | | destroy | delete | +
    + > **Note** > 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`. diff --git a/facades.md b/facades.md index 4cb4db031c7..37d0360ffb0 100644 --- a/facades.md +++ b/facades.md @@ -243,6 +243,8 @@ When the real-time facade is used, the publisher implementation will be resolved Below you will find every facade and its underlying class. This is a useful tool for quickly digging into the API documentation for a given facade root. The [service container binding](/docs/{{version}}/container) key is also included where applicable. +
    + Facade | Class | Service Container Binding ------------- | ------------- | ------------- App | [Illuminate\Foundation\Application](https://laravel.com/api/{{version}}/Illuminate/Foundation/Application.html) | `app` @@ -293,3 +295,5 @@ Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/a View | [Illuminate\View\Factory](https://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` View (Instance) | [Illuminate\View\View](https://laravel.com/api/{{version}}/Illuminate/View/View.html) |   Vite | [Illuminate\Foundation\Vite](https://laravel.com/api/{{version}}/Illuminate/Foundation/Vite.html) |   + +
    diff --git a/logging.md b/logging.md index 228f399fdba..816962d8289 100644 --- a/logging.md +++ b/logging.md @@ -46,6 +46,8 @@ By default, Monolog is instantiated with a "channel name" that matches the curre Each log channel is powered by a "driver". The driver determines how and where the log message is actually recorded. The following log channel drivers are available in every Laravel application. An entry for most of these drivers is already present in your application's `config/logging.php` configuration file, so be sure to review this file to become familiar with its contents: +
    + Name | Description ------------- | ------------- `custom` | A driver that calls a specified factory to create a channel @@ -59,6 +61,8 @@ Name | Description `stack` | A wrapper to facilitate creating "multi-channel" channels `syslog` | A `SyslogHandler` based Monolog driver +
    + > **Note** > Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. @@ -70,18 +74,26 @@ Name | Description The `single` and `daily` channels have three optional configuration options: `bubble`, `permission`, and `locking`. +
    + Name | Description | Default ------------- | ------------- | ------------- `bubble` | Indicates if messages should bubble up to other channels after being handled | `true` `locking` | Attempt to lock the log file before writing to it | `false` `permission` | The log file's permissions | `0644` +
    + Additionally, the retention policy for the `daily` channel can be configured via the `days` option: +
    + Name | Description | Default ------------- |-------------------------------------------------------------------| ------------- `days` | The number of days that daily log files should be retained | `7` +
    + #### Configuring The Papertrail Channel diff --git a/releases.md b/releases.md index 2e49441fde3..9d1d7b1adc2 100644 --- a/releases.md +++ b/releases.md @@ -21,6 +21,9 @@ When referencing the Laravel framework or its components from your application o For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest major release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). + +
    + | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | | --- | --- | --- | --- | --- | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | @@ -28,6 +31,8 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 10 | 8.1 | February 7th, 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 | February 6th, 2024 | August 5th, 2025 | February 3rd, 2026 | +
    +
    diff --git a/scheduling.md b/scheduling.md index 6c0e313b9f0..c1fd1024a46 100644 --- a/scheduling.md +++ b/scheduling.md @@ -101,6 +101,8 @@ The `exec` method may be used to issue a command to the operating system: 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 @@ -135,6 +137,8 @@ Method | Description `->yearlyOn(6, 1, '17:00');` | Run the task every year on June 1st at 17:00 `->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, you may schedule a command to run weekly on Monday: // Run once per week on Monday at 1 PM... @@ -151,6 +155,8 @@ These methods may be combined with additional constraints to create even more fi A list of additional schedule constraints may be found below: +
    + Method | Description ------------- | ------------- `->weekdays();` | Limit the task to weekdays @@ -168,6 +174,8 @@ Method | Description `->when(Closure);` | Limit the task based on a truth test `->environments($env);` | Limit the task to specific environments +
    + #### Day Constraints diff --git a/valet.md b/valet.md index a38d5a5b18f..025d66363d2 100644 --- a/valet.md +++ b/valet.md @@ -458,6 +458,8 @@ If you would like to define a custom Valet driver for a single application, crea ## Other Valet Commands +
    + Command | Description ------------- | ------------- `valet list` | Display a list of all Valet commands. @@ -470,6 +472,8 @@ Command | Description `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 From 5b57b3f0c68aab36a80f97e64e503cbd697a1faf Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 10 Jan 2023 16:30:43 +0000 Subject: [PATCH 0582/2609] [9.x] Adds Pint `--dirty` option documentation (#8437) * Adds Pint dirty option documentation * Update pint.md Co-authored-by: Taylor Otwell --- pint.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pint.md b/pint.md index 98de2a6e078..7111f7f0d6e 100644 --- a/pint.md +++ b/pint.md @@ -53,6 +53,12 @@ If you would like Pint to simply inspect your code for style errors without actu ./vendor/bin/pint --test ``` +If you would like Pint to only modify the files that have uncommitted changes according to Git, you may use the `--dirty` option: + +```shell +./vendor/bin/pint --dirty +``` + ## Configuring Pint From 47c1478fabf24409e9b1cb6cffed23a91ad279a8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Jan 2023 10:32:45 -0600 Subject: [PATCH 0583/2609] wip --- controllers.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/controllers.md b/controllers.md index 4ee0bfd8e05..f23e4595a4d 100644 --- a/controllers.md +++ b/controllers.md @@ -430,6 +430,12 @@ In this example, the following routes will be registered. As you can see, a `DEL | PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | | DELETE | `/photos/{photo}/thumbnail` | destroy | photos.thumbnail.destroy | +If you would like Laravel to register the `DELETE` route for a singleton resource but not register the creation or storage routes, you may utilize the `destroyable` method: + +```php +Route::singleton(...)->destroyable(); +``` + #### API Singleton Resources From 1a5221235146b210205a44b43bc9babb9bac1c23 Mon Sep 17 00:00:00 2001 From: Murilo Perosa <45050585+MuriloPerosa@users.noreply.github.com> Date: Thu, 12 Jan 2023 12:31:01 -0300 Subject: [PATCH 0584/2609] [9.x] Update Laravel Sail Docs (#8452) * [9.x] Updated Laravel Sail documentation to consider PHP 8.2 in the installation instructions for existing projects. * Update sail.md Co-authored-by: Taylor Otwell --- sail.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sail.md b/sail.md index d0fbfaada47..bfadfb532a0 100644 --- a/sail.md +++ b/sail.md @@ -166,11 +166,11 @@ docker run --rm \ -u "$(id -u):$(id -g)" \ -v "$(pwd):/var/www/html" \ -w /var/www/html \ - laravelsail/php81-composer:latest \ + laravelsail/php82-composer:latest \ composer install --ignore-platform-reqs ``` -When using the `laravelsail/phpXX-composer` image, you should use the same version of PHP that you plan to use for your application (`74`, `80`, or `81`). +When using the `laravelsail/phpXX-composer` image, you should use the same version of PHP that you plan to use for your application (`74`, `80`, `81`, or `82`). ### Executing Artisan Commands From a8e73be137a8ebb88935082e3771ba73058fd899 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 12 Jan 2023 15:48:28 +0000 Subject: [PATCH 0585/2609] [10.x] Adds introductions about Composer's Minimum Stability (#8451) * [10.x] Adds introductions about Composer's Minimum Stability * Keeps prefer stable * Update upgrade.md Co-authored-by: Taylor Otwell --- upgrade.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/upgrade.md b/upgrade.md index 60604145614..87e110154cc 100644 --- a/upgrade.md +++ b/upgrade.md @@ -8,6 +8,7 @@
    - [Updating Dependencies](#updating-dependencies) +- [Updating Minimum Stability](#updating-minimum-stability)
    @@ -53,6 +54,14 @@ You should update the following dependencies in your application's `composer.jso
    +#### Minimum Stability + +You should update the `minimum-stability` setting in your application's `composer.json` file to `stable`: + +```json +"minimum-stability": "stable", +``` + ### Miscellaneous From a675b8bfc5c1ef953cea6751c66f59ea647f508f Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Sat, 14 Jan 2023 01:21:34 +1000 Subject: [PATCH 0586/2609] [9.x] Document `setVisible` and `setHidden` (#8453) * Document Eloquent `setVisible` and `setHidden` methods * Document Eloquent Collection `setVisible` and `setHidden` methods --- eloquent-collections.md | 16 ++++++++++++++++ eloquent-serialization.md | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/eloquent-collections.md b/eloquent-collections.md index 76ef8113e64..fa089fe92f5 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -75,6 +75,8 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe [makeVisible](#method-makeVisible) [makeHidden](#method-makeHidden) [only](#method-only) +[setVisible](#method-setVisible) +[setHidden](#method-setHidden) [toQuery](#method-toquery) [unique](#method-unique) @@ -193,6 +195,20 @@ The `only` method returns all of the models that have the given primary keys: $users = $users->only([1, 2, 3]); + +#### `setVisible($attributes)` {.collection-method} + +The `setVisible` method [temporarily overrides](/docs/{{version}}/eloquent-serialization#temporarily-modifying-attribute-visibility) all of the visible attributes on each model in the collection: + + $users = $users->setVisible(['id', 'name']); + + +#### `setHidden($attributes)` {.collection-method} + +The `setHidden` method [temporarily overrides](/docs/{{version}}/eloquent-serialization#temporarily-modifying-attribute-visibility) all of the hidden attributes on each model in the collection: + + $users = $users->setHidden(['email', 'password', 'remember_token']); + #### `toQuery()` {.collection-method} diff --git a/eloquent-serialization.md b/eloquent-serialization.md index d1fabbef9d6..7da5b69e7c3 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -123,6 +123,12 @@ Likewise, if you would like to hide some attributes that are typically visible, return $user->makeHidden('attribute')->toArray(); +If you wish to temporarily override all of the visible or hidden attributes, you may use the `setVisible` and `setHidden` methods respectively: + + return $user->setVisible(['id', 'name'])->toArray(); + + return $user->setHidden(['email', 'password', 'remember_token'])->toArray(); + ## Appending Values To JSON From 549d54cb653959f4040510a7ae4581e5f87e9029 Mon Sep 17 00:00:00 2001 From: veganista Date: Mon, 16 Jan 2023 13:58:59 +0000 Subject: [PATCH 0587/2609] Custom Mail Transport Needs To Call Parent Constructor (#8454) I think the example for the mailchimp custom transport needs to be updated. I followed the example given while trying to create a custom transport and ran into an issue. Without calling the parent constructor for AbstractTransport the following exception is thrown: ``` Typed property Symfony\Component\Mailer\Transport\AbstractTransport::$dispatcher must not be accessed before initialization ``` --- mail.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mail.md b/mail.md index 51d681044f9..f7126e52ad1 100644 --- a/mail.md +++ b/mail.md @@ -1104,6 +1104,8 @@ Laravel includes a variety of mail transports; however, you may wish to write yo */ public function __construct(ApiClient $client) { + parent::__construct(); + $this->client = $client; } From 74be8f270974ecdc09ab8e7711ae6370765f7599 Mon Sep 17 00:00:00 2001 From: Johan Krijt Date: Tue, 17 Jan 2023 16:02:21 +0100 Subject: [PATCH 0588/2609] [9.x] Fix Guzzle Middleware example (#8457) The `withHeader` method returns a request instance with the provided value replacing the specified header and doesn't change the current request itself. --- http-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index df368b092e9..613164ba9e3 100644 --- a/http-client.md +++ b/http-client.md @@ -273,7 +273,7 @@ Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guz $response = Http::withMiddleware( Middleware::mapRequest(function (RequestInterface $request) { - $request->withHeader('X-Example', 'Value'); + $request = $request->withHeader('X-Example', 'Value'); return $request; }) From 491d35b050bd62e8a38c941a9f07090ea6608add Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Jan 2023 16:14:48 -0600 Subject: [PATCH 0589/2609] wip --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 74646955fb9..ce9deb30d86 100644 --- a/validation.md +++ b/validation.md @@ -1447,7 +1447,7 @@ The field under validation must be present in the input data but can be empty. #### prohibited -The field under validation must be an empty string or not present. +The field under validation may not be present. #### prohibited_if:_anotherfield_,_value_,... From 7b1bb955c7c6018e1fcd3487d5e55700a1fc1ce8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jan 2023 10:13:57 -0600 Subject: [PATCH 0590/2609] wip --- queues.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/queues.md b/queues.md index eec2edbff66..4992c71e1d9 100644 --- a/queues.md +++ b/queues.md @@ -1174,10 +1174,12 @@ Occasionally you may need to manually mark a job as "failed". To do so, you may $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: +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. Or, for convenience, you may pass a string error message which will be converted to an exception for you: $this->fail($exception); + $this->fail('Something went wrong.'); + > **Note** > For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). From 45302446a6a6467d256b6433757a2e20408dd132 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jan 2023 10:14:41 -0600 Subject: [PATCH 0591/2609] wip --- migrations.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/migrations.md b/migrations.md index c2fe7d15a5d..9d6c6b7233a 100644 --- a/migrations.md +++ b/migrations.md @@ -1272,6 +1272,10 @@ You may enable or disable foreign key constraints within your migrations by usin Schema::disableForeignKeyConstraints(); + Schema::withoutForeignKeyConstraints(function () { + // Constraints disabled within this closure... + }); + > **Warning** > SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. In addition, SQLite only supports foreign keys upon creation of the table and [not when tables are altered](https://www.sqlite.org/omitted.html). From aa9f8e0c9339add5f89a5b4b9fe514a6c183f726 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jan 2023 10:17:21 -0600 Subject: [PATCH 0592/2609] wip --- queries.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 90e4c2a2582..d472154bc86 100644 --- a/queries.md +++ b/queries.md @@ -969,10 +969,17 @@ The query builder also provides convenient methods for incrementing or decrement DB::table('users')->decrement('votes', 5); -You may also specify additional columns to update during the operation: +If needed, you may also specify additional columns to update during the increment or decrement operation: DB::table('users')->increment('votes', 1, ['name' => 'John']); +In addition, you may increment or decrement multiple columns at once using the `incrementEach` and `decrementEach` methods: + + DB::table('users')->increment([ + 'votes' => 5, + 'balance' => 100, + ]); + ## Delete Statements From a815fd1d42a511f348d3cefb18bbbf5c8f6c5b45 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jan 2023 10:17:26 -0600 Subject: [PATCH 0593/2609] wip --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index d472154bc86..af33a099fd2 100644 --- a/queries.md +++ b/queries.md @@ -975,7 +975,7 @@ If needed, you may also specify additional columns to update during the incremen In addition, you may increment or decrement multiple columns at once using the `incrementEach` and `decrementEach` methods: - DB::table('users')->increment([ + DB::table('users')->incrementEach([ 'votes' => 5, 'balance' => 100, ]); From 05153da7051ae136a31a726348d0bc578c24bbff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jan 2023 10:23:40 -0600 Subject: [PATCH 0594/2609] wip --- horizon.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/horizon.md b/horizon.md index b83ee868230..264d7d4494c 100644 --- a/horizon.md +++ b/horizon.md @@ -5,6 +5,7 @@ - [Configuration](#configuration) - [Balancing Strategies](#balancing-strategies) - [Dashboard Authorization](#dashboard-authorization) + - [Silenced Jobs](#silenced-jobs) - [Upgrading Horizon](#upgrading-horizon) - [Running Horizon](#running-horizon) - [Deploying Horizon](#deploying-horizon) @@ -146,6 +147,26 @@ Horizon exposes a dashboard at the `/horizon` URI. By default, you will only be Remember that Laravel automatically injects the authenticated user into the gate closure. If your application is providing Horizon security via another method, such as IP restrictions, then your Horizon users may not need to "login". Therefore, you will need to change `function ($user)` closure signature above to `function ($user = null)` in order to force Laravel to not require authentication. + +### Silenced Jobs + +Sometimes, you may not be interested in viewing certain jobs dispatched by your application or third-party packages. Instead of these jobs taking up space in your "Completed Jobs" list, you can silence them. To get started, add the job's class name to the `silenced` configuration option in your application's `horizon` configuration file: + + 'silenced' => [ + App\Jobs\ProcessPodcast::class, + ], + +Alternatively, the job you wish to silence can implement the `Laravel\Horizon\Contracts\Silenced` interface. If a job implements this interface, it will automatically be silenced, even if it is not present in the `silenced` configuration array: + + use Laravel\Horizon\Contracts\Silenced; + + class ProcessPodcast implements ShouldQueue, Silenced + { + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + // ... + } + ## Upgrading Horizon From ab440134fb13926f166640506bbd5b1a4b3507bd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jan 2023 13:35:34 -0600 Subject: [PATCH 0595/2609] initial upgrade guide --- upgrade.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 87e110154cc..e0080280e7d 100644 --- a/upgrade.md +++ b/upgrade.md @@ -17,6 +17,9 @@
    +- [Model "Dates" Property](#model-dates-property) +- [Service Mocking](#serving-mocking) +
    @@ -24,13 +27,21 @@
    +- [Closure Validation Rule Messages](#closure-validation-rule-messages) +- [Monolog 3](#monolog-3) +- [Query Exception Constructor](#query-exception-constructor) +- [Rate Limiter Return Values](#rate-limiter-return-values) +- [Relation `getBaseQuery` Method](#relation-getbasequery-method) +- [The `Redirect::home` Method](#redirect-home) +- [The `Bus::dispatchNow` Method](#dispatch-now) +
    ## Upgrading To 10.0 From 9.x -#### Estimated Upgrade Time: ?? Minutes +#### Estimated Upgrade Time: 10 Minutes > **Note** > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. @@ -62,6 +73,114 @@ You should update the `minimum-stability` setting in your application's `compose "minimum-stability": "stable", ``` +### Database + + +#### Query Exception Constructor + +**Likelihood Of Impact: Very Low** + +The `Illuminate\Database\QueryException` constructor now accepts a string connection name as its first argument. If your application is mainly throwing this exception, you should adjust your code accordingly. + +### Eloquent + + +#### Model "Dates" Property + +**Likelihood Of Impact: Medium** + +The Eloquent model's deprecated `$dates` property has been removed. Instead, your application should use the `$casts` property: + +```php +protected $casts = [ + 'deployed_at' => 'datetime', +]; +``` + + +#### Relation `getBaseQuery` Method + +**Likelihood Of Impact: Very Low** + +The `getBaseQuery` method on the `Illuminate\Database\Eloquent\Relations\Relation` class has been renamed to `toBase`. + +### Logging + + +#### Monolog 3 + +**Likelihood Of Impact: Low** + +Laravel's Monolog dependency has been updated to Monolog 3.x. If you are directly interacting with Monolog within your application, you should review Monolog's [upgrade guide](https://github.com/Seldaek/monolog/blob/main/UPGRADE.md). + +### Queues + + +#### The `Bus::dispatchNow` Method + +**Likelihood Of Impact: Low** + +The deprecated `Bus::dispatchNow` and `dispatch_now` methods have been removed. Instead, your application should use the `Bus::dispatchSync` and `dispatch_sync` methods, respectively. + +### Routing + + +#### Rate Limiter Return Values + +**Likelihood Of Impact: Low** + +When invoking the `RateLimiter::attempt` method, the value returned by the provided closure will now be returned by the method. If nothing or `null` is returned, the `attempt` method will return `true`: + +```php +$value = RateLimiter::attempt('key', 10, fn () => ['example'], 1); + +$value; // ['example'] +``` + + +#### The `Redirect::home` Method + +**Likelihood Of Impact: Very Low** + +The deprecated `Redirect::home` method has been removed. Instead, your application should redirect to an explicitly named route: + +```php +return Redirect::route('home'); +``` + +### Testing + + +#### Service Mocking + +**Likelihood Of Impact: Medium** + +The deprecated `MocksApplicationServices` trait has been removed from the framework. This trait provided testing methods such as `expectsEvents`, `expectsJobs`, and `expectsNotifications`. + +If your application uses these methods, we recommend you transition to `Event::fake`, `Bus::fake`, and `Notification::fake`, respectively. You can learn more about mocking via the complete [mocking documentation](/docs/{{version}}/mocking). + +### Validation + + +#### Closure Validation Rule Messages + +**Likelihood Of Impact: Very Low** + +When writing closure based custom validation rules, invoking the `$fail` callback more than once will now append the messages to an array instead of overwriting the previous message. Typically, this will not affect your application. + +In addition, the `$fail` callback now returns an object. If you were previously type-hinting the return type of your validation closure, this may require you to update your type-hint: + +```php +public function rules() +{ + 'name' => [ + function ($attribute, $value, $fail) { + $fail('validation.translation.key')->translate(); + }, + ], +} +``` + ### Miscellaneous From 6b51e5e5c99d68d59ffbaadb3796c7e6d439b21b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jan 2023 13:36:59 -0600 Subject: [PATCH 0596/2609] wip --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index e0080280e7d..65add48f71e 100644 --- a/upgrade.md +++ b/upgrade.md @@ -89,7 +89,7 @@ The `Illuminate\Database\QueryException` constructor now accepts a string connec **Likelihood Of Impact: Medium** -The Eloquent model's deprecated `$dates` property has been removed. Instead, your application should use the `$casts` property: +The Eloquent model's deprecated `$dates` property has been removed. Your application should now use the `$casts` property: ```php protected $casts = [ From 89315ab71c4e5f6751fbe3b38742b0b6c9ddbc88 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 17 Jan 2023 13:54:53 -0600 Subject: [PATCH 0597/2609] add "mysql" to list of available features (#8459) --- homestead.md | 1 + 1 file changed, 1 insertion(+) diff --git a/homestead.md b/homestead.md index 6a6e4a5aec0..e54c80d1d08 100644 --- a/homestead.md +++ b/homestead.md @@ -343,6 +343,7 @@ features: - meilisearch: true - minio: true - mongodb: true + - mysql: true - neo4j: true - ohmyzsh: true - openresty: true From ffbcaa8081efe01e6275903b2bcce589dd0dfd4a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 17 Jan 2023 19:56:37 +0000 Subject: [PATCH 0598/2609] [10.x] Clarifies when pull requests should contain types (#8458) * Clarifies when types pull requests are accepted * Update contributions.md Co-authored-by: Taylor Otwell --- contributions.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contributions.md b/contributions.md index e01ad8ca642..a46e6f6d0f0 100644 --- a/contributions.md +++ b/contributions.md @@ -116,6 +116,30 @@ Below is an example of a valid Laravel documentation block. Note that the `@para // ... } +When the `@param` or `@return` attributes are redundant due to the use of native types, they can be removed: + + /** + * Execute the job. + */ + public function handle(AudioProcessor $processor): void + { + // + } + +However, when the native type is generic, please specify the generic type through the use of the `@param` or `@return` attributes: + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return [ + Attachment::fromStorage('/path/to/file'), + ]; + } + ### StyleCI From 4d1c5a8df1c92668f62cf5735ee354fd50419c6a Mon Sep 17 00:00:00 2001 From: Feras AlHallak <82784472+Feras-alhallak@users.noreply.github.com> Date: Wed, 18 Jan 2023 16:42:08 +0200 Subject: [PATCH 0599/2609] Add useRefreshTokenModel Method (#8460) * Add useRefreshTokenModel Method Add useRefreshTokenModel to Overriding Default Models Section * Add use RefreshToken * Update passport.md Co-authored-by: Taylor Otwell --- passport.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 8ae80964ab2..6c32397bae2 100644 --- a/passport.md +++ b/passport.md @@ -233,6 +233,7 @@ After defining your model, you may instruct Passport to use your custom model vi use App\Models\Passport\AuthCode; use App\Models\Passport\Client; use App\Models\Passport\PersonalAccessClient; + use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; /** @@ -245,8 +246,9 @@ After defining your model, you may instruct Passport to use your custom model vi $this->registerPolicies(); Passport::useTokenModel(Token::class); - Passport::useClientModel(Client::class); + Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); + Passport::useClientModel(Client::class); Passport::usePersonalAccessClientModel(PersonalAccessClient::class); } From 26ddf9b1e0319bb951d6685b55c8d1bcd0ee464f Mon Sep 17 00:00:00 2001 From: Bastian Rickmann Date: Thu, 19 Jan 2023 00:07:10 +0100 Subject: [PATCH 0600/2609] Copy filesystem informations from sail to filesystem | Minio | AWS_URL + temporary urls (#8461) * Update filesystem.md This information is hidden on the pages relating Laravel Sail. I consider it be note worthy here as well. I spent hours on researching for bugs and upgrade package versions only to accidentially read the actual reason in the docs relating Laravel Sail. * Update filesystem.md Co-authored-by: Taylor Otwell --- filesystem.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/filesystem.md b/filesystem.md index c82b29ffe40..e3e4e283a15 100644 --- a/filesystem.md +++ b/filesystem.md @@ -187,6 +187,18 @@ Typically, after updating the disk's credentials to match the credentials of the 'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000/'), + +#### MinIO + +In order for Laravel's Flysystem integration to generate proper URLs when using MinIO, you should define the `AWS_URL` environment variable so that it matches your application's local URL and includes the bucket name in the URL path: + +```ini +AWS_URL=http://localhost:9000/local +``` + +> **Warning** +> Generating temporary storage URLs via the `temporaryUrl` method is not supported when using MinIO. + ## Obtaining Disk Instances From 421d5485ae53621a84c2dcee769560c38c076c0a Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 20 Jan 2023 02:02:18 +1100 Subject: [PATCH 0601/2609] Reverts 491d35b050bd62e8a38c941a9f07090ea6608add (#8463) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index ce9deb30d86..74646955fb9 100644 --- a/validation.md +++ b/validation.md @@ -1447,7 +1447,7 @@ The field under validation must be present in the input data but can be empty. #### prohibited -The field under validation may not be present. +The field under validation must be an empty string or not present. #### prohibited_if:_anotherfield_,_value_,... From d54f020c955bbf686135cc67a569ef3aacaa93b1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 20 Jan 2023 02:03:42 +1100 Subject: [PATCH 0602/2609] Updates prohibits rule definition (#8462) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 518cb7da21a..65434a3fc7f 100644 --- a/validation.md +++ b/validation.md @@ -1473,7 +1473,7 @@ The field under validation must be an empty string or not present unless the _an #### prohibits:_anotherfield_,... -If the field under validation is present, no fields in _anotherfield_ can be present, even if empty. +If the field under validation is present and not an empty string, then _anotherfield_ is [prohibited](#rule-prohibited). #### regex:_pattern_ From f3aff4fadb229a91d62324dda854661861a93487 Mon Sep 17 00:00:00 2001 From: Koramit Pichanaharee Date: Fri, 20 Jan 2023 22:44:36 +0700 Subject: [PATCH 0603/2609] Update to Inertia.js v1.0 (#8468) * Update to Inertia.js v1.0 * Update vite.md Co-authored-by: Taylor Otwell --- vite.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vite.md b/vite.md index df2b312df80..6cbee763227 100644 --- a/vite.md +++ b/vite.md @@ -317,13 +317,13 @@ The Laravel Vite plugin provides a convenient `resolvePageComponent` function to ```js import { createApp, h } from 'vue'; -import { createInertiaApp } from '@inertiajs/inertia-vue3'; +import { createInertiaApp } from '@inertiajs/vue3'; import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; createInertiaApp({ resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')), setup({ el, App, props, plugin }) { - createApp({ render: () => h(App, props) }) + return createApp({ render: () => h(App, props) }) .use(plugin) .mount(el) }, From 1165bd5441d9733eeb148898f25230d6c058b86f Mon Sep 17 00:00:00 2001 From: Matthew Gilpin Date: Sat, 21 Jan 2023 02:19:53 +1000 Subject: [PATCH 0604/2609] [9.x] Added documentation for Storage::mimeType() (#8466) * Added a mention to Storage::mimeType() * Update filesystem.md Co-authored-by: Taylor Otwell --- filesystem.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filesystem.md b/filesystem.md index e3e4e283a15..079cf7a7b58 100644 --- a/filesystem.md +++ b/filesystem.md @@ -346,6 +346,10 @@ The `lastModified` method returns the UNIX timestamp of the last time the file w $time = Storage::lastModified('file.jpg'); +The MIME type of a given file may be obtained via the `mimeType` method: + + $mime = Storage::mimeType('file.jpg') + #### File Paths From edf107be3918200984e2f5723c3e1137dbe6469d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 20 Jan 2023 13:36:15 -0600 Subject: [PATCH 0605/2609] prohibited docs clarification --- validation.md | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/validation.md b/validation.md index 74646955fb9..d52c985b2dc 100644 --- a/validation.md +++ b/validation.md @@ -1442,17 +1442,35 @@ The field under validation must match the authenticated user's password. #### present -The field under validation must be present in the input data but can be empty. +The field under validation must exist in the input data. #### prohibited -The field under validation must be an empty string or not present. +The field under validation must be missing or "empty". A field is "empty" if it meets one of the following criteria: + +
    + +- The value is `null`. +- The value is an empty string. +- The value is an empty array or empty `Countable` object. +- The value is an uploaded file with an empty path. + +
    #### prohibited_if:_anotherfield_,_value_,... -The field under validation must be an empty string or not present if the _anotherfield_ field is equal to any _value_. +The field under validation must be missing or "empty" if the _anotherfield_ field is equal to any _value_. A field is "empty" if it meets one of the following criteria: + +
    + +- The value is `null`. +- The value is an empty string. +- The value is an empty array or empty `Countable` object. +- The value is an uploaded file with an empty path. + +
    If complex conditional prohibition logic is required, you may utilize the `Rule::prohibitedIf` method. This method accepts a boolean or a closure. When given a closure, the closure should return `true` or `false` to indicate if the field under validation should be prohibited: @@ -1470,12 +1488,30 @@ If complex conditional prohibition logic is required, you may utilize the `Rule: #### prohibited_unless:_anotherfield_,_value_,... -The field under validation must be an empty string or not present unless the _anotherfield_ field is equal to any _value_. +The field under validation must be missing or "empty" unless the _anotherfield_ field is equal to any _value_. A field is "empty" if it meets one of the following criteria: + +
    + +- The value is `null`. +- The value is an empty string. +- The value is an empty array or empty `Countable` object. +- The value is an uploaded file with an empty path. + +
    #### prohibits:_anotherfield_,... -If the field under validation is present, no fields in _anotherfield_ can be present, even if empty. +If the field under validation is not missing or "empty", all fields in _anotherfield_ must be missing or "empty". A field is "empty" if it meets one of the following criteria: + +
    + +- The value is `null`. +- The value is an empty string. +- The value is an empty array or empty `Countable` object. +- The value is an uploaded file with an empty path. + +
    #### regex:_pattern_ From 3aaa1540686dc20b39cdbba27ecd9b3cbd1b4c82 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 20 Jan 2023 13:37:12 -0600 Subject: [PATCH 0606/2609] wip --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index d52c985b2dc..2900e22904b 100644 --- a/validation.md +++ b/validation.md @@ -1526,7 +1526,7 @@ Internally, this rule uses the PHP `preg_match` function. The pattern specified #### required -The field under validation must be present in the input data and not empty. A field is considered "empty" if one of the following conditions are true: +The field under validation must be present in the input data and not empty. A field is "empty" if it meets one of the following criteria:
    From 22bbd047177e17635e94473e1f552e4ae99dcb2e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 20 Jan 2023 13:43:03 -0600 Subject: [PATCH 0607/2609] wip --- validation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validation.md b/validation.md index 2900e22904b..6ce05ef4e8c 100644 --- a/validation.md +++ b/validation.md @@ -1447,7 +1447,7 @@ The field under validation must exist in the input data. #### prohibited -The field under validation must be missing or "empty". A field is "empty" if it meets one of the following criteria: +The field under validation must be missing or empty. A field is "empty" if it meets one of the following criteria:
    @@ -1461,7 +1461,7 @@ The field under validation must be missing or "empty". A field is "empty" if it #### prohibited_if:_anotherfield_,_value_,... -The field under validation must be missing or "empty" if the _anotherfield_ field is equal to any _value_. A field is "empty" if it meets one of the following criteria: +The field under validation must be missing or empty if the _anotherfield_ field is equal to any _value_. A field is "empty" if it meets one of the following criteria:
    @@ -1488,7 +1488,7 @@ If complex conditional prohibition logic is required, you may utilize the `Rule: #### prohibited_unless:_anotherfield_,_value_,... -The field under validation must be missing or "empty" unless the _anotherfield_ field is equal to any _value_. A field is "empty" if it meets one of the following criteria: +The field under validation must be missing or empty unless the _anotherfield_ field is equal to any _value_. A field is "empty" if it meets one of the following criteria:
    @@ -1502,7 +1502,7 @@ The field under validation must be missing or "empty" unless the _anotherfield_ #### prohibits:_anotherfield_,... -If the field under validation is not missing or "empty", all fields in _anotherfield_ must be missing or "empty". A field is "empty" if it meets one of the following criteria: +If the field under validation is not missing or empty, all fields in _anotherfield_ must be missing or empty. A field is "empty" if it meets one of the following criteria:
    From e70df2c5fa69ef638001cd4b378f34e30fc9bf37 Mon Sep 17 00:00:00 2001 From: Stanislas Date: Sun, 22 Jan 2023 18:35:01 +0100 Subject: [PATCH 0608/2609] Queues: fix link to Redis Cluster docs (#8469) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 4992c71e1d9..f23c691f9a6 100644 --- a/queues.md +++ b/queues.md @@ -104,7 +104,7 @@ In order to use the `redis` queue driver, you should configure a Redis database **Redis Cluster** -If your Redis queue connection uses a Redis Cluster, your queue names must contain a [key hash tag](https://redis.io/topics/cluster-spec#keys-hash-tags). This is required in order to ensure all of the Redis keys for a given queue are placed into the same hash slot: +If your Redis queue connection uses a Redis Cluster, your queue names must contain a [key hash tag](https://redis.io/docs/reference/cluster-spec/#hash-tags). This is required in order to ensure all of the Redis keys for a given queue are placed into the same hash slot: 'redis' => [ 'driver' => 'redis', From 931acb18026aaf7a73b0f010826a37a35ebbf361 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 24 Jan 2023 16:57:47 +0000 Subject: [PATCH 0609/2609] Adds Laravel Ignition v2 to Laravel 10's upgrade guide (#8471) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 65add48f71e..695a7c00f1f 100644 --- a/upgrade.md +++ b/upgrade.md @@ -62,6 +62,7 @@ You should update the following dependencies in your application's `composer.jso
    - `laravel/framework` to `^10.0` +- `spatie/laravel-ignition` to `^2.0`
    From fe1610db0b7d36713ed3a4503366a724d5ccf463 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 25 Jan 2023 02:14:22 +0330 Subject: [PATCH 0610/2609] [9.x] add prepareForValidation method in validation (#8473) * add prepareForValidation method in validation * add preparing after validation in main content * fix passed method name * Update validation.md * Update validation.md * Update validation.md Co-authored-by: Taylor Otwell --- validation.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/validation.md b/validation.md index 6ce05ef4e8c..68556f31472 100644 --- a/validation.md +++ b/validation.md @@ -494,6 +494,20 @@ If you need to prepare or sanitize any data from the request before you apply yo ]); } +Likewise, if you need to normalize any request data after validation is complete, you may use the `passedValidation` method: + + use Illuminate\Support\Str; + + /** + * Handle a passed validation attempt. + * + * @return void + */ + protected function passedValidation() + { + $this->replace(['name' => 'Taylor']); + } + ## Manually Creating Validators From e3bcd8f29cd4102bc3694e72f5739e628bf9f512 Mon Sep 17 00:00:00 2001 From: Jesper Noordsij <45041769+jnoordsij@users.noreply.github.com> Date: Wed, 25 Jan 2023 15:34:02 +0100 Subject: [PATCH 0611/2609] Add missing trailing ; to example in mail.md (#8479) --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index f7126e52ad1..e6677e5918b 100644 --- a/mail.md +++ b/mail.md @@ -1151,7 +1151,7 @@ Once you've defined your custom transport, you may register it via the `extend` { Mail::extend('mailchimp', function (array $config = []) { return new MailchimpTransport(/* ... */); - }) + }); } Once your custom transport has been defined and registered, you may create a mailer definition within your application's `config/mail.php` configuration file that utilizes the new transport: From 7372e2d372612ee9fbc8636b28abb5f89aaffecd Mon Sep 17 00:00:00 2001 From: BinotaLIU Date: Wed, 25 Jan 2023 22:37:20 +0800 Subject: [PATCH 0612/2609] Fix wrong return type hint (#8478) --- eloquent-relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 372f4e424f9..691becf832c 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -489,7 +489,7 @@ Typical Eloquent foreign key conventions will be used when performing the relati class Project extends Model { - public function deployments(): HasOneThrough + public function deployments(): HasManyThrough { return $this->hasManyThrough( Deployment::class, From 171505af6995cca45dad5e9c7f5ff840596d18b7 Mon Sep 17 00:00:00 2001 From: veganista Date: Wed, 25 Jan 2023 14:42:02 +0000 Subject: [PATCH 0613/2609] Adds some missing steps when using additional symfony transports (#8477) * Update sendinblue composer require command Adds symfony/http-client to composer command for adding the sendinblue symfony transport. * Document the need to register additional transports Without adding the additional transports to config/mail.php you get an error when using the newly instaled transport * Update mail.md Co-authored-by: Taylor Otwell --- mail.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mail.md b/mail.md index e6677e5918b..344fd9fddea 100644 --- a/mail.md +++ b/mail.md @@ -1167,7 +1167,7 @@ Once your custom transport has been defined and registered, you may create a mai Laravel includes support for some existing Symfony maintained mail transports like Mailgun and Postmark. However, you may wish to extend Laravel with support for additional Symfony maintained transports. You can do so by requiring the necessary Symfony mailer via Composer and registering the transport with Laravel. For example, you may install and register the "Sendinblue" Symfony mailer: ```none -composer require symfony/sendinblue-mailer +composer require symfony/sendinblue-mailer symfony/http-client ``` Once the Sendinblue mailer package has been installed, you may add an entry for your Sendinblue API credentials to your application's `services` configuration file: @@ -1176,7 +1176,7 @@ Once the Sendinblue mailer package has been installed, you may add an entry for 'key' => 'your-api-key', ], -Finally, you may use the `Mail` facade's `extend` method to register the transport with Laravel. Typically, this should be done within the `boot` method of a service provider: +Next, you may use the `Mail` facade's `extend` method to register the transport with Laravel. Typically, this should be done within the `boot` method of a service provider: use Illuminate\Support\Facades\Mail; use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; @@ -1199,3 +1199,10 @@ Finally, you may use the `Mail` facade's `extend` method to register the transpo ); }); } + +Once your transport has been registered, you may create a mailer definition within your application's config/mail.php configuration file that utilizes the new transport: + + 'sendinblue' => [ + 'transport' => 'sendinblue', + // ... + ], From b82c1dd7d7025549955663770912883249deebe7 Mon Sep 17 00:00:00 2001 From: Ryan Baham Date: Wed, 25 Jan 2023 09:13:19 -0600 Subject: [PATCH 0614/2609] [10.x] Adds docs for `whereExists` query objects (#8476) * docs: `whereExists` query objects Updated the `whereExists` docs to include the new ability to pass closures OR query objects. * Update queries.md Co-authored-by: Taylor Otwell --- queries.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/queries.md b/queries.md index eacc4a01876..30b61a466e8 100644 --- a/queries.md +++ b/queries.md @@ -687,7 +687,17 @@ The `whereExists` method allows you to write "where exists" SQL clauses. The `wh }) ->get(); -The query above will produce the following SQL: +Alternatively, you may provide a query object to the `whereExists` method instead of a closure: + + $orders = DB::table('orders') + ->select(DB::raw(1)) + ->whereColumn('orders.user_id', 'users.id'); + + $users = DB::table('users') + ->whereExists($orders) + ->get(); + +Both of the examples above will produce the following SQL: ```sql select * from users From 7c2360311d40da1a83388a48ed22dfd91873a12c Mon Sep 17 00:00:00 2001 From: Victor Moreira <103689307+devsquad-victor-lima@users.noreply.github.com> Date: Fri, 27 Jan 2023 07:29:00 -0300 Subject: [PATCH 0615/2609] Fix Service Mocking link (#8483) The current 'Service Mocking' link wasn't getting the user to the right place. --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 695a7c00f1f..bb67923f0fb 100644 --- a/upgrade.md +++ b/upgrade.md @@ -18,7 +18,7 @@
    - [Model "Dates" Property](#model-dates-property) -- [Service Mocking](#serving-mocking) +- [Service Mocking](#service-mocking)
    From e628b1cb259875f4f4c28d967a569653c25f4efc Mon Sep 17 00:00:00 2001 From: Idris Lawal <34219909+IDTitanium@users.noreply.github.com> Date: Fri, 27 Jan 2023 12:10:11 +0100 Subject: [PATCH 0616/2609] Updated the default PHP version currently used by sail for v9.x (#8481) --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index bfadfb532a0..f70879f3398 100644 --- a/sail.md +++ b/sail.md @@ -353,7 +353,7 @@ sail tinker ## PHP Versions -Sail currently supports serving your application via PHP 8.2, 8.1, PHP 8.0, or PHP 7.4. The default PHP version used by Sail is currently PHP 8.1. 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: +Sail currently supports serving your application via PHP 8.2, 8.1, PHP 8.0, or PHP 7.4. The default PHP version used by Sail is currently PHP 8.2. 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.2 From 8780958cdc196272fe61da91594f9a6bb7e1f9e2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Jan 2023 14:18:51 +0000 Subject: [PATCH 0617/2609] clarify middleware aliases --- authentication.md | 6 +++--- middleware.md | 36 ++++++++++++++++++------------------ passport.md | 6 +++--- sanctum.md | 2 +- urls.md | 10 +++++----- verification.md | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/authentication.md b/authentication.md index fd80f884a9d..02df961ff0c 100644 --- a/authentication.md +++ b/authentication.md @@ -420,11 +420,11 @@ You may also use HTTP Basic Authentication without setting a user identifier coo } -Next, [register the route middleware](/docs/{{version}}/middleware#registering-middleware) and attach it to a route: +Next, attach the middleware to a route: Route::get('/api/user', function () { // Only authenticated users may access this route... - })->middleware('auth.basic.once'); + })->middleware(AuthenticateOnceWithBasicAuth::class); ## Logging Out @@ -456,7 +456,7 @@ In addition to calling the `logout` method, it is recommended that you invalidat 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 included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` route middleware key as defined in your application's HTTP kernel: +Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` route middleware alias as defined in your application's HTTP kernel: Route::middleware(['auth', 'auth.session'])->group(function () { Route::get('/', function () { diff --git a/middleware.md b/middleware.md index d71c797e4eb..d8c9090f2b8 100644 --- a/middleware.md +++ b/middleware.md @@ -117,11 +117,25 @@ If you want a middleware to run during every HTTP request to your application, l ### Assigning Middleware To Routes -If you would like to assign middleware to specific routes, you should first assign the middleware a key in your application's `app/Http/Kernel.php` file. By default, the `$routeMiddleware` property of this class contains entries for the middleware included with Laravel. You may add your own middleware to this list and assign it a key of your choosing: +If you would like to assign middleware to specific routes, you may invoke the `middleware` method when defining the route: + + use App\Http\Middleware\Authenticate; + + Route::get('/profile', function () { + // ... + })->middleware(Authenticate::class); + +You may assign multiple middleware to the route by passing an array of middleware names to the `middleware` method: + + Route::get('/', function () { + // ... + })->middleware([First::class, Second::class]); + +For convenience, you may assign aliases to middleware in your application's `app/Http/Kernel.php` file. By default, the `$middlewareAliases` property of this class contains entries for the middleware included with Laravel. You may add your own middleware to this list and assign it an alias of your choosing: // Within App\Http\Kernel class... - protected $routeMiddleware = [ + protected $middlewareAliases = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, @@ -133,26 +147,12 @@ If you would like to assign middleware to specific routes, you should first assi 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ]; -Once the middleware has been defined in the HTTP kernel, you may use the `middleware` method to assign middleware to a route: +Once the middleware alias has been defined in the HTTP kernel, you may use the alias when assigning middlware to routes: Route::get('/profile', function () { // ... })->middleware('auth'); -You may assign multiple middleware to the route by passing an array of middleware names to the `middleware` method: - - Route::get('/', function () { - // ... - })->middleware(['first', 'second']); - -When assigning middleware, you may also pass the fully qualified class name: - - use App\Http\Middleware\EnsureTokenIsValid; - - Route::get('/profile', function () { - // ... - })->middleware(EnsureTokenIsValid::class); - #### Excluding Middleware @@ -205,7 +205,7 @@ Laravel includes predefined `web` and `api` middleware groups that contain commo ], 'api' => [ - 'throttle:api', + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ]; diff --git a/passport.md b/passport.md index 70b734b543b..82e00b69837 100644 --- a/passport.md +++ b/passport.md @@ -817,11 +817,11 @@ Before your application can issue tokens via the client credentials grant, you w php artisan passport:client --client ``` -Next, to use this grant type, you need to add the `CheckClientCredentials` middleware to the `$routeMiddleware` property of your `app/Http/Kernel.php` file: +Next, to use this grant type, you may add the `CheckClientCredentials` middleware to the `$middlewareAliases` property of your application's `app/Http/Kernel.php` file: use Laravel\Passport\Http\Middleware\CheckClientCredentials; - protected $routeMiddleware = [ + protected $middlewareAliases = [ 'client' => CheckClientCredentials::class, ]; @@ -1077,7 +1077,7 @@ If you are issuing personal access tokens using the `App\Models\User` model's `c ### Checking Scopes -Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, add the following middleware to the `$routeMiddleware` property of your `app/Http/Kernel.php` file: +Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, add the following middleware to the `$middlewareAliases` property of your `app/Http/Kernel.php` file: 'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class, 'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class, diff --git a/sanctum.md b/sanctum.md index 88571900424..33aad4bbadd 100644 --- a/sanctum.md +++ b/sanctum.md @@ -168,7 +168,7 @@ When handling an incoming request authenticated by Sanctum, you may determine if #### Token Ability Middleware -Sanctum also includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given ability. To get started, add the following middleware to the `$routeMiddleware` property of your application's `app/Http/Kernel.php` file: +Sanctum also includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given ability. To get started, add the following middleware to the `$middlewareAliases` property of your application's `app/Http/Kernel.php` file: 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class, 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class, diff --git a/urls.md b/urls.md index 26c655fc46d..464b48cf871 100644 --- a/urls.md +++ b/urls.md @@ -126,16 +126,16 @@ Sometimes, you may need to allow your application's frontend to append data to a abort(401); } -Instead of validating signed URLs using the incoming request instance, 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: +Instead of validating signed URLs using the incoming request instance, you may assign the `Illuminate\Routing\Middleware\ValidateSignature` [middleware](/docs/{{version}}/middleware) to the route. If it is not already present, you may assign this middleware an alias in your HTTP kernel's `$middlewareAliases` array: /** - * The application's route middleware. + * The application's middleware aliases. * - * These middleware may be assigned to groups or used individually. + * Aliases may be used to conveniently assign middleware to routes and groups. * - * @var array + * @var array */ - protected $routeMiddleware = [ + protected $middlewareAliases = [ 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, ]; diff --git a/verification.md b/verification.md index 2e0c85b2c64..d8b449dbf26 100644 --- a/verification.md +++ b/verification.md @@ -112,7 +112,7 @@ Sometimes a user may misplace or accidentally delete the email address verificat ### Protecting Routes -[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. Typically, this middleware is paired with the `auth` middleware: +[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware alias, which is an alias for 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. Typically, this middleware is paired with the `auth` middleware: Route::get('/profile', function () { // Only verified users may access this route... From d333b83cf47290bc15b192461e2b29e21aab4eff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Jan 2023 14:20:15 +0000 Subject: [PATCH 0618/2609] replace middleware --- sanctum.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sanctum.md b/sanctum.md index 33aad4bbadd..d40f41835f2 100644 --- a/sanctum.md +++ b/sanctum.md @@ -79,7 +79,7 @@ Next, if you plan to utilize Sanctum to authenticate a SPA, you should add Sanct 'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - 'throttle:api', + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], @@ -272,7 +272,7 @@ Next, you should add Sanctum's middleware to your `api` middleware group within 'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - 'throttle:api', + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], From 5855e007af526e860812f5fb77cf68d95a4c0ae2 Mon Sep 17 00:00:00 2001 From: Majid Alaeinia <11965368+majidalaeinia@users.noreply.github.com> Date: Fri, 27 Jan 2023 23:24:24 +0330 Subject: [PATCH 0619/2609] [9.x] Fix notation of the "Note" on the queue docs (#8485) * [9.x] Fix notation * Update queues.md * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/queues.md b/queues.md index f23c691f9a6..d6394f53abe 100644 --- a/queues.md +++ b/queues.md @@ -858,8 +858,9 @@ When chaining jobs, you may use the `catch` method to specify a closure that sho ])->catch(function (Throwable $e) { // A job within the chain has failed... })->dispatch(); - -> {note} Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. + +> **Warning** +> Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. ### Customizing The Queue & Connection @@ -1494,8 +1495,9 @@ Using the `catch` method, you may provide a closure that should be executed if t })->catch(function (Throwable $e) { // This job has failed... }); - -> {note} Since `catch` callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within `catch` callbacks. + +> **Warning** +> Since `catch` callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within `catch` callbacks. ## Running The Queue Worker From 59e33719c7c2f5841f01e7b9f95a266a7ac8f81c Mon Sep 17 00:00:00 2001 From: Ankur Kumar Date: Mon, 30 Jan 2023 20:01:06 +0530 Subject: [PATCH 0620/2609] Add Mailpit as along with Mailhog (#8489) --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 344fd9fddea..3fa303e362e 100644 --- a/mail.md +++ b/mail.md @@ -935,7 +935,7 @@ When designing a mailable's template, it is convenient to quickly preview the re }); > **Warning** -> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog) or [HELO](https://usehelo.com). +> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog) or [Mailpit](https://github.com/axllent/mailpit) or [HELO](https://usehelo.com). ## Localizing Mailables From 6998de1d2ecdcf725bc9523f87ce229dd405d193 Mon Sep 17 00:00:00 2001 From: robogito <15885847+robogito@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:38:04 +0100 Subject: [PATCH 0621/2609] Example of using Auth::viaRequest (#8488) * Example of using Auth::viaRequest * Update authentication.md --------- Co-authored-by: Taylor Otwell --- authentication.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/authentication.md b/authentication.md index dfa26b2440f..9fbfd79d320 100644 --- a/authentication.md +++ b/authentication.md @@ -611,6 +611,12 @@ Once your custom authentication driver has been defined, you may configure it as ], ], +Finally, you may reference the guard when assigning the authentication middleware to a route: + + Route::middleware('auth:api')->group(function () { + // ... + } + ## Adding Custom User Providers From f94643b9fd657cdde2823431357aacc975c3ee92 Mon Sep 17 00:00:00 2001 From: BerkanYildiz Date: Mon, 30 Jan 2023 15:38:56 +0100 Subject: [PATCH 0622/2609] Fix namespace of the Vite class (#8487) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 6cbee763227..a49ce6c0de5 100644 --- a/vite.md +++ b/vite.md @@ -467,7 +467,7 @@ export default defineConfig({ ### Aliases -It is common in JavaScript applications to [create aliases](#aliases) to regularly referenced directories. But, you may also create aliases to use in Blade by using the `macro` method on the `Illuminate\Support\Vite` class. Typically, "macros" should be defined within the `boot` method of a [service provider](/docs/{{version}}/providers): +It is common in JavaScript applications to [create aliases](#aliases) to regularly referenced directories. But, you may also create aliases to use in Blade by using the `macro` method on the `Illuminate\Support\Facades\Vite` class. Typically, "macros" should be defined within the `boot` method of a [service provider](/docs/{{version}}/providers): /** * Bootstrap any application services. From 9cc623ea900df8ed4316d1ba4ec8f369fed5e803 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Mon, 30 Jan 2023 14:41:28 +0000 Subject: [PATCH 0623/2609] 4th argument negative number example for Str::mask (#8486) Changes provide a more comprehensive example of the Str mask method that includes a negative number as a 4th argument as well as used as the 3rd argument. --- helpers.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index ef6082f0930..388572fe644 100644 --- a/helpers.md +++ b/helpers.md @@ -2622,12 +2622,16 @@ The `mask` method masks a portion of a string with a repeated character, and may // tay*************** -If needed, you provide a negative number as the third argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: +If needed, you may provide negative numbers as the third or fourth argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: $string = Str::of('taylor@example.com')->mask('*', -15, 3); // tay***@example.com + $string = Str::of('taylor@example.com')->mask('*', 4, -4); + + // tayl**********.com + #### `match` {.collection-method} From e25b49ea5e0d736c185df1b71b12e29700f3b2ee Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Tue, 31 Jan 2023 01:00:04 +0330 Subject: [PATCH 0624/2609] [9.x] add ```to_route``` helper in redirects (#8494) * add to_route helper in redirects * Update redirects.md --------- Co-authored-by: Taylor Otwell --- redirects.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redirects.md b/redirects.md index 260f6729336..09a73704b4e 100644 --- a/redirects.md +++ b/redirects.md @@ -35,6 +35,10 @@ If your route has parameters, you may pass them as the second argument to the `r return redirect()->route('profile', ['id' => 1]); +For convenience, Laravel also offers the global `to_route` function: + + return to_route('profile', ['id' => 1]); + #### Populating Parameters Via Eloquent Models From 5c17c247e39d96d682c607731f9825b56769f9d7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 30 Jan 2023 16:54:10 -0600 Subject: [PATCH 0625/2609] wip --- queues.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index 324a301d313..638195c574e 100644 --- a/queues.md +++ b/queues.md @@ -841,8 +841,9 @@ When chaining jobs, you may use the `catch` method to specify a closure that sho ])->catch(function (Throwable $e) { // A job within the chain has failed... })->dispatch(); - -> {note} Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. + +> **Warning** +> Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. ### Customizing The Queue & Connection From a8c46a16786ac3e6d6e09af92b6d3ba7cf919d71 Mon Sep 17 00:00:00 2001 From: Philip <17368112+vHeemstra@users.noreply.github.com> Date: Tue, 31 Jan 2023 14:50:51 +0100 Subject: [PATCH 0626/2609] docs: Added note about default attribute format (#8493) * Update eloquent.md Added note about default attribute format needing to be as they would come from the raw database query. See laravel/framework#45857 * Update eloquent.md * Update eloquent.md * Update eloquent.md --------- Co-authored-by: Taylor Otwell --- eloquent.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index dec210bb361..f8c1db97e2a 100644 --- a/eloquent.md +++ b/eloquent.md @@ -339,7 +339,7 @@ By default, all Eloquent models will use the default database connection that is ### Default Attribute Values -By default, a newly instantiated model instance will not contain any attribute values. If you would like to define the default values for some of your model's attributes, you may define an `$attributes` property on your model: +By default, a newly instantiated model instance will not contain any attribute values. If you would like to define the default values for some of your model's attributes, you may define an `$attributes` property on your model. Attribute values placed in the `$attributes` array should be in their raw, "storable" format as if they were just read from the database: '[]', 'delayed' => false, ]; } From c0f742a4f06a21295614705fbffe2b96594f9443 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 1 Feb 2023 01:40:56 +1100 Subject: [PATCH 0627/2609] [9.x] Documents "missing" validation rule (#8465) * Documents "missing" validation rule * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/validation.md b/validation.md index 68556f31472..c0654c00399 100644 --- a/validation.md +++ b/validation.md @@ -870,6 +870,11 @@ Below is a list of all available validation rules and their function: [MIME Type By File Extension](#rule-mimes) [Min](#rule-min) [Min Digits](#rule-min-digits) +[Missing](#rule-missing) +[Missing If](#rule-missing-if) +[Missing Unless](#rule-missing-unless) +[Missing With](#rule-missing-with) +[Missing With All](#rule-missing-with-all) [Multiple Of](#rule-multiple-of) [Not In](#rule-not-in) [Not Regex](#rule-not-regex) @@ -1411,6 +1416,31 @@ The field under validation must be a multiple of _value_. > **Warning** > The [`bcmath` PHP extension](https://www.php.net/manual/en/book.bc.php) is required in order to use the `multiple_of` rule. + +#### missing + +The field under validation must not be present in the input data. + + + #### missing_if:_anotherfield_,_value_,... + + The field under validation must not be present if the _anotherfield_ field is equal to any _value_. + + + #### missing_unless:_anotherfield_,_value_ + +The field under validation must not be present unless the _anotherfield_ field is equal to any _value_. + + + #### missing_with:_foo_,_bar_,... + + The field under validation must not be present _only if_ any of the other specified fields are present. + + + #### missing_with_all:_foo_,_bar_,... + + The field under validation must not be present _only if_ all of the other specified fields are present. + #### not_in:_foo_,_bar_,... From f84d53376c75048e6fb12a8faf38746bfd39d4ed Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 Jan 2023 08:42:43 -0600 Subject: [PATCH 0628/2609] wip --- http-client.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/http-client.md b/http-client.md index 613164ba9e3..9bfb90ce0e4 100644 --- a/http-client.md +++ b/http-client.md @@ -248,6 +248,12 @@ If you have a response instance and would like to throw an instance of `Illumina // Throw an exception if an error occurred and the given closure resolves to false... $response->throwUnless(fn ($response) => false); + // Throw an exception if the response has a specific status code... + $response->throwIfStatus(403); + + // Throw an exception unless the response has a specific status code... + $response->throwUnlessStatus(200); + return $response['user']['id']; The `Illuminate\Http\Client\RequestException` instance has a public `$response` property which will allow you to inspect the returned response. From 474adc9e7497df2b50e266dbcb955207ff7639d3 Mon Sep 17 00:00:00 2001 From: Craig Morris Date: Tue, 31 Jan 2023 14:46:51 +0000 Subject: [PATCH 0629/2609] Skip if batch cancelled (#8495) * add docs for SkipIfBatchCancelled * typo * Update queues.md * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/queues.md b/queues.md index d6394f53abe..b15c90be773 100644 --- a/queues.md +++ b/queues.md @@ -1415,20 +1415,18 @@ 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: +As you may have noticed in the previous examples, batched jobs should typically determine if their corresponding batch has been cancelled before continuing execution. However, for convenience, you may assign the `SkipIfBatchCancelled` [middleware](#job-middleware) to the job instead. As its name indicates, this middleware will instruct Laravel to not process the job if its corresponding batch has been cancelled: + + use Illuminate\Queue\Middleware\SkipIfBatchCancelled; /** - * Execute the job. + * Get the middleware the job should pass through. * - * @return void + * @return array */ - public function handle() + public function middleware() { - if ($this->batch()->cancelled()) { - return; - } - - // Continue processing... + return [new SkipIfBatchCancelled]; } From d16b4d08a7de7f2a000746be5d9dab819be6fd0d Mon Sep 17 00:00:00 2001 From: mpyw Date: Tue, 31 Jan 2023 23:52:36 +0900 Subject: [PATCH 0630/2609] [9.x] Introduce validation `:ascii` options (#8474) * [9.x] Follow laravel/framework#45769 changes * Correct the definite articles * Avoid ambiguous explanations * Warning about Unicode dashes and underscores * Link to unicode.org * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/validation.md b/validation.md index c0654c00399..08a157352e7 100644 --- a/validation.md +++ b/validation.md @@ -943,17 +943,35 @@ The field under validation must be a value after or equal to the given date. For #### alpha -The field under validation must be entirely alphabetic characters. +The field under validation must be entirely Unicode alphabetic characters contained in [`\p{L}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AL%3A%5D&g=&i=) and [`\p{M}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AM%3A%5D&g=&i=). + +To restrict this validation rule to characters in the ASCII range (`a-z` and `A-Z`), you may provide the `ascii` option to the validation rule: + +```php +'username' => 'alpha:ascii', +``` #### alpha_dash -The field under validation may have alpha-numeric characters, as well as dashes and underscores. +The field under validation must be entirely Unicode alpha-numeric characters contained in [`\p{L}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AL%3A%5D&g=&i=), [`\p{M}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AM%3A%5D&g=&i=), [`\p{N}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AN%3A%5D&g=&i=), as well as ASCII dashes (`-`) and ASCII underscores (`_`). + +To restrict this validation rule to characters in the ASCII range (`a-z` and `A-Z`), you may provide the `ascii` option to the validation rule: + +```php +'username' => 'alpha_dash:ascii', +``` #### alpha_num -The field under validation must be entirely alpha-numeric characters. +The field under validation must be entirely Unicode alpha-numeric characters contained in [`\p{L}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AL%3A%5D&g=&i=), [`\p{M}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AM%3A%5D&g=&i=), and [`\p{N}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AN%3A%5D&g=&i=). + +To restrict this validation rule to characters in the ASCII range (`a-z` and `A-Z`), you may provide the `ascii` option to the validation rule: + +```php +'username' => 'alpha_num:ascii', +``` #### array From 030a83b995642b59ac695ff259157e2e0098e4af Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 Jan 2023 08:57:23 -0600 Subject: [PATCH 0631/2609] enum array casts --- eloquent-mutators.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index f1dfbfd4a3a..032c45afe93 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -449,6 +449,23 @@ Once you have defined the cast on your model, the specified attribute will be au $server->save(); } + +#### Casting Arrays Of Enums + +Sometimes you may need your model to store an array of enum values within a single column. To accomplish this, you may utilize the `AsEnumArrayObject` or `AsEnumCollection` casts provided by Laravel: + + use App\Enums\ServerStatus; + use Illuminate\Database\Eloquent\Casts\AsEnumCollection; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'statuses' => AsEnumCollection.':'.ServerStatus::class, + ]; + ### Encrypted Casting From be805888a5f5858157cd123b2a21a779e5c16cf9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 Jan 2023 08:58:38 -0600 Subject: [PATCH 0632/2609] wip --- http-tests.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/http-tests.md b/http-tests.md index 8426cc1de8d..4f9f9d7d046 100644 --- a/http-tests.md +++ b/http-tests.md @@ -639,6 +639,8 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertJson](#assert-json) [assertJsonCount](#assert-json-count) [assertJsonFragment](#assert-json-fragment) +[assertJsonIsArray](#assert-json-is-array) +[assertJsonIsObject](#assert-json-is-object) [assertJsonMissing](#assert-json-missing) [assertJsonMissingExact](#assert-json-missing-exact) [assertJsonMissingValidationErrors](#assert-json-missing-validation-errors) @@ -804,6 +806,20 @@ Assert that the response contains the given JSON data anywhere in the response: $response->assertJsonFragment(['name' => 'Taylor Otwell']); + +#### assertJsonIsArray + +Assert that the response JSON is an array: + + $response->assertJsonIsArray(); + + +#### assertJsonIsObject + +Assert that the response JSON is an object: + + $response->assertJsonIsObject(); + #### assertJsonMissing From 16bf3cb9aa28072cfc11e68871894b9afd83770e Mon Sep 17 00:00:00 2001 From: Timur Fralik <36575061+Timur11timur@users.noreply.github.com> Date: Tue, 31 Jan 2023 17:01:04 +0200 Subject: [PATCH 0633/2609] Add Arr::sortDesc() description (#8470) --- helpers.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/helpers.md b/helpers.md index 388572fe644..f1f90903451 100644 --- a/helpers.md +++ b/helpers.md @@ -62,6 +62,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::set](#method-array-set) [Arr::shuffle](#method-array-shuffle) [Arr::sort](#method-array-sort) +[Arr::sortDesc](#method-array-sort-desc) [Arr::sortRecursive](#method-array-sort-recursive) [Arr::toCssClasses](#method-array-to-css-classes) [Arr::undot](#method-array-undot) @@ -904,6 +905,41 @@ You may also sort the array by the results of a given closure: ] */ + +#### `Arr::sortDesc()` {.collection-method} + +The `Arr::sortDesc` method sorts an array in descending order by its values: + + use Illuminate\Support\Arr; + + $array = ['Desk', 'Table', 'Chair']; + + $sorted = Arr::sortDesc($array); + + // ['Table', 'Desk', 'Chair'] + +You may also sort the array by the results of a given closure: + + use Illuminate\Support\Arr; + + $array = [ + ['name' => 'Desk'], + ['name' => 'Table'], + ['name' => 'Chair'], + ]; + + $sorted = array_values(Arr::sortDesc($array, function ($value) { + return $value['name']; + })); + + /* + [ + ['name' => 'Table'], + ['name' => 'Desk'], + ['name' => 'Chair'], + ] + */ + #### `Arr::sortRecursive()` {.collection-method} From e097508016800ead06615ec2b209079e54e5f6fa Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 Jan 2023 09:01:45 -0600 Subject: [PATCH 0634/2609] wip --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index f8c1db97e2a..714cfeefe87 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1659,5 +1659,5 @@ Sometimes you may wish to "save" a given model without dispatching any events. Y You may also "update", "delete", "soft delete", "restore", and "replicate" a given model without dispatching any events: $user->deleteQuietly(); - + $user->forceDeleteQuietly(); $user->restoreQuietly(); From fab58af982daad3f020590278bb78e6ccf228585 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 Jan 2023 09:04:22 -0600 Subject: [PATCH 0635/2609] wip --- installation.md | 4 ++-- mail.md | 6 +++--- sail.md | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/installation.md b/installation.md index 5f8402012a4..dcdf41cb7fc 100644 --- a/installation.md +++ b/installation.md @@ -184,13 +184,13 @@ Once the application's Docker containers have been started, you can access the a ### Choosing Your Sail Services -When creating a new Laravel application via Sail, you may use the `with` query string variable to choose which services should be configured in your new application's `docker-compose.yml` file. Available services include `mysql`, `pgsql`, `mariadb`, `redis`, `memcached`, `meilisearch`, `minio`, `selenium`, and `mailhog`: +When creating a new Laravel application via Sail, you may use the `with` query string variable to choose which services should be configured in your new application's `docker-compose.yml` file. Available services include `mysql`, `pgsql`, `mariadb`, `redis`, `memcached`, `meilisearch`, `minio`, `selenium`, and `mailpit`: ```shell curl -s "https://laravel.build/example-app?with=mysql,redis" | bash ``` -If you do not specify which services you would like configured, a default stack of `mysql`, `redis`, `meilisearch`, `mailhog`, and `selenium` will be configured. +If you do not specify which services you would like configured, a default stack of `mysql`, `redis`, `meilisearch`, `mailpit`, and `selenium` will be configured. You may instruct Sail to install a default [Devcontainer](/docs/{{version}}/sail#using-devcontainers) by adding the `devcontainer` parameter to the URL: diff --git a/mail.md b/mail.md index 3fa303e362e..98e8f49a2da 100644 --- a/mail.md +++ b/mail.md @@ -935,7 +935,7 @@ When designing a mailable's template, it is convenient to quickly preview the re }); > **Warning** -> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog) or [Mailpit](https://github.com/axllent/mailpit) or [HELO](https://usehelo.com). +> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog), [Mailpit](https://github.com/axllent/mailpit), or [HELO](https://usehelo.com). ## Localizing Mailables @@ -1027,11 +1027,11 @@ When developing an application that sends email, you probably don't want to actu Instead of sending your emails, the `log` mail driver will write all email messages to your log files for inspection. Typically, this driver would only be used during local development. For more information on configuring your application per environment, check out the [configuration documentation](/docs/{{version}}/configuration#environment-configuration). -#### HELO / Mailtrap / MailHog +#### HELO / Mailtrap / Mailpit Alternatively, you may use a service like [HELO](https://usehelo.com) or [Mailtrap](https://mailtrap.io) and the `smtp` driver to send your email messages to a "dummy" mailbox where you may view them in a true email client. This approach has the benefit of allowing you to actually inspect the final emails in Mailtrap's message viewer. -If you are using [Laravel Sail](/docs/{{version}}/sail), you may preview your messages using [MailHog](https://github.com/mailhog/MailHog). When Sail is running, you may access the MailHog interface at: `http://localhost:8025`. +If you are using [Laravel Sail](/docs/{{version}}/sail), you may preview your messages using [Mailpit](https://github.com/axllent/mailpit). When Sail is running, you may access the Mailpit interface at: `http://localhost:8025`. #### Using A Global `to` Address diff --git a/sail.md b/sail.md index f70879f3398..ba73f8c0ad3 100644 --- a/sail.md +++ b/sail.md @@ -323,15 +323,15 @@ selenium: ## 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. When using Sail, MailHog's default host is `mailhog` and is available via port 1025: +Laravel Sail's default `docker-compose.yml` file contains a service entry for [Mailpit](https://github.com/mailpit/Mailpit). Mailpit 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. When using Sail, Mailpit's default host is `mailpit` and is available via port 1025: ```ini -MAIL_HOST=mailhog +MAIL_HOST=mailpit MAIL_PORT=1025 MAIL_ENCRYPTION=null ``` -When Sail is running, you may access the MailHog web interface at: http://localhost:8025 +When Sail is running, you may access the Mailpit web interface at: http://localhost:8025 ## Container CLI From 1d96b84278a3c135c0cfd7c488d59a0fb58b88cc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 Jan 2023 09:04:45 -0600 Subject: [PATCH 0636/2609] wip --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 98e8f49a2da..a818f9f8a33 100644 --- a/mail.md +++ b/mail.md @@ -935,7 +935,7 @@ When designing a mailable's template, it is convenient to quickly preview the re }); > **Warning** -> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog), [Mailpit](https://github.com/axllent/mailpit), or [HELO](https://usehelo.com). +> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [Mailpit](https://github.com/axllent/mailpit) or [HELO](https://usehelo.com). ## Localizing Mailables From d7a1c6ae30e338279cb0d66f9022a5e3f648c61d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 31 Jan 2023 10:13:22 -0600 Subject: [PATCH 0637/2609] wip --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 032c45afe93..db1f32ee999 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -463,7 +463,7 @@ Sometimes you may need your model to store an array of enum values within a sing * @var array */ protected $casts = [ - 'statuses' => AsEnumCollection.':'.ServerStatus::class, + 'statuses' => AsEnumCollection::class.':'.ServerStatus::class, ]; From ceeebcd1acb173ffe39d095e46ef3e3fac5a2407 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 31 Jan 2023 17:41:37 +0100 Subject: [PATCH 0638/2609] [9.x] Refer to general laravel-assets tag (#8496) * Update horizon.md * Update telescope.md --- horizon.md | 4 ++-- telescope.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/horizon.md b/horizon.md index 264d7d4494c..f51fd62c2ed 100644 --- a/horizon.md +++ b/horizon.md @@ -176,13 +176,13 @@ When upgrading to a new major version of Horizon, it's important that you carefu php artisan horizon:publish ``` -To keep the assets up-to-date and avoid issues in future updates, you may add the `horizon:publish` command to the `post-update-cmd` scripts in your application's `composer.json` file: +To keep the assets up-to-date and avoid issues in future updates, you may add the `vendor:publish --tag=laravel-assets` command to the `post-update-cmd` scripts in your application's `composer.json` file: ```json { "scripts": { "post-update-cmd": [ - "@php artisan horizon:publish --ansi" + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" ] } } diff --git a/telescope.md b/telescope.md index 77d2fd39fe4..b5e2a09a055 100644 --- a/telescope.md +++ b/telescope.md @@ -156,13 +156,13 @@ In addition, when upgrading to any new Telescope version, you should re-publish php artisan telescope:publish ``` -To keep the assets up-to-date and avoid issues in future updates, you may add the `telescope:publish` command to the `post-update-cmd` scripts in your application's `composer.json` file: +To keep the assets up-to-date and avoid issues in future updates, you may add the `vendor:publish --tag=laravel-assets` command to the `post-update-cmd` scripts in your application's `composer.json` file: ```json { "scripts": { "post-update-cmd": [ - "@php artisan telescope:publish --ansi" + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" ] } } From f9f0c2f08879bead47c452fac8f2152e98a61e3e Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 1 Feb 2023 12:54:30 +1000 Subject: [PATCH 0639/2609] [9.x] Document installation of Vue and React plugins (#8497) * Document installation of Vue and React plugins * Use long-form CLI option for consistency and clarity * Fix --- vite.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/vite.md b/vite.md index a49ce6c0de5..23932319826 100644 --- a/vite.md +++ b/vite.md @@ -260,7 +260,13 @@ export default defineConfig({ ### Vue -There are a few additional options you will need to include in the `vite.config.js` configuration file when using the Vue plugin with the Laravel plugin: +If you would like to build your front-end using the [Vue](https://vuejs.org/) framework, then you will also need to install the `@vitejs/plugin-vue` plugin: + +```sh +npm install --save-dev @vitejs/plugin-vue +``` + +You may then include the plugin in your `vite.config.js` configuration file. There are a few additional options you will need when using the Vue plugin with Laravel: ```js import { defineConfig } from 'vite'; @@ -298,7 +304,30 @@ export default defineConfig({ ### React -When using Vite with React, you will need to ensure that any files containing JSX have a `.jsx` or `.tsx` extension, remembering to update your entry point, if required, as [shown above](#configuring-vite). You will also need to include the additional `@viteReactRefresh` Blade directive alongside your existing `@vite` directive. +If you would like to build your front-end using the [React](https://reactjs.org/) framework, then you will also need to install the `@vitejs/plugin-react` plugin: + +```sh +npm install --save-dev @vitejs/plugin-react +``` + +You may then include the plugin in your `vite.config.js` configuration file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [ + laravel(['resources/js/app.jsx']), + react(), + ], +}); +``` + +You will need to ensure that any files containing JSX have a `.jsx` or `.tsx` extension, remembering to update your entry point, if required, as [shown above](#configuring-vite). + +You will also need to include the additional `@viteReactRefresh` Blade directive alongside your existing `@vite` directive. ```blade @viteReactRefresh @@ -655,7 +684,7 @@ Vite::useCspNonce($nonce); If your Vite manifest includes `integrity` hashes for your assets, Laravel will automatically add the `integrity` attribute on any script and style tags it generates in order to enforce [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). By default, Vite does not include the `integrity` hash in its manifest, but you may enable it by installing the [`vite-plugin-manifest-uri`](https://www.npmjs.com/package/vite-plugin-manifest-sri) NPM plugin: ```shell -npm install -D vite-plugin-manifest-sri +npm install --save-dev vite-plugin-manifest-sri ``` You may then enable this plugin in your `vite.config.js` file: From 68e145ea5d0c7e97057eb4b5ee7e9770e09c19eb Mon Sep 17 00:00:00 2001 From: Volodya Kurshudyan <70023120+xurshudyan@users.noreply.github.com> Date: Wed, 1 Feb 2023 18:54:40 +0400 Subject: [PATCH 0640/2609] Add forceDeleting method to events list (#8500) Co-authored-by: Valodia --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 714cfeefe87..46e62ee16ba 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1421,7 +1421,7 @@ The `is` and `isNot` methods are also available when using the `belongsTo`, `has > **Note** > Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). -Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `trashed`, `forceDeleted`, `restoring`, `restored`, and `replicating`. +Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `trashed`, `forceDeleting`, `forceDeleted`, `restoring`, `restored`, and `replicating`. The `retrieved` event will dispatch when an existing model is retrieved from the database. When a new model is saved for the first time, the `creating` and `created` events will dispatch. The `updating` / `updated` events will dispatch when an existing model is modified and the `save` method is called. The `saving` / `saved` events will dispatch when a model is created or updated - even if the model's attributes have not been changed. Event names ending with `-ing` are dispatched before any changes to the model are persisted, while events ending with `-ed` are dispatched after the changes to the model are persisted. From 322a5d9810854927906d44e856c50d23be217245 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Thu, 2 Feb 2023 03:54:54 +1300 Subject: [PATCH 0641/2609] Fix Mailpit link (#8498) --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index ba73f8c0ad3..8114ae91998 100644 --- a/sail.md +++ b/sail.md @@ -323,7 +323,7 @@ selenium: ## Previewing Emails -Laravel Sail's default `docker-compose.yml` file contains a service entry for [Mailpit](https://github.com/mailpit/Mailpit). Mailpit 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. When using Sail, Mailpit's default host is `mailpit` and is available via port 1025: +Laravel Sail's default `docker-compose.yml` file contains a service entry for [Mailpit](https://github.com/axllent/mailpit). Mailpit 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. When using Sail, Mailpit's default host is `mailpit` and is available via port 1025: ```ini MAIL_HOST=mailpit From f2676cdfd80c6dca792b5a42b0e670d5f2d24584 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Wed, 1 Feb 2023 11:35:26 -0500 Subject: [PATCH 0642/2609] [9.x] Add additional Eloquent factory `sequence` method docs (#8501) * Add `sequence()` method docs * Update eloquent-factories.md --------- Co-authored-by: Taylor Otwell --- eloquent-factories.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/eloquent-factories.md b/eloquent-factories.md index d2d88fd67bd..9fa8dbc08e8 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -256,6 +256,16 @@ Within a sequence closure, you may access the `$index` or `$count` properties on ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) ->create(); +For convenience, sequences may also be applied using the `sequence` method, which simply invokes the `state` method internally. The `sequence` method accepts a closure or arrays of sequenced attributes: + + $users = User::factory() + ->count(2) + ->sequence( + ['name' => 'First User'], + ['name' => 'Second User'], + ) + ->create(); + ## Factory Relationships From cfd8eed9cb459447c1d85f9f44cd07bb92eb0b5a Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Wed, 1 Feb 2023 21:52:25 +0000 Subject: [PATCH 0643/2609] [9.x] value helper function further parameters passed to closure (#8502) * value helper function further parameters Updated the docs for the `value` function to [reflect the fact additional parameters can be passed to the closure if one is provided as the first parameter](https://github.com/laravel/framework/blob/9.x/src/Illuminate/Collections/helpers.php#L186-L189). * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/helpers.md b/helpers.md index f1f90903451..d8b8093a9ca 100644 --- a/helpers.md +++ b/helpers.md @@ -4045,6 +4045,14 @@ The `value` function returns the value it is given. However, if you pass a closu }); // false + +Additional arguments may be passed to the `value` function. If the first argument is a closure then the additional parameters will be passed to the closure as arguments, otherwise they will be ignored: + + $result = value(function ($name) { + return $parameter; + }, 'Taylor'); + + // 'Taylor' #### `view()` {.collection-method} From 22d4444c6cb56f750936a64dc32b3d08968dcc9b Mon Sep 17 00:00:00 2001 From: AyubM Date: Thu, 2 Feb 2023 15:39:09 -0500 Subject: [PATCH 0644/2609] Move tagged cache access info to correct section (#8503) --- cache.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cache.md b/cache.md index 6812e22aa6a..f6572bc01e4 100644 --- a/cache.md +++ b/cache.md @@ -271,7 +271,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. Items stored via tags may not be accessed without also providing the tags that were used to store the value. For example, let's access a tagged cache and `put` a value into 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); @@ -280,7 +280,7 @@ Cache tags allow you to tag related items in the cache and then flush all cached ### Accessing Tagged Cache Items -To retrieve a tagged cache item, pass the same ordered list of tags to the `tags` method and then call the `get` method with the key you wish to retrieve: +Items stored via tags may not be accessed without also providing the tags that were used to store the value. To retrieve a tagged cache item, pass the same ordered list of tags to the `tags` method and then call the `get` method with the key you wish to retrieve: $john = Cache::tags(['people', 'artists'])->get('John'); From 88776d17d68d28baffd98333dea46b1aee80d90c Mon Sep 17 00:00:00 2001 From: Umar Jafar <68953325+UmarJafar@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:16:47 +0500 Subject: [PATCH 0645/2609] Grammar Mistake Fixed: Adding pronoun instead to repeating DBngin again (#8504) * Grammer Mistake Fixed: Adding pronoun instead to repeating DBngin again * Update valet.md --------- Co-authored-by: Umar Jafar Co-authored-by: Taylor Otwell --- valet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valet.md b/valet.md index 01726e0d8fc..998f9f5cef7 100644 --- a/valet.md +++ b/valet.md @@ -124,7 +124,7 @@ Once this file has been created, you may simply execute the `valet use` command #### 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. +If your application needs a database, check out [DBngin](https://dbngin.com), which 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 From 58c1b94bb53ad9880946543d6adfc1c1be48a2c4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 3 Feb 2023 13:34:46 -0600 Subject: [PATCH 0646/2609] wip --- http-tests.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/http-tests.md b/http-tests.md index 4f9f9d7d046..c314fa834f9 100644 --- a/http-tests.md +++ b/http-tests.md @@ -15,6 +15,7 @@ - [Available Assertions](#available-assertions) - [Response Assertions](#response-assertions) - [Authentication Assertions](#authentication-assertions) + - [Validation Assertions](#validation-assertions) ## Introduction @@ -1287,3 +1288,33 @@ Assert that a user is not authenticated: Assert that a specific user is authenticated: $this->assertAuthenticatedAs($user, $guard = null); + + +## Validation Assertions + +Laravel provides two primary validation related assertions that you may use to ensure the data provided in your request was either valid or invalid. + + +#### assertValid + +Assert that the response has no validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session: + + // Assert that no validation errors are present... + $response->assertValid(); + + // Assert that the given keys do not have validation errors... + $response->assertValid(['name', 'email']); + + +#### assertInvalid + +Assert that the response has validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session: + + $response->assertInvalid(['name', 'email']); + +You may also assert that a given key has a particular validation error message. When doing so, you may provide the entire message or only a small portion of the message: + + $response->assertInvalid([ + 'name' => 'The name field is required.', + 'email' => 'valid email address', + ]); From a01d47d5d171373e29a95bf795c264a13aa773d4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 3 Feb 2023 14:21:54 -0600 Subject: [PATCH 0647/2609] wip --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index d2a9190751e..ff3a9abe76e 100644 --- a/validation.md +++ b/validation.md @@ -2150,9 +2150,9 @@ If you only need the functionality of a custom rule once throughout your applica 'title' => [ 'required', 'max:255', - function (string $attribute, string|null $value, Closure $fail) { + function (string $attribute, mixed $value, Closure $fail) { if ($value === 'foo') { - $fail('The '.$attribute.' is invalid.'); + $fail("The {$attribute} is invalid."); } }, ], From 777044e8a99ab11ee7c85496b72eed8d4b51df79 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 3 Feb 2023 16:46:00 -0600 Subject: [PATCH 0648/2609] document cache tag pruning --- cache.md | 11 +++++++++++ upgrade.md | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/cache.md b/cache.md index 2654f42c92d..08313cd99bd 100644 --- a/cache.md +++ b/cache.md @@ -13,6 +13,7 @@ - [Storing Tagged Cache Items](#storing-tagged-cache-items) - [Accessing Tagged Cache Items](#accessing-tagged-cache-items) - [Removing Tagged Cache Items](#removing-tagged-cache-items) + - [Pruning Stale Cache Tags](#pruning-stale-cache-tags) - [Atomic Locks](#atomic-locks) - [Driver Prerequisites](#lock-driver-prerequisites) - [Managing Locks](#managing-locks) @@ -297,6 +298,16 @@ In contrast, this statement would remove only cached values tagged with `authors Cache::tags('authors')->flush(); + +### Pruning Stale Cache Tags + +> **Warning** +> Pruning stale cache tags is only necessary when using Redis as your application's cache driver. + +In order to properly prune stale cache tag entries when using the Redis cache driver, Laravel's `cache:prune-stale-tags` Artisan command should be [scheduled](/docs/{{version}}/scheduling) in your application's `App\Console\Kernel` class: + + $schedule->command('cache:prune-stale-tags')->hourly(); + ## Atomic Locks diff --git a/upgrade.md b/upgrade.md index bb67923f0fb..c0e854f947a 100644 --- a/upgrade.md +++ b/upgrade.md @@ -18,6 +18,7 @@
    - [Model "Dates" Property](#model-dates-property) +- [Redis Cache Tags](#redis-cache-tags) - [Service Mocking](#service-mocking)
    @@ -74,6 +75,19 @@ You should update the `minimum-stability` setting in your application's `compose "minimum-stability": "stable", ``` +### Cache + + +#### Redis Cache Tags + +**Likelihood Of Impact: Medium** + +Redis [cache tag](/docs/{{version}}/cache#cache-tags) support has been rewritten for better performance and storage efficiency. In previously releases of Laravel, stale cache tags would accumulate in the cache when using Redis as your application's cache driver. + +However, to properly prune stale cache tag entries, Laravel's new `cache:prune-stale-tags` Artisan command should be [scheduled](/docs/{{version}}/scheduling) in your application's `App\Console\Kernel` class: + + $schedule->command('cache:prune-stale-tags')->hourly(); + ### Database From dc86513ebceb31d522b9f24eadfe46407a8ab1b9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 3 Feb 2023 17:00:34 -0600 Subject: [PATCH 0649/2609] more upgrade guide documentation --- upgrade.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/upgrade.md b/upgrade.md index c0e854f947a..3f5f91cc98b 100644 --- a/upgrade.md +++ b/upgrade.md @@ -17,6 +17,7 @@
    +- [Database Expressions](#database-expressions) - [Model "Dates" Property](#model-dates-property) - [Redis Cache Tags](#redis-cache-tags) - [Service Mocking](#service-mocking) @@ -35,6 +36,7 @@ - [Relation `getBaseQuery` Method](#relation-getbasequery-method) - [The `Redirect::home` Method](#redirect-home) - [The `Bus::dispatchNow` Method](#dispatch-now) +- [ULID Columns](#ulid-columns)
    @@ -90,6 +92,23 @@ However, to properly prune stale cache tag entries, Laravel's new `cache:prune-s ### Database + +#### Database Expressions + +**Likelihood Of Impact: Medium** + +Database "expressions" (typically generated via `DB::raw`) have been rewritten in Laravel 10.x to offer additional functionality in the future. Notably, the grammar's raw string value must now be retrieved via the expression's `getValue(Grammar $grammar)` method. Casting an expression to a string using `(string)` is no longer supported. + +**Typically, this does not affect end-user applications**; however, if your application is manually casting database expressions to strings using `(string)` or invoking the `__toString` method on the expression directly, you should update your code to invoke the `getValue` method instead: + +```php +use Illuminate\Support\Facades\DB; + +$expression = DB::raw('select 1'); + +$string = $expression->getValue(DB::connection()->getQueryGrammar()); +``` + #### Query Exception Constructor @@ -97,6 +116,19 @@ However, to properly prune stale cache tag entries, Laravel's new `cache:prune-s The `Illuminate\Database\QueryException` constructor now accepts a string connection name as its first argument. If your application is mainly throwing this exception, you should adjust your code accordingly. + +#### ULID Columns + +**Likelihood Of Impact: Low** + +When migrations invoke the `ulid` method without any arguments, the column will now be named `ulid`. In previous releases of Laravel, invoking this method without any arguments created a column erroneously named `uuid`: + + $table->ulid(); + +To explicitly specify a column name when invoking the `ulid` method, you may pass the column name to the method: + + $table->ulid('ulid'); + ### Eloquent @@ -139,6 +171,13 @@ The deprecated `Bus::dispatchNow` and `dispatch_now` methods have been removed. ### Routing + +#### Middleware Aliases + +**Likelihood Of Impact: Optional** + +In new Laravel applications, the `$routeMiddleware` property of the `App\Http\Kernel` class has been renamed to `$middlewareAliases` to better reflect its purpose. You are welcome to rename this property in your existing applications; however, it is not required. + #### Rate Limiter Return Values From a1a64940238de04fe85b4762bd0ce26098b0b513 Mon Sep 17 00:00:00 2001 From: Roberto Butti Date: Sat, 4 Feb 2023 16:13:33 +0100 Subject: [PATCH 0650/2609] changed PHP requirement for Laravel 10 (PHP 8.1 min) (#8506) --- deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index 965ebbe48d3..08bc65ac2be 100644 --- a/deployment.md +++ b/deployment.md @@ -24,7 +24,7 @@ The Laravel framework has a few system requirements. You should ensure that your
    -- PHP >= 8.0 +- PHP >= 8.1 - BCMath PHP Extension - Ctype PHP Extension - cURL PHP Extension From 538b5073f6eb274a1cb29391ba4ce74ff319fd4a Mon Sep 17 00:00:00 2001 From: MltStephane Date: Sun, 5 Feb 2023 16:13:38 +0100 Subject: [PATCH 0651/2609] fix: fix typo in link (#8509) --- billing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/billing.md b/billing.md index cbb890ff5e2..8c31b67d372 100644 --- a/billing.md +++ b/billing.md @@ -1406,7 +1406,7 @@ If you would like to offer trial periods without collecting the user's payment m ]); > **Warning** -> 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. +> 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 billable model instance will return `true` if the current date is not past the value of `trial_ends_at`: From 041f38351d4d28459650edcbe74614fc05b7ed7a Mon Sep 17 00:00:00 2001 From: Roberto Butti Date: Sun, 5 Feb 2023 16:15:58 +0100 Subject: [PATCH 0652/2609] Update octane.md (#8508) * Update octane.md - update the link to openswoole website - update the requirements for PHP 8.1 (because Laravel 10 requires PHP 8.1) - added a small section mentioning how to install Open Swoole * Update octane.md --------- Co-authored-by: Taylor Otwell --- octane.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/octane.md b/octane.md index ef6f596e0bb..b9f03224c7c 100644 --- a/octane.md +++ b/octane.md @@ -26,7 +26,7 @@ ## Introduction -[Laravel Octane](https://github.com/laravel/octane) supercharges your application's performance by serving your application using high-powered application servers, including [Open Swoole](https://swoole.co.uk), [Swoole](https://github.com/swoole/swoole-src), and [RoadRunner](https://roadrunner.dev). Octane boots your application once, keeps it in memory, and then feeds it requests at supersonic speeds. +[Laravel Octane](https://github.com/laravel/octane) supercharges your application's performance by serving your application using high-powered application servers, including [Open Swoole](https://openswoole.com/), [Swoole](https://github.com/swoole/swoole-src), and [RoadRunner](https://roadrunner.dev). Octane boots your application once, keeps it in memory, and then feeds it requests at supersonic speeds. ## Installation @@ -46,8 +46,8 @@ php artisan octane:install ## Server Prerequisites -> **Warning** -> Laravel Octane requires [PHP 8.0+](https://php.net/releases/). +> **Warning** +> Laravel Octane requires [PHP 8.1+](https://php.net/releases/). ### RoadRunner @@ -103,10 +103,21 @@ If you plan to use the Swoole application server to serve your Laravel Octane ap pecl install swoole ``` + +#### Open Swoole + +If you want to use the Open Swoole application server to serve your Laravel Octane application, you must install the Open Swoole PHP extension. Typically, this can be done via PECL: + +```shell +pecl install openswoole +``` + +Using Laravel Octane with Open Swoole grants the same functionality provided by Swoole, such as concurrent tasks, ticks, and intervals. + #### Swoole Via Laravel Sail -> **Warning** +> **Warning** > Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. Alternatively, you may develop your Swoole based Octane application using [Laravel Sail](/docs/{{version}}/sail), the official Docker based development environment for Laravel. Laravel Sail includes the Swoole extension by default. However, you will still need to adjust the `supervisor.conf` file used by Sail to keep your application running. To get started, execute the `sail:publish` Artisan command: @@ -164,7 +175,7 @@ By default, applications running via Octane generate links prefixed with `http:/ ### Serving Your Application Via Nginx -> **Note** +> **Note** > If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). In production environments, you should serve your Octane application behind a traditional web server such as a Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. @@ -385,7 +396,7 @@ $service->method($request->input('name')); The global `request` helper will always return the request the application is currently handling and is therefore safe to use within your application. -> **Warning** +> **Warning** > It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. @@ -456,7 +467,7 @@ While building your application, you should take special care to avoid creating ## Concurrent Tasks -> **Warning** +> **Warning** > This feature requires [Swoole](#swoole). When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's `concurrently` method. You may combine this method with PHP array destructuring to retrieve the results of each operation: @@ -483,7 +494,7 @@ When invoking the `concurrently` method, you should not provide more than 1024 t ## Ticks & Intervals -> **Warning** +> **Warning** > This feature requires [Swoole](#swoole). When using Swoole, you may register "tick" operations that will be executed every specified number of seconds. You may register "tick" callbacks via the `tick` method. The first argument provided to the `tick` method should be a string that represents the name of the ticker. The second argument should be a callable that will be invoked at the specified interval. @@ -506,7 +517,7 @@ Octane::tick('simple-ticker', fn () => ray('Ticking...')) ## The Octane Cache -> **Warning** +> **Warning** > This feature requires [Swoole](#swoole). When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. Therefore, this cache driver is an excellent choice for applications that need extreme read / write speeds from their caching layer. @@ -517,7 +528,7 @@ This cache driver is powered by [Swoole tables](https://www.swoole.co.uk/docs/mo Cache::store('octane')->put('framework', 'Laravel', 30); ``` -> **Note** +> **Note** > The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. @@ -536,7 +547,7 @@ Cache::store('octane')->interval('random', function () { ## Tables -> **Warning** +> **Warning** > This feature requires [Swoole](#swoole). When using Swoole, you may define and interact with your own arbitrary [Swoole tables](https://www.swoole.co.uk/docs/modules/swoole-table). Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server. However, the data within them will be lost when the server is restarted. @@ -565,5 +576,5 @@ Octane::table('example')->set('uuid', [ return Octane::table('example')->get('uuid'); ``` -> **Warning** +> **Warning** > The column types supported by Swoole tables are: `string`, `int`, and `float`. From 4388fe27f793e62efd1bba38a32b98c44f2cac0e Mon Sep 17 00:00:00 2001 From: Saleh Hashemi <81674631+salehhashemi1992@users.noreply.github.com> Date: Sun, 5 Feb 2023 18:54:12 +0330 Subject: [PATCH 0653/2609] [9.x] Add Fragment Helpers (#8507) * [9.x] Add Fragment Helpers Docs for PR: laravel/framework#44774 * formatting --------- Co-authored-by: Taylor Otwell --- blade.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/blade.md b/blade.md index 62bf8956887..1a76dc1823f 100644 --- a/blade.md +++ b/blade.md @@ -1722,6 +1722,26 @@ Then, when rendering the view that utilizes this template, you may invoke the `f return view('dashboard', ['users' => $users])->fragment('user-list'); ``` +The `fragmentIf` method allows you to conditionally return a fragment of a view based on a given condition. Otherwise, the entire view will be returned: + +```php +return view('dashboard', ['users' => $users]) + ->fragmentIf($request->hasHeader('HX-Request'), 'user-list'); +``` + +The `fragments` and `fragmentsIf` methods allow you to return multiple view fragments in the response. The fragments will be concatenated together: + +```php +view('dashboard', ['users' => $users]) + ->fragments(['user-list', 'comment-list']); + +view('dashboard', ['users' => $users]) + ->fragmentsIf( + $request->hasHeader('HX-Request'), + ['user-list', 'comment-list'] + ); +``` + ## Extending Blade From b0bc7f291b2f089f3447f253558a16cbb90dc66c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 5 Feb 2023 09:27:22 -0600 Subject: [PATCH 0654/2609] wip --- validation.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/validation.md b/validation.md index ff3a9abe76e..8d6753d0152 100644 --- a/validation.md +++ b/validation.md @@ -2027,21 +2027,21 @@ Laravel provides a variety of helpful validation rules; however, you may wish to 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: `__invoke`. 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 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: Date: Mon, 6 Feb 2023 15:59:43 +0100 Subject: [PATCH 0655/2609] Update releases.md (#8511) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index f1c73c9d7eb..950a65ce09f 100644 --- a/releases.md +++ b/releases.md @@ -27,7 +27,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 7 | 7.2 - 8.0 | March 3rd, 2020 | October 6th, 2020 | March 3rd, 2021 | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | -| 10 | 8.1 - 8.2 | February 7th, 2023 | August 6th, 2024 | February 4th, 2025 | +| 10 | 8.1 - 8.2 | Q1 2023 | August 6th, 2024 | February 4th, 2025 |
    From 5f1f11dbe993c7a74d06b6355f1f50157c76a7ac Mon Sep 17 00:00:00 2001 From: Patrick O'Meara Date: Tue, 7 Feb 2023 09:16:34 +1100 Subject: [PATCH 0656/2609] [9.x] Add documentation about the DatabaseTrunates trait (#8510) * Add documentation about the DatabaseTrunates trait As this trait is most beneficial in Dusk tests, the bulk of the information is in the Dusk docs I've used the same header from Database Testing "Resetting The Database After Each Test" * formatting * Update database-testing.md --------- Co-authored-by: Taylor Otwell --- database-testing.md | 2 +- dusk.md | 56 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/database-testing.md b/database-testing.md index 757d5c8ead6..7cc2beb1370 100644 --- a/database-testing.md +++ b/database-testing.md @@ -43,7 +43,7 @@ Before proceeding much further, let's discuss how to reset your database after e The `Illuminate\Foundation\Testing\RefreshDatabase` trait does not migrate your database if your schema is up to date. Instead, it will only execute the test within a database transaction. Therefore, any records added to the database by test cases that do not use this trait may still exist in the database. -If you would like to totally reset the database using migrations, you may use the `Illuminate\Foundation\Testing\DatabaseMigrations` trait instead. However, the `DatabaseMigrations` trait is significantly slower than the `RefreshDatabase` trait. +If you would like to totally reset the database, you may use the `Illuminate\Foundation\Testing\DatabaseMigrations` or `Illuminate\Foundation\Testing\DatabaseTruncation` traits instead. However, both of these options are significantly slower than the `RefreshDatabase` trait. ## Model Factories diff --git a/dusk.md b/dusk.md index 55db79d4d66..ba9dd6c4d6f 100644 --- a/dusk.md +++ b/dusk.md @@ -6,7 +6,7 @@ - [Using Other Browsers](#using-other-browsers) - [Getting Started](#getting-started) - [Generating Tests](#generating-tests) - - [Database Migrations](#migrations) + - [Resetting The Database After Each Test](#resetting-the-database-after-each-test) - [Running Tests](#running-tests) - [Environment Handling](#environment-handling) - [Browser Basics](#browser-basics) @@ -142,10 +142,15 @@ To generate a Dusk test, use the `dusk:make` Artisan command. The generated test php artisan dusk:make LoginTest ``` - -### Database Migrations + +### Resetting The Database After Each Test -Most of the tests you write will interact with pages that retrieve data from your application's database; however, your Dusk tests should never use the `RefreshDatabase `trait. The `RefreshDatabase` trait leverages database transactions which will not be applicable or available across HTTP requests. Instead, use the `DatabaseMigrations` trait, which re-migrates the database for each test: +Most of the tests you write will interact with pages that retrieve data from your application's database; however, your Dusk tests should never use the `RefreshDatabase` trait. The `RefreshDatabase` trait leverages database transactions which will not be applicable or available across HTTP requests. Instead, you have two options: the `DatabaseMigrations` trait and the `DatabaseTruncation` trait. + + +#### Using Database Migrations + +The `DatabaseMigrations` trait will run your database migrations before each test. However, dropping and re-creating your database tables for each test is typically slower than truncating the tables: **Warning** > SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. + +#### Using Database Truncation + +Before using the `DatabaseTruncation` trait, you must install the `doctrine/dbal` package using the Composer package manager: + +```shell +composer require --dev doctrine/dbal +``` + +The `DatabaseTruncation` trait will migrate your database on the first test in order to ensure your database tables have been properly created. However, on subsequent tests, the database's tables will simply be truncated - providing a speed boost over re-running all of your database migrations: + + ### Running Tests From 38d624f359dbf4aca95874f0a264d349e8e01e14 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 6 Feb 2023 16:35:33 -0600 Subject: [PATCH 0657/2609] document assertClosurePushed --- mocking.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mocking.md b/mocking.md index 6fb684a7cda..393ba06ddab 100644 --- a/mocking.md +++ b/mocking.md @@ -603,6 +603,9 @@ After calling the `Queue` facade's `fake` method, you may then assert that the a // Assert a job was not pushed... Queue::assertNotPushed(AnotherJob::class); + + // Assert that a Closure was pushed to the queue... + Queue::assertClosurePushed(); } } From 85a506f1282a4714f0dc37bb2722eb9f0e0272ec Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 6 Feb 2023 16:47:35 -0600 Subject: [PATCH 0658/2609] initial work on release notes --- releases.md | 429 ++++------------------------------------------------ 1 file changed, 31 insertions(+), 398 deletions(-) diff --git a/releases.md b/releases.md index d69ae481310..7c159ee63a4 100644 --- a/releases.md +++ b/releases.md @@ -2,14 +2,14 @@ - [Versioning Scheme](#versioning-scheme) - [Support Policy](#support-policy) -- [Laravel 9](#laravel-9) +- [Laravel 10](#laravel-10) ## Versioning Scheme -Laravel and its other first-party packages follow [Semantic Versioning](https://semver.org). Major framework releases are released every year (~February), while minor and patch releases may be released as often as every week. Minor and patch releases should **never** contain breaking changes. +Laravel and its other first-party packages follow [Semantic Versioning](https://semver.org). Major framework releases are released every year (~Q1), while minor and patch releases may be released as often as every week. Minor and patch releases should **never** contain breaking changes. -When referencing the Laravel framework or its components from your application or package, you should always use a version constraint such as `^9.0`, since major releases of Laravel do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less. +When referencing the Laravel framework or its components from your application or package, you should always use a version constraint such as `^10.0`, since major releases of Laravel do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less. #### Named Arguments @@ -46,427 +46,60 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe (*) Supported PHP versions - -## Laravel 9 + +## Laravel 10 -As you may know, Laravel transitioned to yearly releases with the release of Laravel 8. Previously, major versions were released every 6 months. This transition is intended to ease the maintenance burden on the community and challenge our development team to ship amazing, powerful new features without introducing breaking changes. Therefore, we have shipped a variety of robust features to Laravel 8 without breaking backwards compatibility, such as parallel testing support, improved Breeze starter kits, HTTP client improvements, and even new Eloquent relationship types such as "has one of many". +As you may know, Laravel transitioned to yearly releases with the release of Laravel 8. Previously, major versions were released every 6 months. This transition is intended to ease the maintenance burden on the community and challenge our development team to ship amazing, powerful new features without introducing breaking changes. Therefore, we have shipped a variety of robust features to Laravel 9 without breaking backwards compatibility. Therefore, this commitment to ship great new features during the current release will likely lead to future "major" releases being primarily used for "maintenance" tasks such as upgrading upstream dependencies, which can be seen in these release notes. -Laravel 9 continues the improvements made in Laravel 8.x by introducing support for Symfony 6.0 components, Symfony Mailer, Flysystem 3.0, improved `route:list` output, a Laravel Scout database driver, new Eloquent accessor / mutator syntax, implicit route bindings via Enums, and a variety of other bug fixes and usability improvements. +Laravel 10 continues the improvements made in Laravel 9.x by introducing argument and return types to all application skeleton methods, as well as all stub files used to generate classes throughout the framework. In addition, a new, developer-friendly abstraction layer has been introduced for starting and interacting with external processes. Further, Laravel Pennant has been introduced to provide a wonderful approach to managing your application's "feature flags". -### PHP 8.0 +### PHP 8.1 -Laravel 9.x requires a minimum PHP version of 8.0. +Laravel 10.x requires a minimum PHP version of 8.1. - -### Symfony Mailer + +### Types -_Symfony Mailer support was contributed by [Dries Vints](https://github.com/driesvints)_, [James Brooks](https://github.com/jbrooksuk), and [Julius Kiekbusch](https://github.com/Jubeki). +_Application skeleton and stub type-hints were contributed by [Nuno Maduro](https://github.com/nunomaduro)_. -Previous releases of Laravel utilized the [Swift Mailer](https://swiftmailer.symfony.com/docs/introduction.html) library to send outgoing email. However, that library is no longer maintained and has been succeeded by Symfony Mailer. +On its initial release, Laravel utilized all of the type-hinting features available in PHP at the time. However, many new features have been added to PHP in the subsequent years, including additional primitive type-hints, return types, and union types. -Please review the [upgrade guide](/docs/{{version}}/upgrade#symfony-mailer) to learn more about ensuring your application is compatible with Symfony Mailer. - - -### Flysystem 3.x - -_Flysystem 3.x support was contributed by [Dries Vints](https://github.com/driesvints)_. - -Laravel 9.x upgrades our upstream Flysystem dependency to Flysystem 3.x. Flysystem powers all of filesystem interactions offered by the `Storage` facade. - -Please review the [upgrade guide](/docs/{{version}}/upgrade#flysystem-3) to learn more about ensuring your application is compatible with Flysystem 3.x. - - -### Improved Eloquent Accessors / Mutators - -_Improved Eloquent accessors / mutators was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. - -Laravel 9.x offers a new way to define Eloquent [accessors and mutators](/docs/{{version}}/eloquent-mutators#accessors-and-mutators). In previous releases of Laravel, the only way to define accessors and mutators was by defining prefixed methods on your model like so: +Laravel 10.x thorough updates the application skeleton and all stubs utilized by the framework to introduce argument and return types to all method signatures. In addition, extraneous "doc block" type-hint information has been deleted: ```php -public function getNameAttribute($value) -{ - return strtoupper($value); -} - -public function setNameAttribute($value) -{ - $this->attributes['name'] = $value; -} -``` - -However, in Laravel 9.x you may define an accessor and mutator using a single, non-prefixed method by type-hinting a return type of `Illuminate\Database\Eloquent\Casts\Attribute`: - -```php -use Illuminate\Database\Eloquent\Casts\Attribute; - -public function name(): Attribute -{ - return new Attribute( - get: fn ($value) => strtoupper($value), - set: fn ($value) => $value, - ); -} -``` + new Address( - $attributes['address_line_one'], - $attributes['address_line_two'], - ), - set: fn (Address $value) => [ - 'address_line_one' => $value->lineOne, - 'address_line_two' => $value->lineTwo, - ], - ); -} -``` - - -### Enum Eloquent Attribute Casting - -> **Warning** -> Enum casting is only available for PHP 8.1+. - -_Enum casting was contributed by [Mohamed Said](https://github.com/themsaid)_. - -Eloquent now allows you to cast your attribute values to PHP ["backed" Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: - - use App\Enums\ServerStatus; - /** - * The attributes that should be cast. - * - * @var array + * Display a listing of the resource. */ - protected $casts = [ - 'status' => ServerStatus::class, - ]; - -Once you have defined the cast on your model, the specified attribute will be automatically cast to and from an enum when you interact with the attribute: - - if ($server->status == ServerStatus::Provisioned) { - $server->status = ServerStatus::Ready; - - $server->save(); + public function index(): Response + { + // } - -### Implicit Route Bindings With Enums - -_Implicit Enum bindings was contributed by [Nuno Maduro](https://github.com/nunomaduro)_. - -PHP 8.1 introduces support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). Laravel 9.x introduces the ability to type-hint an Enum on your route definition and Laravel will only invoke the route if that route segment is a valid Enum value in the URI. Otherwise, an HTTP 404 response will be returned automatically. For example, given the following Enum: - -```php -enum Category: string -{ - case Fruits = 'fruits'; - case People = 'people'; -} -``` - -You may define a route that will only be invoked if the `{category}` route segment is `fruits` or `people`. Otherwise, an HTTP 404 response will be returned: - -```php -Route::get('/categories/{category}', function (Category $category) { - return $category->value; -}); -``` - - -### Forced Scoping Of Route Bindings - -_Forced scoped bindings was contributed by [Claudio Dekker](https://github.com/claudiodekker)_. - -In previous releases of Laravel, you may wish to scope the second Eloquent model in a route definition such that it must be a child of the previous Eloquent model. For example, consider this route definition that retrieves a blog post by slug for a specific user: - - use App\Models\Post; - use App\Models\User; - - Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { - return $post; - }); - -When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. However, this behavior was only previously supported by Laravel when a custom key was used for the child route binding. - -However, in Laravel 9.x, you may now instruct Laravel to scope "child" bindings even when a custom key is not provided. To do so, you may invoke the `scopeBindings` method when defining your route: - - use App\Models\Post; - use App\Models\User; - - Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { - return $post; - })->scopeBindings(); - -Or, you may instruct an entire group of route definitions to use scoped bindings: - - Route::scopeBindings()->group(function () { - Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { - return $post; - }); - }); - - -### Controller Route Groups - -_Route group improvements were contributed by [Luke Downing](https://github.com/lukeraymonddowning)_. - -You may now use the `controller` method to define the common controller for all of the routes within the group. Then, when defining the routes, you only need to provide the controller method that they invoke: - - use App\Http\Controllers\OrderController; - - Route::controller(OrderController::class)->group(function () { - Route::get('/orders/{id}', 'show'); - Route::post('/orders', 'store'); - }); - - -### Full Text Indexes / Where Clauses - -_Full text indexes and "where" clauses were contributed by [Taylor Otwell](https://github.com/taylorotwell) and [Dries Vints](https://github.com/driesvints)_. - -When using MySQL or PostgreSQL, the `fullText` method may now be added to column definitions to generate full text indexes: - - $table->text('bio')->fullText(); - -In addition, the `whereFullText` and `orWhereFullText` methods may be used to add full text "where" clauses to a query for columns that have [full text indexes](/docs/{{version}}/migrations#available-index-types). These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a `MATCH AGAINST` clause will be generated for applications utilizing MySQL: - - $users = DB::table('users') - ->whereFullText('bio', 'web developer') - ->get(); - - -### Laravel Scout Database Engine - -_The Laravel Scout database engine was contributed by [Taylor Otwell](https://github.com/taylorotwell) and [Dries Vints](https://github.com/driesvints)_. - -If your application interacts with small to medium sized databases or has a light workload, you may now use Scout's "database" engine instead of a dedicated search service such as Algolia or MeiliSearch. The database engine will use "where like" clauses and full text indexes when filtering results from your existing database to determine the applicable search results for your query. - -To learn more about the Scout database engine, consult the [Scout documentation](/docs/{{version}}/scout). - - -### Rendering Inline Blade Templates - -_Rendering inline Blade templates was contributed by [Jason Beggs](https://github.com/jasonlbeggs). Rendering inline Blade components was contributed by [Toby Zerner](https://github.com/tobyzerner)_. - -Sometimes you may need to transform a raw Blade template string into valid HTML. You may accomplish this using the `render` method provided by the `Blade` facade. The `render` method accepts the Blade template string and an optional array of data to provide to the template: - -```php -use Illuminate\Support\Facades\Blade; - -return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']); -``` - -Similarly, the `renderComponent` method may be used to render a given class component by passing the component instance to the method: - -```php -use App\View\Components\HelloComponent; - -return Blade::renderComponent(new HelloComponent('Julian Bashir')); -``` - - -### Slot Name Shortcut - -_Slot name shortcuts were contributed by [Caleb Porzio](https://github.com/calebporzio)._ - -In previous releases of Laravel, slot names were provided using a `name` attribute on the `x-slot` tag: - -```blade - - - Server Error - - - Whoops! Something went wrong! - -``` - -However, beginning in Laravel 9.x, you may specify the slot's name using a convenient, shorter syntax: - -```xml - - Server Error - -``` - - -### Checked / Selected Blade Directives - -_Checked and selected Blade directives were contributed by [Ash Allen](https://github.com/ash-jc-allen) and [Taylor Otwell](https://github.com/taylorotwell)_. - -For convenience, you may now use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will echo `checked` if the provided condition evaluates to `true`: - -```blade -active)) /> -``` - -Likewise, the `@selected` directive may be used to indicate if a given select option should be "selected": - -```blade - -``` - - -### Bootstrap 5 Pagination Views - -_Bootstrap 5 pagination views were contributed by [Jared Lewis](https://github.com/jrd-lewis)_. - -Laravel now includes pagination views built using [Bootstrap 5](https://getbootstrap.com/). To use these views instead of the default Tailwind views, you may call the paginator's `useBootstrapFive` method within the `boot` method of your `App\Providers\AppServiceProvider` class: - - use Illuminate\Pagination\Paginator; - /** - * Bootstrap any application services. + * Display the specified resource. */ - public function boot(): void + public function show(Flight $flight): Response { - Paginator::useBootstrapFive(); + // } - -### Improved Validation Of Nested Array Data - -_Improved validation of nested array inputs was contributed by [Steve Bauman](https://github.com/stevebauman)_. - -Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may now accomplish this using the `Rule::forEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: - - use App\Rules\HasPermission; - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; - - $validator = Validator::make($request->all(), [ - 'companies.*.id' => Rule::forEach(function ($value, $attribute) { - return [ - Rule::exists(Company::class, 'id'), - new HasPermission('manage-company', $value), - ]; - }), - ]); - - -### Laravel Breeze API & Next.js - -_The Laravel Breeze API scaffolding and Next.js starter kit was contributed by [Taylor Otwell](https://github.com/taylorotwell) and [Miguel Piedrafita](https://twitter.com/m1guelpf)_. - -The [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-next) starter kit has received an "API" scaffolding mode and complimentary [Next.js](https://nextjs.org) [frontend implementation](https://github.com/laravel/breeze-next). This starter kit scaffolding may be used to jump start your Laravel applications that are serving as a backend, Laravel Sanctum authenticated API for a JavaScript frontend. - - -### Improved Ignition Exception Page - -_Ignition is developed by [Spatie](https://spatie.be/)._ + // ... -Ignition, the open source exception debug page created by Spatie, has been redesigned from the ground up. The new, improved Ignition ships with Laravel 9.x and includes light / dark themes, customizable "open in editor" functionality, and more. - -

    - -

    - - -### Improved `route:list` CLI Output - -_Improved `route:list` CLI output was contributed by [Nuno Maduro](https://github.com/nunomaduro)_. - -The `route:list` CLI output has been significantly improved for the Laravel 9.x release, offering a beautiful new experience when exploring your route definitions. - -

    - -

    - - -### Test Coverage Using Artisan `test` Command - -_Test coverage when using the Artisan `test` command was contributed by [Nuno Maduro](https://github.com/nunomaduro)_. - -The Artisan `test` command has received a new `--coverage` option that you may use to explore the amount of code coverage your tests are providing to your application: - -```shell -php artisan test --coverage -``` - -The test coverage results will be displayed directly within the CLI output. - -

    - -

    - -In addition, if you would like to specify a minimum threshold that your test coverage percentage must meet, you may use the `--min` option. The test suite will fail if the given minimum threshold is not met: - -```shell -php artisan test --coverage --min=80.3 +} ``` -

    - -

    - - -### Soketi Echo Server - -_The Soketi Echo server was developed by [Alex Renoki](https://github.com/rennokki)_. - -Although not exclusive to Laravel 9.x, Laravel has recently assisted with the documentation of Soketi, a [Laravel Echo](/docs/{{version}}/broadcasting) compatible Web Socket server written for Node.js. Soketi provides a great, open source alternative to Pusher and Ably for those applications that prefer to manage their own Web Socket server. - -For more information on using Soketi, please consult the [broadcasting documentation](/docs/{{version}}/broadcasting) and [Soketi documentation](https://docs.soketi.app/). - - -### Improved Collections IDE Support - -_Improved collections IDE support was contributed by [Nuno Maduro](https://github.com/nunomaduro)_. - -Laravel 9.x adds improved, "generic" style type definitions to the collections component, improving IDE and static analysis support. IDEs such as [PHPStorm](https://blog.jetbrains.com/phpstorm/2021/12/phpstorm-2021-3-release/#support_for_future_laravel_collections) or static analysis tools such as [PHPStan](https://phpstan.org) will now better understand Laravel collections natively. - -

    - -

    - - -### New Helpers - -Laravel 9.x introduces two new, convenient helper functions that you may use in your own application. - - -#### `str` - -The `str` function returns a new `Illuminate\Support\Stringable` instance for the given string. This function is equivalent to the `Str::of` method: - - $string = str('Taylor')->append(' Otwell'); - - // 'Taylor Otwell' - -If no argument is provided to the `str` function, the function returns an instance of `Illuminate\Support\Str`: - - $snake = str()->snake('LaravelFramework'); - - // 'laravel_framework' - - -#### `to_route` - -The `to_route` function generates a redirect HTTP response for a given named route, providing an expressive way to redirect to named routes from your routes and controllers: - - return to_route('users.show', ['user' => 1]); - -If necessary, you may pass the HTTP status code that should be assigned to the redirect and any additional response headers as the third and fourth arguments to the to_route method: - - return to_route('users.show', ['user' => 1], 302, ['X-Framework' => 'Laravel']); +This change is entirely backwards compatible with existing applications. Therefore, existing applications that do not have these type-hints will continue to function normally. From 35b8cc007275452be28d14e4dd3d382b7be0b9be Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 6 Feb 2023 17:01:32 -0600 Subject: [PATCH 0659/2609] process release notes --- releases.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/releases.md b/releases.md index 7c159ee63a4..8679f3c03e4 100644 --- a/releases.md +++ b/releases.md @@ -103,3 +103,43 @@ class FlightController extends Controller ``` This change is entirely backwards compatible with existing applications. Therefore, existing applications that do not have these type-hints will continue to function normally. + + +### Process Interaction + +Laravel 10.x introduces a beautiful abstraction layer for starting and interacting with external processes via a new `Process` facade: + +```php +use Illuminate\Support\Facades\Process; + +$result = Process::run('ls -la'); + +return $result->output(); +``` + +Processes may even be started in pools, allowing for the convenient execution and management of concurrent processes: + +```php +use Illuminate\Console\Process\Pool; +use Illuminate\Support\Facades\Pool; + +[$first, $second, $third] = Process::concurrently(function (Pool $pool) { + $pool->command('cat first.txt'); + $pool->command('cat second.txt'); + $pool->command('cat third.txt'); +}); + +return $first->output(); +``` + +In addition, processes may be faked for convenient testing: + +```php +Process::fake(); + +// ... + +Process::assertRan('ls -la'); +``` + +For more information on interacting with processes, please consult the [comprehensive process documentation](/docs/{{version}}/processes). From 0ad2da88a66bfd3f0227faec4da845749cfa0bbf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 6 Feb 2023 17:03:22 -0600 Subject: [PATCH 0660/2609] wip --- releases.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/releases.md b/releases.md index 8679f3c03e4..9c8530d5920 100644 --- a/releases.md +++ b/releases.md @@ -107,6 +107,8 @@ This change is entirely backwards compatible with existing applications. Therefo ### Process Interaction +_The process abstraction layer was contributed by [Nuno Maduro](https://github.com/nunomaduro) and [Taylor Otwell](https://github.com/taylorotwell)_. + Laravel 10.x introduces a beautiful abstraction layer for starting and interacting with external processes via a new `Process` facade: ```php From 35e122ece18c06532f6c52f1e368095b70ad23b1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 7 Feb 2023 15:36:27 +1100 Subject: [PATCH 0661/2609] wip --- pennant.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 pennant.md diff --git a/pennant.md b/pennant.md new file mode 100644 index 00000000000..841abc6b525 --- /dev/null +++ b/pennant.md @@ -0,0 +1,32 @@ +# Laravel Pennant + +- [Introduction](#introduction) +- [Database Migrations](#database-migrations) + + +## Introduction + +[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flagging package, without the fluff. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much much more. + + +### Installation + +First, install Pennant into your project using the Composer package manager: + +```shell +composer require laravel/pennant +``` + +## Database Migrations + +Pennant's service provider registers its own database migration directory, so remember to migrate your database after installing the package. The migrations will create a `features` table to power Pennant's database driver: + +```shell +php artisan migrate +``` + +If you need to overwrite the migrations that are included with Pennant, you can publish them using the `vendor:publish` Artisan command: + +```shell +php artisan vendor:publish --tag=pennant-migrations +``` From c78490f4a14ebb2679c764f484bb3b4224aa6ed6 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 7 Feb 2023 16:03:07 +1100 Subject: [PATCH 0662/2609] wip --- pennant.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pennant.md b/pennant.md index 841abc6b525..fa8491b89f3 100644 --- a/pennant.md +++ b/pennant.md @@ -1,7 +1,8 @@ # Laravel Pennant - [Introduction](#introduction) -- [Database Migrations](#database-migrations) +- [Installation](#installation) +- [Configuration](#configuration) ## Introduction @@ -9,7 +10,7 @@ [Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flagging package, without the fluff. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much much more. -### Installation +## Installation First, install Pennant into your project using the Composer package manager: @@ -17,16 +18,19 @@ First, install Pennant into your project using the Composer package manager: composer require laravel/pennant ``` -## Database Migrations - -Pennant's service provider registers its own database migration directory, so remember to migrate your database after installing the package. The migrations will create a `features` table to power Pennant's database driver: +Next, you should publish the Pennant configuration and migration files using the `vendor:publish` Artisan command. The `pennant` configuration file will be placed in your application's `config` directory: ```shell -php artisan migrate +php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider" ``` -If you need to overwrite the migrations that are included with Pennant, you can publish them using the `vendor:publish` Artisan command: +Finally, you should run the database migrations. This will create a `features` table that Pennant uses to power the database driver. ```shell -php artisan vendor:publish --tag=pennant-migrations +php artisan migrate ``` + + +## Configuration + +After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to configure the default driver and the individual driver options. From 2499b41f1cff1c423271d97678b90e8f7ef5d560 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 7 Feb 2023 16:07:54 +1100 Subject: [PATCH 0663/2609] wip --- pennant.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennant.md b/pennant.md index fa8491b89f3..2dd8e5220d0 100644 --- a/pennant.md +++ b/pennant.md @@ -7,7 +7,7 @@ ## Introduction -[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flagging package, without the fluff. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much much more. +[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flagging package, without the fluff. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. ## Installation @@ -18,13 +18,13 @@ First, install Pennant into your project using the Composer package manager: composer require laravel/pennant ``` -Next, you should publish the Pennant configuration and migration files using the `vendor:publish` Artisan command. The `pennant` configuration file will be placed in your application's `config` directory: +Next, you should publish the Pennant configuration and migration files using the `vendor:publish` Artisan command: ```shell php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider" ``` -Finally, you should run the database migrations. This will create a `features` table that Pennant uses to power the database driver. +Finally, you should run the database migrations. This will create a `features` table that Pennant uses to power the database driver: ```shell php artisan migrate @@ -33,4 +33,4 @@ php artisan migrate ## Configuration -After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to configure the default driver and the individual driver options. +After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to select the default driver and configure the individual drivers. From ca8c0eb6849e20be5289ff788519c901d1a0c379 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 7 Feb 2023 16:45:51 +1100 Subject: [PATCH 0664/2609] wip --- pennant.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pennant.md b/pennant.md index 2dd8e5220d0..ce36de583a3 100644 --- a/pennant.md +++ b/pennant.md @@ -3,6 +3,7 @@ - [Introduction](#introduction) - [Installation](#installation) - [Configuration](#configuration) +- [Events](#events) ## Introduction @@ -34,3 +35,42 @@ php artisan migrate ## Configuration After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to select the default driver and configure the individual drivers. + + +## Events + +There are a few events that are fired which may be useful in tracking the usage of active feature flags throughout your application. + +- `Illuminate\Pennant\Events\RetrievingKnownFeature` is dispatched + + +- Defining features + - string based + - class based + - dynamic class based + +- events + - known + - unknown + - dynamic + +- helpers + - global + - blade + +- bulk updates + - purge + +- customising an objects scope +- default scope + +- eager loading + loadMissing + +- attaching feature to objects (this should be improved) + $user->feature('foo')->active(); + +- using the array driver for lottery based features. +- using the array driver for package features + +- array driver for testing +- custom drivers From 7c45d2504e6e7e6e714aac26fda2d21cb43dee14 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 01:56:36 +1100 Subject: [PATCH 0665/2609] Update framework extensions (#8513) --- deployment.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deployment.md b/deployment.md index 965ebbe48d3..19688383cd6 100644 --- a/deployment.md +++ b/deployment.md @@ -25,16 +25,17 @@ The Laravel framework has a few system requirements. You should ensure that your
    - PHP >= 8.0 -- BCMath PHP Extension - Ctype PHP Extension - cURL PHP Extension - DOM PHP Extension - Fileinfo PHP Extension -- JSON PHP Extension +- Filter PHP Extension +- Hash PHP Extension - Mbstring PHP Extension - OpenSSL PHP Extension - PCRE PHP Extension - PDO PHP Extension +- Session PHP Extension - Tokenizer PHP Extension - XML PHP Extension From cba5c3e675f60520847889e5927b5fd8783057f5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 09:35:18 -0600 Subject: [PATCH 0666/2609] wip --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index b15c90be773..3dabeffbbb9 100644 --- a/queues.md +++ b/queues.md @@ -1583,7 +1583,7 @@ 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 many 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. +When jobs are available on the queue, the worker will keep processing jobs with no delay in between jobs. However, the `sleep` option determines how many seconds the worker will "sleep" if there are no jobs available. Of course, while sleeping, the worker will not process any new jobs: ```shell php artisan queue:work --sleep=3 From db8e0af96a5319214085431f71375cb5e161387e Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 02:36:17 +1100 Subject: [PATCH 0667/2609] Removes dependency on bcmath (#8467) --- validation.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/validation.md b/validation.md index 08a157352e7..fc285052548 100644 --- a/validation.md +++ b/validation.md @@ -1431,9 +1431,6 @@ The integer under validation must have a minimum length of _value_. The field under validation must be a multiple of _value_. -> **Warning** -> The [`bcmath` PHP extension](https://www.php.net/manual/en/book.bc.php) is required in order to use the `multiple_of` rule. - #### missing From 21abe5611ba2575319965cc165e03338c6b68329 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 09:58:22 -0600 Subject: [PATCH 0668/2609] query count --- database-testing.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/database-testing.md b/database-testing.md index 7cc2beb1370..f32f05d94e8 100644 --- a/database-testing.md +++ b/database-testing.md @@ -202,3 +202,12 @@ Assert that a given model does not exist in the database: $user->delete(); $this->assertModelMissing($user); + + +#### expectsDatabaseQueryCount + +The `expectsDatabaseQueryCount` method may be invoked at the beginning of your test to specify the total number of database queries that you expect to be run during the test. If the actual number of executed queries does not exactly match this expectation, the test will fail: + + $this->expectsDatabaseQueryCount(5); + + // Test... From 339f4bf00b07d108f3f04198d9c84b0463b8ad9c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 10:04:27 -0600 Subject: [PATCH 0669/2609] document uri templates --- http-client.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/http-client.md b/http-client.md index 9bfb90ce0e4..96ef8118283 100644 --- a/http-client.md +++ b/http-client.md @@ -58,6 +58,20 @@ The `Illuminate\Http\Client\Response` object also implements the PHP `ArrayAcces return Http::get('http://example.com/users/1')['name']; + +#### URI Templates + +The HTTP client also allows you to construct request URLs using the [URI template specification](https://www.rfc-editor.org/rfc/rfc6570). To define the URL parameters that can be expanded by your URI template, you may use the `withUrlParameters` method: + +```php +Http::withUrlParameters([ + 'endpoint' => 'https://laravel.com/', + 'page' => 'docs', + 'version' => '9.x', + 'topic' => 'validation', +])->get('{+endpoint}/{page}/{version}/{topic}'); +``` + #### Dumping Requests From c96997954b36e3f2a1747c8a5e0e8661e1db7da8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 10:07:00 -0600 Subject: [PATCH 0670/2609] add sail:add docs --- sail.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sail.md b/sail.md index 8114ae91998..3b6ae08a697 100644 --- a/sail.md +++ b/sail.md @@ -62,6 +62,15 @@ Finally, you may start Sail. To continue learning how to use Sail, please contin ./vendor/bin/sail up ``` + +#### Adding Additional Services + +If you would like to add an additional service to your existing Sail installation, you may run the `sail:add` Artisan command: + +```shell +php artisan sail:add +``` + #### Using Devcontainers From fbe5162c1296a630aee631af3ffa80a500b9b0fa Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 10:10:46 -0600 Subject: [PATCH 0671/2609] style directive --- blade.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/blade.md b/blade.md index 1a76dc1823f..e3f7754f8db 100644 --- a/blade.md +++ b/blade.md @@ -426,7 +426,7 @@ The `$loop` variable also contains a variety of other useful properties: | `$loop->parent` | When in a nested loop, the parent's loop variable. | -### Conditional Classes +### Conditional Classes & Styles The `@class` directive conditionally compiles a CSS class string. The directive accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list: @@ -446,6 +446,21 @@ The `@class` directive conditionally compiles a CSS class string. The directive ``` +Likewise, the `@style` directive may be used to conditionally add inline CSS styles to an HTML element: + +```blade +@php + $isActive = true; +@endphp + + $isActive, +])> + + +``` + ### Additional Attributes From 2d993e24d9158bce89abc58bfe8daba43a30a6e5 Mon Sep 17 00:00:00 2001 From: Sean Poynter-Smith Date: Tue, 7 Feb 2023 16:35:06 +0000 Subject: [PATCH 0672/2609] Fix missing anchor (#8517) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 3f5f91cc98b..19d2bece4d7 100644 --- a/upgrade.md +++ b/upgrade.md @@ -69,6 +69,7 @@ You should update the following dependencies in your application's `composer.jso
    + #### Minimum Stability You should update the `minimum-stability` setting in your application's `composer.json` file to `stable`: From b05a2792f7b9be6499df4b1387bf8b02cf018b40 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 10:47:21 -0600 Subject: [PATCH 0673/2609] translation stringables --- localization.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/localization.md b/localization.md index 784b53dc382..ac3afd01ccf 100644 --- a/localization.md +++ b/localization.md @@ -169,6 +169,28 @@ If your placeholder contains all capital letters, or only has its first letter c 'welcome' => 'Welcome, :NAME', // Welcome, DAYLE 'goodbye' => 'Goodbye, :Name', // Goodbye, Dayle + +#### Object Replacement Formatting + +If you attempt to provide an object as a translation placeholder, 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, Laravel allows you to register a custom formatting handler for that particular type of object. To accomplish this, you should invoke the translator's `stringable` method. The `stringable` method accepts a closure, which should type-hint the type of object that it is responsible for formatting. Typically, the `stringable` method should be invoked within the `boot` method of your application's `AppServiceProvider` class: + + use Illuminate\Support\Facades\Lang; + use Money\Money; + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Lang::stringable(function (Money $money) { + return $money->formatTo('en_GB'); + }); + } + ### Pluralization From cf2cda25e11dd49647ec0639f9c4a20746692da7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 11:22:58 -0600 Subject: [PATCH 0674/2609] improve bus / queue docs --- mocking.md | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/mocking.md b/mocking.md index 393ba06ddab..981f7ab1516 100644 --- a/mocking.md +++ b/mocking.md @@ -6,14 +6,13 @@ - [Facade Spies](#facade-spies) - [Bus Fake](#bus-fake) - [Job Chains](#bus-job-chains) - - [Job Batches](#job-batches) + - [Job Batches](#bus-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) @@ -245,7 +244,11 @@ As you can see in the example above, the array of chained jobs may be an array o new UpdateInventory, ]); - +You may use the `assertDispatchedWithoutChain` method to assert that a job was pushed without a chain of jobs: + + Bus::assertDispatchedWithoutChain(ShipOrder::class); + + ### Job Batches The `Bus` facade's `assertBatched` method may be used to assert that a [batch of jobs](/docs/{{version}}/queues#job-batching) 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: @@ -629,31 +632,8 @@ If you only need to fake specific jobs while allowing your other jobs to execute Queue::assertPushed(ShipOrder::class, 2); } - -### 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); +> **Note** +> To test jobs that are batched or chained via the `Bus` facade, consult the documentation on utilizing the `Bus` facade's [batch assertions](#bus-job-batches) and [chain assertions](#bus-job-chains) ## Storage Fake From 3621cc166d8b855882908bf4eeafdd7e29bf471b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 11:24:50 -0600 Subject: [PATCH 0675/2609] wip --- mocking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocking.md b/mocking.md index 981f7ab1516..dee6d1ded18 100644 --- a/mocking.md +++ b/mocking.md @@ -571,7 +571,7 @@ By passing a closure as the second argument to the `assertSentOnDemand` method, ## Queue Fake -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. +You may use the `Queue` facade's `fake` method to prevent queued jobs from being pushed to the [queue](/docs/{{version}}/queues). 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: From 5be022afedc190d8a332336dcfce093fb942f7f2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 12:19:08 -0600 Subject: [PATCH 0676/2609] document has many through improvements --- eloquent-relationships.md | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 95d66119eac..339910cbc95 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -411,6 +411,16 @@ Now that we have examined the table structure for the relationship, let's define The first argument passed to the `hasOneThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. +Or, if the relevant relationships have already been defined on all of the models involved in the relationship, you may fluently define a "has-one-through" relationship by invoking the `through` method and supplying the names of those relationships. For example, if the `Mechanic` model has a `cars` relationship and the `Car` model has an `owner` relationship, you may define a "has-one-through" relationship connecting the mechanic and the owner like so: + +```php +// String based syntax... +return $this->through('cars')->has('owner'); + +// Dynamic syntax... +return $this->throughCars()->hasOwner(); +``` + #### Key Conventions @@ -434,6 +444,16 @@ Typical Eloquent foreign key conventions will be used when performing the relati } } +Or, as discussed earlier, if the relevant relationships have already been defined on all of the models involved in the relationship, you may fluently define a "has-one-through" relationship by invoking the `through` method and supplying the names of those relationships. This approach offers the advantage of reusing the key conventions already defined on the existing relationships: + +```php +// String based syntax... +return $this->through('cars')->has('owner'); + +// Dynamic syntax... +return $this->throughCars()->hasOwner(); +``` + ### Has Many Through @@ -474,6 +494,16 @@ Now that we have examined the table structure for the relationship, let's define The first argument passed to the `hasManyThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. +Or, if the relevant relationships have already been defined on all of the models involved in the relationship, you may fluently define a "has-many-through" relationship by invoking the `through` method and supplying the names of those relationships. For example, if the `Project` model has a `environments` relationship and the `Environment` model has a `deployments` relationship, you may define a "has-many-through" relationship connecting the project and the deployments like so: + +```php +// String based syntax... +return $this->through('environments')->has('deployments'); + +// Dynamic syntax... +return $this->throughEnvironments()->hasDeployments(); +``` + Though the `Deployment` model's table does not contain a `project_id` column, the `hasManyThrough` relation provides access to a project's deployments via `$project->deployments`. To retrieve these models, Eloquent inspects the `project_id` column on the intermediate `Environment` model's table. After finding the relevant environment IDs, they are used to query the `Deployment` model's table. @@ -496,6 +526,16 @@ Typical Eloquent foreign key conventions will be used when performing the relati } } +Or, as discussed earlier, if the relevant relationships have already been defined on all of the models involved in the relationship, you may fluently define a "has-many-through" relationship by invoking the `through` method and supplying the names of those relationships. This approach offers the advantage of reusing the key conventions already defined on the existing relationships: + +```php +// String based syntax... +return $this->through('environments')->has('deployments'); + +// Dynamic syntax... +return $this->throughEnvironments()->hasDeployments(); +``` + ## Many To Many Relationships From 2122cb95f4c837f7ab794a7dd093dec773dbf550 Mon Sep 17 00:00:00 2001 From: Ben Miller <67608755+modernben@users.noreply.github.com> Date: Tue, 7 Feb 2023 10:56:40 -0800 Subject: [PATCH 0677/2609] Update Ably url on Broadcasting (#8520) Ably has changed its URL to https://ably.com --- broadcasting.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 7a5c7d5292a..695609f9161 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -52,7 +52,7 @@ The core concepts behind broadcasting are simple: clients connect to named chann #### 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) and [soketi](https://docs.soketi.app/) provide additional broadcasting drivers that do not require commercial broadcasting providers. +By default, Laravel includes two server-side broadcasting drivers for you to choose from: [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.com). However, community driven packages such as [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) and [soketi](https://docs.soketi.app/) provide additional broadcasting drivers that do not require commercial broadcasting providers. > **Note** > Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). @@ -115,7 +115,7 @@ The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) and [ ### Ably -If you plan to broadcast your events using [Ably](https://ably.io), you should install the Ably PHP SDK using the Composer package manager: +If you plan to broadcast your events using [Ably](https://ably.com), you should install the Ably PHP SDK using the Composer package manager: ```shell composer require ably/ably-php @@ -229,7 +229,7 @@ window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_ABLY_PUBLIC_KEY, - wsHost: 'realtime-pusher.ably.io', + wsHost: 'realtime-pusher.ably.com', wsPort: 443, disableStats: true, encrypted: true, @@ -250,7 +250,7 @@ npm run dev ## 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 [Ably](https://ably.io) drivers. The events may be easily consumed on the client-side using the [Laravel Echo](#client-side-installation) 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.com) drivers. The events may be easily consumed on the client-side using the [Laravel Echo](#client-side-installation) 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. From 77e4f6d2384bc06b07e58ff298c25552b6e82a24 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 7 Feb 2023 19:57:26 +0000 Subject: [PATCH 0678/2609] [10.x] Adds artisan test `--profile` flag to release notes (#8519) * Adds `--profile` flag to release notes * Update releases.md --------- Co-authored-by: Taylor Otwell --- releases.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/releases.md b/releases.md index 9c8530d5920..7d77f60ff93 100644 --- a/releases.md +++ b/releases.md @@ -145,3 +145,20 @@ Process::assertRan('ls -la'); ``` For more information on interacting with processes, please consult the [comprehensive process documentation](/docs/{{version}}/processes). + + +### Test Profiling + +_Test profiling was contributed by [Nuno Maduro](https://github.com/nunomaduro)_. + +The Artisan `test` command has received a new `--profile` option that allows you to easily identify the slowest tests in your application: + +```shell +php artisan test --profile +``` + +For convenience, the slowest tests will be displayed directly within the CLI output: + +

    + +

    From ce2596924461a1b64201133e06ed12a8e7e49480 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 7 Feb 2023 19:58:28 +0000 Subject: [PATCH 0679/2609] [10.x] PHPUnit 10 migration guide (#8518) * Adds PHPUnit 10 migration guide * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/upgrade.md b/upgrade.md index 19d2bece4d7..c6c16296e4d 100644 --- a/upgrade.md +++ b/upgrade.md @@ -69,6 +69,17 @@ You should update the following dependencies in your application's `composer.jso
    +Optionally, if you wish to use [PHPUnit 10](https://phpunit.de/announcements/phpunit-10.html), you should delete the `processUncoveredFiles` attribute from the `` section of your application's `phpunit.xml` configuration file. Then, update the following dependencies in your application's `composer.json` file: + +
    + +- `nunomaduro/collision` to `^7.0` +- `phpunit/phpunit` to `^10.0` + +
    + +Finally, examine any other third-party packages consumed by your application and verify you are using the proper version for Laravel 10 support. + #### Minimum Stability From c78e4f730bb8973462deb7952fabe13efc2bbf23 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 14:15:54 -0600 Subject: [PATCH 0680/2609] augment release notes --- releases.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/releases.md b/releases.md index 7d77f60ff93..6f0e31688ca 100644 --- a/releases.md +++ b/releases.md @@ -104,6 +104,44 @@ class FlightController extends Controller This change is entirely backwards compatible with existing applications. Therefore, existing applications that do not have these type-hints will continue to function normally. + +### Laravel Pennant + +_Laravel Pennant was developed by [Tim MacDonald](https://github.com/timacdonald)_. + +A new first-party package, Laravel Pennant, has been released. Laravel Pennant offers a light-weight, streamlined approach to managing your application's feature flags. Out of the box, Pennant includes an in-memory `array` driver and a `database` driver for persistent feature storage. + +Features can be easily defined via the `Feature::define` method: + +```php +use Laravel\Pennant\Feature; +use Illuminate\Support\Lottery; + +Feature::define('new-onboarding-flow', function () { + return Lottery::odds(1, 10); +}); +``` + +Once a feature has been defined, you may easily determine if the current user has access to the given feature: + +```php +if (Feature::active('new-onboarding-flow')) { + // ... +} +``` + +Of course, for convenience, Blade directives are also available: + +```blade +@feature('new-onboarding-flow') +
    + +
    +@endfeature +``` + +Pennant offers a variety of more advanced features and APIs. For more information, please consult the [comprehensive Pennant documentation](/docs/{{version}}/pennant). + ### Process Interaction @@ -162,3 +200,23 @@ For convenience, the slowest tests will be displayed directly within the CLI out

    + + +### Pest Scaffolding + +New Laravel projects may now be created with Pest test scaffolding by default. To opt-in to this feature, provide the `--pest` flag when creating a new application via the Laravel installer: + +```shell +laravel new example-application --pest +``` + + +### Generator CLI Prompts + +_Generator CLI prompts were contributed by [Jess Archer](https://github.com/jessarcher)_. + +To improve the framework's developer experience, all of Laravel's built-in `make` commands no longer require any input. If the commands are invoked without input, you will be prompted for the required arguments: + +```shell +php artisan make:controller +``` From 46a6ef03c45e665547ca55a1238aebdda72bb043 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 14:42:05 -0600 Subject: [PATCH 0681/2609] initial work on process docs --- documentation.md | 1 + processes.md | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 processes.md diff --git a/documentation.md b/documentation.md index 54cdd558ee3..011105b19ba 100644 --- a/documentation.md +++ b/documentation.md @@ -43,6 +43,7 @@ - [Mail](/docs/{{version}}/mail) - [Notifications](/docs/{{version}}/notifications) - [Package Development](/docs/{{version}}/packages) + - [Processes](/docs/{{version}}/processes) - [Queues](/docs/{{version}}/queues) - [Rate Limiting](/docs/{{version}}/rate-limiting) - [Task Scheduling](/docs/{{version}}/scheduling) diff --git a/processes.md b/processes.md new file mode 100644 index 00000000000..f1e5a909096 --- /dev/null +++ b/processes.md @@ -0,0 +1,119 @@ +# Processes + +- [Introduction](#introduction) +- [Invoking Processes](#invoking-processes) + - [Process Options](#process-options) + - [Process Output](#process-output) +- [Concurrent Processes](#concurrent-processes) +- [Testing](#testing) + - [Faking Processes](#faking-responses) + - [Inspecting Results](#inspecting-requests) + - [Preventing Stray Processes](#preventing-stray-processes) + + +## Introduction + +Laravel provides an expressive, minimal API around the [Symfony Process component](https://symfony.com/doc/current/components/process.html), allowing you to conveniently invoke external processes from your Laravel application. Laravel's process features are focused on the most common use cases and a wonderful developer experience. + + +## Invoking Processes + +To invoke a process, you may use the `run` and `start` methods offered by the `Process` facade. The `run` method will invoke a process and wait for the process to finish executing, while the `start` method is used for asynchronous process execution. We'll examine both approaches within this documentation. First, let's examine how to invoke a basic process and inspect its result: + +```php +use Illuminate\Support\Facades\Process; + +$result = Process::run('ls -la'); + +return $result->output(); +``` + +Of course, the `Illuminate\Console\Process\ProcessResult` instance returned by the `run` method offers a variety of helpful methods that may be used to inspect the process result: + +```php +$result = Process::run('ls -la'); + +$result->successful(): +$result->failed(); +$result->exitCode(); +$result->output(); +$result->errorOutput(); +``` + + +#### Throwing Exceptions + +If you have a process result and would like to throw an instance of `Illuminate\Console\Process\Exceptions\ProcessFailedException` if the exit code indicates that the process failed, you may use the `throw` and `throwIf` methods: + +```php +$result = Process::run('ls -la')->throw(); + +$result = Process::run('ls -la')->throwIf($condition); +``` + + +### Process Options + +Of course, you may need to customize the behavior of a process before invoking it. Thankfully, Laravel allows you to tweak a variety of process features, such as the working directory, timeout, and environment variables. + + +#### Working Directory Path + +For instance, you may use the `path` method to specify the working directory of the process. If this method is not invoked, the process will inherit the working directory of the currently executing PHP script: + +```php +$result = Process::path(base_path())->run('ls -la'); +``` + + +#### Timeouts + +By default, processes will throw an instance of `Illuminate\Console\Process\Exceptions\ProcessTimedOutException` after executing for more than 60 seconds. However, you can customize this behavior via the `timeout` method: + +```php +$result = Process::timeout(120)->run('bash import.sh'); +``` + +Or, if you would like to disable the process timeout entirely, you may invoke the `forever` method: + +```php +$result = Process::forever()->run('bash import.sh'); +``` + +The `idleTimeout` method may be used to specify the maximum number of seconds the process may run without returning any output: + +```php +$result = Process::timeout(60)->idleTimeout(30)->run('bash import.sh'); +``` + + +#### Environment Variables + +Environment variables may be provided to the process via the `env` method. The invoked process will also inherit all of the environment variables defined by your system: + +```php +$result = Process::forever() + ->env(['IMPORT_PATH' => base_path()]) + ->run('bash import.sh'); +``` + +If you wish to remove an inherited environment variable from the invoked process, you may pass that environment variable with a value of `false`: + +```php +$result = Process::forever() + ->env(['LOAD_PATH' => false]) + ->run('bash import.sh'); +``` + + +#### TTY Mode + +The `tty` method may be used to enable TTY mode for your process. TTY mode connects the input and output of the process to the input and output of your program, allowing your process to open an editor like Vim or Nano as a process: + +```php +Process::forever()->tty()->run('vim'); +``` + + +### Process Output + From 6abd3086798de1ff264370a71a79cf02398898f7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 15:35:16 -0600 Subject: [PATCH 0682/2609] continuing process doc work --- processes.md | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/processes.md b/processes.md index f1e5a909096..e7d42437ae2 100644 --- a/processes.md +++ b/processes.md @@ -4,6 +4,9 @@ - [Invoking Processes](#invoking-processes) - [Process Options](#process-options) - [Process Output](#process-output) +- [Asynchronous Processes](#asynchronous-processes) + - [Process IDs & Signals](#process-ids-and-signals) + - [Asynchronous Process Output](#asynchronous-process-output) - [Concurrent Processes](#concurrent-processes) - [Testing](#testing) - [Faking Processes](#faking-responses) @@ -117,3 +120,129 @@ Process::forever()->tty()->run('vim'); ### Process Output +As previously discussed, process output may be accessed using the `output` (stdout) and `errorOutput` (stderr) methods on a process result: + +```php +use Illuminate\Support\Facades\Process; + +$result = Process::run('ls -la'); + +echo $result->output(); +echo $result->errorOutput(); +``` + +However, output may also be gathered in real-time by passing a closure as the second argument to the `run` method. The closure will receive two arguments: the "type" of output (`stdout` or `stderr`) and the output string itself: + +```php +$result = Process::run('ls -la', function (string $type, string $output) { + echo $output; +}); +``` + +Laravel also offers the `seeInOutput` and `seeInErrorOutput` methods, which provide a convenient way to determine if a given string was contained in the process' output: + +```php +if (Process::run('ls -la')->seeInOutput('laravel')) { + // ... +} +``` + + +#### Disabling Process Output + +If your process is writing a significant amount of output that you are not interested in, you can conserve memory by disabling output retrieval entirely. To accomplish this, invoke the `withoutOutput` method while building the process: + +```php +use Illuminate\Support\Facades\Process; + +$result = Process::withoutOutput()->run('bash import.sh'); +``` + + +## Asynchronous Processes + +While the `run` method invokes processes synchronously, the `start` method may be used to invoke a process asynchronously. This allows your application to continue performing other tasks while the process runs in the background. Once the process has been invoked, you may utilize the `running` method to determine if the process is still running: + +```php +$process = Process::timeout(120)->start('bash import.sh'); + +while ($process->running()) { + // ... +} +``` + +Instead of continually checking the `running` method to determine if a process is still running, you may invoke the `wait` method to wait until the process is finished executing and retrieve the process result instance: + +```php +$process = Process::timeout(120)->start('bash import.sh'); + +// ... + +$result = $process->wait(); +``` + + +### Process IDs & Signals + +The `pid` method may be used to retrieve the operating system assigned process ID of the running process: + +```php +$process = Process::start('bash import.sh'); + +return $process->pid(); +``` + +You may use the `signal` method to send a "signal" to the running process. A list of predefined signal constants can be found within the [PHP documentation](https://www.php.net/manual/en/pcntl.constants.php): + +```php +$process->signal(SIGUSR2); +``` + + +### Asynchronous Process Output + +While an asynchronous process is running, you may of course access its entire current output using the `output` and `errorOutput` methods; however, you may utilize the `latestOutput` and `latestErrorOutput` to access the output from the process that has occurred since the output was last retrieved: + +```php +$process = Process::timeout(120)->start('bash import.sh'); + +while ($process->running()) { + echo $process->latestOutput(); + echo $process->latestErrorOutput(); + + sleep(1); +} +``` + +Like the `run` method, output may also be gathered in real-time from asynchronous processes by passing a closure as the second argument to the `start` method. The closure will receive two arguments: the "type" of output (`stdout` or `stderr`) and the output string itself: + +```php +Process::start('bash import.sh', function (string $type, string $output) { + echo $output; +}); +``` + + +## Concurrent Processes + +Laravel also makes it a breeze to manage a pool of concurrent processes, allowing you to easily execute many processes simultaneously. To get started, invoke the `pool` method, which accepts a closure that will receive an instance of `Illuminate\Console\Process\Pool`. + +Within this closure, you may define the processes that belong to the pool. Once a process pool is started via the `start` method, you may access a [collection](/docs/{{version}}/collections) of the running processes via the `running` method. Then, you may wait for all of the pool processes to finish executing and resolve their results via the `wait` method. The `wait` method returns an array accessible object that allows you to access the process result instance of each process in the pool by its key: + +```php +$pool = Process::pool(function (Pool $pool) { + $pool->path(base_path())->command('bash import-1.sh'); + $pool->path(base_path())->command('bash import-2.sh'); + $pool->path(base_path())->command('bash import-3.sh'); +})->start(function ($type, $output, $key) { + // ... +}); + +while ($pool->running()->isNotEmpty()) { + // ... +} + +$results = $pool->wait(); + +echo $results[0]->output(); +``` From 83865bf2c53b260040815395afd24b5994a2f41b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 17:46:05 -0600 Subject: [PATCH 0683/2609] process documentation --- mocking.md | 22 +--- processes.md | 324 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 313 insertions(+), 33 deletions(-) diff --git a/mocking.md b/mocking.md index dee6d1ded18..f5f412879da 100644 --- a/mocking.md +++ b/mocking.md @@ -12,6 +12,7 @@ - [HTTP Fake](#http-fake) - [Mail Fake](#mail-fake) - [Notification Fake](#notification-fake) +- [Process Fake](#process-fake) - [Queue Fake](#queue-fake) - [Storage Fake](#storage-fake) - [Interacting With Time](#interacting-with-time) @@ -102,8 +103,6 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, namespace Tests\Feature; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\Facades\Cache; use Tests\TestCase; @@ -155,8 +154,6 @@ You may use the `Bus` facade's `fake` method to prevent jobs from being dispatch namespace Tests\Feature; use App\Jobs\ShipOrder; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\Facades\Bus; use Tests\TestCase; @@ -284,8 +281,6 @@ When testing code that dispatches events, you may wish to instruct Laravel to no use App\Events\OrderFailedToShip; use App\Events\OrderShipped; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\Facades\Event; use Tests\TestCase; @@ -369,9 +364,7 @@ If you only want to fake event listeners for a portion of your test, you may use use App\Events\OrderCreated; use App\Models\Order; - use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Event; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Tests\TestCase; class ExampleTest extends TestCase @@ -411,8 +404,6 @@ After calling the `Mail` facade's `fake` method, you may then assert that [maila namespace Tests\Feature; use App\Mail\OrderShipped; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\Facades\Mail; use Tests\TestCase; @@ -512,8 +503,6 @@ After calling the `Notification` facade's `fake` method, you may then assert tha namespace Tests\Feature; use App\Notifications\OrderShipped; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\Facades\Notification; use Tests\TestCase; @@ -568,6 +557,11 @@ By passing a closure as the second argument to the `assertSentOnDemand` method, } ); + +## Process Fake + +The `Process` facade's `fake` method allows you to instruct the Laravel process services to return stubbed / dummy results when processes are invoked. For more information on faking processes, please consult the [process testing documentation](/docs/{{version}}/processes#testing). + ## Queue Fake @@ -582,8 +576,6 @@ After calling the `Queue` facade's `fake` method, you may then assert that the a use App\Jobs\AnotherJob; use App\Jobs\FinalJob; use App\Jobs\ShipOrder; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\Facades\Queue; use Tests\TestCase; @@ -644,8 +636,6 @@ The `Storage` facade's `fake` method allows you to easily generate a fake disk t namespace Tests\Feature; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; use Tests\TestCase; diff --git a/processes.md b/processes.md index e7d42437ae2..7c444a3819e 100644 --- a/processes.md +++ b/processes.md @@ -8,9 +8,14 @@ - [Process IDs & Signals](#process-ids-and-signals) - [Asynchronous Process Output](#asynchronous-process-output) - [Concurrent Processes](#concurrent-processes) + - [Naming Pool Processes](#naming-pool-processes) + - [Pool Process IDs & Signals](#pool-process-ids-and-signals) - [Testing](#testing) - - [Faking Processes](#faking-responses) - - [Inspecting Results](#inspecting-requests) + - [Faking Processes](#faking-processes) + - [Faking Specific Processes](#faking-specific-processes) + - [Faking Process Sequences](#faking-process-sequences) + - [Faking Asynchronous Process Lifecycles](#faking-asynchronous-process-lifecycles) + - [Available Assertions](#available-assertions) - [Preventing Stray Processes](#preventing-stray-processes) @@ -21,7 +26,7 @@ Laravel provides an expressive, minimal API around the [Symfony Process componen ## Invoking Processes -To invoke a process, you may use the `run` and `start` methods offered by the `Process` facade. The `run` method will invoke a process and wait for the process to finish executing, while the `start` method is used for asynchronous process execution. We'll examine both approaches within this documentation. First, let's examine how to invoke a basic process and inspect its result: +To invoke a process, you may use the `run` and `start` methods offered by the `Process` facade. The `run` method will invoke a process and wait for the process to finish executing, while the `start` method is used for asynchronous process execution. We'll examine both approaches within this documentation. First, let's examine how to invoke a basic, synchronous process and inspect its result: ```php use Illuminate\Support\Facades\Process; @@ -31,7 +36,7 @@ $result = Process::run('ls -la'); return $result->output(); ``` -Of course, the `Illuminate\Console\Process\ProcessResult` instance returned by the `run` method offers a variety of helpful methods that may be used to inspect the process result: +Of course, the `Illuminate\Contracts\Console\Process\ProcessResult` instance returned by the `run` method offers a variety of helpful methods that may be used to inspect the process result: ```php $result = Process::run('ls -la'); @@ -46,7 +51,7 @@ $result->errorOutput(); #### Throwing Exceptions -If you have a process result and would like to throw an instance of `Illuminate\Console\Process\Exceptions\ProcessFailedException` if the exit code indicates that the process failed, you may use the `throw` and `throwIf` methods: +If you have a process result and would like to throw an instance of `Illuminate\Console\Process\Exceptions\ProcessFailedException` if the exit code is greater than zero (thus indicating failure), you may use the `throw` and `throwIf` methods. If the process did not fail, the process result instance will be returned: ```php $result = Process::run('ls -la')->throw(); @@ -62,10 +67,10 @@ Of course, you may need to customize the behavior of a process before invoking i #### Working Directory Path -For instance, you may use the `path` method to specify the working directory of the process. If this method is not invoked, the process will inherit the working directory of the currently executing PHP script: +You may use the `path` method to specify the working directory of the process. If this method is not invoked, the process will inherit the working directory of the currently executing PHP script: ```php -$result = Process::path(base_path())->run('ls -la'); +$result = Process::path(__DIR__)->run('ls -la'); ``` @@ -96,11 +101,11 @@ Environment variables may be provided to the process via the `env` method. The i ```php $result = Process::forever() - ->env(['IMPORT_PATH' => base_path()]) + ->env(['IMPORT_PATH' => __DIR__]) ->run('bash import.sh'); ``` -If you wish to remove an inherited environment variable from the invoked process, you may pass that environment variable with a value of `false`: +If you wish to remove an inherited environment variable from the invoked process, you may provide that environment variable with a value of `false`: ```php $result = Process::forever() @@ -169,9 +174,11 @@ $process = Process::timeout(120)->start('bash import.sh'); while ($process->running()) { // ... } + +$result = $process->wait(); ``` -Instead of continually checking the `running` method to determine if a process is still running, you may invoke the `wait` method to wait until the process is finished executing and retrieve the process result instance: +As you may have noticed, you may invoke the `wait` method to wait until the process is finished executing and retrieve the process result instance: ```php $process = Process::timeout(120)->start('bash import.sh'); @@ -201,7 +208,7 @@ $process->signal(SIGUSR2); ### Asynchronous Process Output -While an asynchronous process is running, you may of course access its entire current output using the `output` and `errorOutput` methods; however, you may utilize the `latestOutput` and `latestErrorOutput` to access the output from the process that has occurred since the output was last retrieved: +While an asynchronous process is running, you may access its entire current output using the `output` and `errorOutput` methods; however, you may utilize the `latestOutput` and `latestErrorOutput` to access the output from the process that has occurred since the output was last retrieved: ```php $process = Process::timeout(120)->start('bash import.sh'); @@ -217,23 +224,28 @@ while ($process->running()) { Like the `run` method, output may also be gathered in real-time from asynchronous processes by passing a closure as the second argument to the `start` method. The closure will receive two arguments: the "type" of output (`stdout` or `stderr`) and the output string itself: ```php -Process::start('bash import.sh', function (string $type, string $output) { +$process = Process::start('bash import.sh', function ($type, $output) { echo $output; }); + +$result = $process->wait(); ``` ## Concurrent Processes -Laravel also makes it a breeze to manage a pool of concurrent processes, allowing you to easily execute many processes simultaneously. To get started, invoke the `pool` method, which accepts a closure that will receive an instance of `Illuminate\Console\Process\Pool`. +Laravel also makes it a breeze to manage a pool of concurrent, asynchronous processes, allowing you to easily execute many tasks simultaneously. To get started, invoke the `pool` method, which accepts a closure that receives an instance of `Illuminate\Console\Process\Pool`. -Within this closure, you may define the processes that belong to the pool. Once a process pool is started via the `start` method, you may access a [collection](/docs/{{version}}/collections) of the running processes via the `running` method. Then, you may wait for all of the pool processes to finish executing and resolve their results via the `wait` method. The `wait` method returns an array accessible object that allows you to access the process result instance of each process in the pool by its key: +Within this closure, you may define the processes that belong to the pool. Once a process pool is started via the `start` method, you may access the [collection](/docs/{{version}}/collections) of running processes via the `running` method: ```php +use Illuminate\Console\Process\Pool; +use Illuminate\Support\Facades\Process; + $pool = Process::pool(function (Pool $pool) { - $pool->path(base_path())->command('bash import-1.sh'); - $pool->path(base_path())->command('bash import-2.sh'); - $pool->path(base_path())->command('bash import-3.sh'); + $pool->path(__DIR__)->command('bash import-1.sh'); + $pool->path(__DIR__)->command('bash import-2.sh'); + $pool->path(__DIR__)->command('bash import-3.sh'); })->start(function ($type, $output, $key) { // ... }); @@ -242,7 +254,285 @@ while ($pool->running()->isNotEmpty()) { // ... } +$results = $pool->wait(); +``` + +As you can see, you may wait for all of the pool processes to finish executing and resolve their results via the `wait` method. The `wait` method returns an array accessible object that allows you to access the process result instance of each process in the pool by its key: + +```php $results = $pool->wait(); echo $results[0]->output(); ``` + +Or, for convenience, the `concurrently` method may be used to start an asynchronous process pool and immediately wait on its results. This can provide particularly expressive syntax when combined with PHP's array destructuring capabilities: + +```php +[$first, $second, $third] = Process::concurrently(function (Pool $pool) { + $pool->path(__DIR__)->command('ls -la'); + $pool->path(app_path())->command('ls -la'); + $pool->path(storage_path())->command('ls -la'); +}); + +echo $first->output(); +``` + + +### Naming Pool Processes + +Accessing process pool results via a numeric key is not very expressive; therefore, Laravel allows you to assign string keys to each process within a pool via the `as` method. This key will also be passed to the closure provided to the `start` method, allowing you to determine which process the real-time output belongs to: + +```php +$pool = Process::pool(function (Pool $pool) { + $pool->as('first')->command('bash import-1.sh'); + $pool->as('second')->command('bash import-2.sh'); + $pool->as('third')->command('bash import-3.sh'); +})->start(function ($type, $output, $key) { + // ... +}); + +$results = $pool->wait(); + +return $results['first']->output(); +``` + + +### Pool Process IDs & Signals + +Since the process pool's `running` method provides a collection of all invoked processes within the pool, you may easily access the underlying pool process IDs: + +```php +$processIds = $pool->running()->each->pid(); +``` + +And, for convenience, you may invoke the `signal` method on a process pool to send a signal to every process within the pool: + +```php +$pool->signal(SIGUSR2); +``` + + +## Testing + +Many Laravel services provide functionality to help you easily and expressively write tests, and Laravel's process service is no exception. The `Process` facade's `fake` method allows you to instruct Laravel to return stubbed / dummy results when processes are invoked. + + +### Faking Processes + +To explore Laravel's ability to fake processes, let's imagine a route that invokes a process: + +```php +use Illuminate\Support\Facades\Process; +use Illuminate\Support\Facades\Route; + +Route::get('/import', function () { + Process::run('bash import.sh'); + + return 'Import complete!'; +}); +``` + +When testing this route, we can instruct Laravel to return a fake, successful process result for every invoked process by calling the `fake` method on the `Process` facade with no arguments. In addition, we can even [assert](#available-assertions) that a given process was "ran": + +```php +get('/'); + + // Simple process assertion... + Process::assertRan('bash import.sh'); + + // Or, inspecting the process configuration... + Process::assertRan(function ($process, $result) { + return $process->command === 'bash import.sh' && + $process->timeout === 60; + }); + } +} +``` + +As discussed, invoking the `fake` method on the `Process` facade will instruct Laravel to always return a successful process result with no output. However, you may easily specify the output and exit code for faked processes using the `Process` facade's `result` method: + +```php +Process::fake([ + '*' => Process::result( + output: 'Test output', + errorOutput: 'Test error output', + exitCode: 1, + ), +]); +``` + + +### Faking Specific Processes + +As you may have noticed in a previous example, the `Process` facade allows you to specify different fake results per process by passing an array to the `fake` method. + +The array's keys should represent command patterns that you wish to fake and their associated results. The `*` character may be used as a wildcard character. Any process commands that have not been faked will actually be invoked. You may use the `Process` facade's `result` method to construct stub / fake results for these commands: + +```php +Process::fake([ + 'cat *' => Process::result( + output: 'Test "cat" output', + ), + 'ls *' => Process::result( + output: 'Test "ls" output', + ), +]); +``` + +If you do not need to customize the exit code or error output of a faked process, you may find it more convenient to specify the fake process results as simple strings: + +```php +Process::fake([ + 'cat *' => 'Test "cat" output', + 'ls *' => 'Test "ls" output', +]); +``` + + +### Faking Process Sequences + +If the code you are testing invokes multiple processes with the same command, you may wish to assign a different fake process result to each process invocation. You may accomplish this via the `Process` facade's `sequence` method: + +```php +Process::fake([ + 'ls *' => Process::sequence() + ->push(Process::result('First invocation')) + ->push(Process::result('Second invocation')), +]); +``` + + +### Faking Asynchronous Process Lifecycles + +Thus far, we have primarily discussed faking processes which are invoked synchronously using the `run` method. However, if you are attempting to test code that interacts with asynchronous processes invoked via `start`, you may need a more sophisticated approach to describing your fake processes. + +For example, let's imagine the following route which interacts with an asynchronous process: + +```php +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Route; + +Route::get('/import', function () { + $process = Process::start('bash import.sh'); + + while ($process->running()) { + Log::info($process->latestOutput()); + Log::info($process->latestErrorOutput()); + } + + return 'Done'; +}); +``` + +To properly fake this process, we need to be able to describe how many times the `running` method should return `true`. In addition, we may want to specify multiple lines of output that should be returned in sequence. To accomplish this, we can use the `Process` facade's `describe` method: + +```php +Process::fake([ + 'bash import.sh' => Process::describe() + ->output('First line of standard output') + ->errorOutput('First line of error output') + ->output('Second line of standard output') + ->exitCode(0) + ->iterations(3), +]); +``` + +Let's dig into the example above. Using the `output` and `errorOutput` methods, we may specify multiple lines of output that will be returned in sequence. The `exitCode` method may be used to specify the final exit code of the fake process. Finally, the `iterations` method may be used to specify how many times the `running` method should return `true`. + + +### Available Assertions + +As [previously discussed](#faking-processes), Laravel provides several process assertions for your feature tests. We'll discuss each of these assertions below. + + +#### assertRan + +Assert that a given process was invoked: + +```php +use Illuminate\Support\Facades\Process; + +Process::assertRan('ls -la'); +``` + +The `assertRan` method also accepts a closure, which will receive an instance of a process and a process result, allowing you to inspect the process' configured options. If this closure returns `true`, the assertion will "pass": + +```php +Process::assertRan(fn ($process, $result) => + $process->command === 'ls -la' && + $process->path === __DIR__ && + $process->timeout === 60 +); +``` + +The `$process` passed to the `assertRan` closure is an instance of `Illuminate\Console\Process\PendingProcess`, while the `$result` is an instance of `Illuminate\Contracts\Console\Process\ProcessResult`. + + +#### assertDidntRun + +Assert that a given process was not invoked: + +```php +use Illuminate\Support\Facades\Process; + +Process::assertDidntRun('ls -la'); +``` + +Like the `assertRan` method, the `assertDidntRun` method also accepts a closure, which will receive an instance of a process and a process result, allowing you to inspect the process' configured options. If this closure returns `true`, the assertion will "fail": + +```php +Process::assertDidntRun(fn ($process, $result) => + $process->command === 'ls -la' +); +``` + + +#### assertRanTimes + +Assert that a given process was invoked a given number of times: + +```php +use Illuminate\Support\Facades\Process; + +Process::assertRanTimes('ls -la', times: 3); +``` + +The `assertRanTimes` method also accepts a closure, which will receive an instance of a process and a process result, allowing you to inspect the process' configured options. If this closure returns `true` and the process was invoked the specified number of times, the assertion will "pass": + +```php +Process::assertRanTimes(function ($process, $result) { + return $process->command === 'ls -la'; +}, times: 3); +``` + + +### Preventing Stray Processes + +If you would like to ensure that all invoked processes have been faked throughout your individual test or complete test suite, you can call the `preventStrayProcesses` method. After calling this method, any processes that do not have a corresponding fake result will throw an exception rather than starting an actual process: + + use Illuminate\Support\Facades\Process; + + Process::preventStrayProcesses(); + + Process::fake([ + 'ls *' => 'Test output...', + ]); + + // Fake response is returned... + Process::run('ls -la'); + + // An exception is thrown... + Process::run('bash import.sh'); From 5160127b5be8c7b5dee2204c1d7041dd5e5fea67 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Feb 2023 18:32:17 -0600 Subject: [PATCH 0684/2609] wip --- processes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processes.md b/processes.md index 7c444a3819e..cbf28678ca6 100644 --- a/processes.md +++ b/processes.md @@ -155,12 +155,12 @@ if (Process::run('ls -la')->seeInOutput('laravel')) { #### Disabling Process Output -If your process is writing a significant amount of output that you are not interested in, you can conserve memory by disabling output retrieval entirely. To accomplish this, invoke the `withoutOutput` method while building the process: +If your process is writing a significant amount of output that you are not interested in, you can conserve memory by disabling output retrieval entirely. To accomplish this, invoke the `quietly` method while building the process: ```php use Illuminate\Support\Facades\Process; -$result = Process::withoutOutput()->run('bash import.sh'); +$result = Process::quietly()->run('bash import.sh'); ``` From d16afaa42b3b1253638801a91fcf6ff5f9e06145 Mon Sep 17 00:00:00 2001 From: Ben Miller <67608755+modernben@users.noreply.github.com> Date: Tue, 7 Feb 2023 18:08:52 -0800 Subject: [PATCH 0685/2609] Revert wsHost for ably (#8522) Their website url has changed but the wsHost stays the same. Just tested and confirmed. --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 695609f9161..2615997c889 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -229,7 +229,7 @@ window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_ABLY_PUBLIC_KEY, - wsHost: 'realtime-pusher.ably.com', + wsHost: 'realtime-pusher.ably.io', wsPort: 443, disableStats: true, encrypted: true, From 28a2467b0ed2fe867862c86fa82351e234e22834 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 15:27:40 +1100 Subject: [PATCH 0686/2609] wip --- pennant.md | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/pennant.md b/pennant.md index ce36de583a3..eb8baf0467b 100644 --- a/pennant.md +++ b/pennant.md @@ -39,9 +39,39 @@ After publishing Pennant's assets, its configuration file will be located at `co ## Events -There are a few events that are fired which may be useful in tracking the usage of active feature flags throughout your application. +### `Illuminate\Pennant\Events\RetrievingKnownFeature` -- `Illuminate\Pennant\Events\RetrievingKnownFeature` is dispatched +This event is dispatched the first time a known feature is resolved during a request for the given scope. This may be useful to track and create metrics against the in-use feature flags throughout your application. + +### `Illuminate\Pennant\Events\RetrievingUnknownFeature` + +This event is dispatched the first time an unknown feature is resolved during a request for the given scope. This may be useful if you have intented to remove a feature flag from your system, but left some stray references to it throughout your application. + +You may also use this event to restrict unknown feature from being referenced. + +```php + report("Resolving unknown feature [{$event->feature}].")); + } +} +``` +- `Illuminate\Pennant\Events\RetrievingUnknownFeature` is dispatched the first time an unknown feature is resolved for the given scope. +- `Illuminate\Pennant\Events\RetrievingUnknownFeature` is dispatched the first time an unknown feature is resolved for the given scope. - Defining features From d3c2702638b8d50349606816ee65594d8bc42f4b Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 15:46:55 +1100 Subject: [PATCH 0687/2609] wip --- pennant.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pennant.md b/pennant.md index eb8baf0467b..1ee51c57bfa 100644 --- a/pennant.md +++ b/pennant.md @@ -8,7 +8,7 @@ ## Introduction -[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flagging package, without the fluff. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. +[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flag package, without any cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. ## Installation @@ -41,13 +41,13 @@ After publishing Pennant's assets, its configuration file will be located at `co ### `Illuminate\Pennant\Events\RetrievingKnownFeature` -This event is dispatched the first time a known feature is resolved during a request for the given scope. This may be useful to track and create metrics against the in-use feature flags throughout your application. +This event is dispatched the first time a known feature is resolved during a request for the given scope. This may be useful to create and track metrics against the feature flags that are in-use throughout your application. ### `Illuminate\Pennant\Events\RetrievingUnknownFeature` -This event is dispatched the first time an unknown feature is resolved during a request for the given scope. This may be useful if you have intented to remove a feature flag from your system, but left some stray references to it throughout your application. +This event is dispatched thefirst time an unknown feature is resolved during a request for the given scope. This may be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. -You may also use this event to restrict unknown feature from being referenced. +You may like to listen for this event and report or throw an exception when it occurs. ```php report("Resolving unknown feature [{$event->feature}].")); + Event::listen(function (RetrievingUnknownFeature $event) { + report("Resolving unknown feature [{$event->feature}].")); + }); } } ``` -- `Illuminate\Pennant\Events\RetrievingUnknownFeature` is dispatched the first time an unknown feature is resolved for the given scope. -- `Illuminate\Pennant\Events\RetrievingUnknownFeature` is dispatched the first time an unknown feature is resolved for the given scope. + +### `Illuminate\Pennant\Events\DynamicallyDefiningFeature` + +This event is dispatched whenever a feature is being dynamically defined for the first time during a request. - Defining features From f0f2d07ed432381b30c35eb752e86e96df2b85c7 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 16:11:15 +1100 Subject: [PATCH 0688/2609] wip --- pennant.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/pennant.md b/pennant.md index 1ee51c57bfa..b35c063a452 100644 --- a/pennant.md +++ b/pennant.md @@ -3,6 +3,7 @@ - [Introduction](#introduction) - [Installation](#installation) - [Configuration](#configuration) +- [Defining Features](#defining-features) - [Events](#events) @@ -36,6 +37,79 @@ php artisan migrate After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to select the default driver and configure the individual drivers. + +## Defining Features + +A feature definition is simply a Closure that returns the initial value for the specific feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature flag check, which would commonly be the current user. + +In this example, we will define a feature for rolling out a new API implementation incrementally to our application's users. + +```php +isInternalTeamMember()) { + return true; + } + + if ($user->isHighTrafficCustomer()) { + return false; + } + + return Lottery::odds(1 / 100); + }); + } +} +``` + +As you can see, we have set the following rules for our feature definition: + +- All internal team members should be using the new API. +- Any high traffic customers should not be using the new API. +- Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being activated. + +You may then check the value of this feature flag: + +```php +resolveNewApiResponse($request); + } + + return $this->resolveLegacyApiResponse($request); + } +} +``` + +- Defining features + - string based + - class based + - dynamic class based (event is fired) + + ## Events @@ -77,10 +151,6 @@ class EventServiceProvider extends ServiceProvider This event is dispatched whenever a feature is being dynamically defined for the first time during a request. -- Defining features - - string based - - class based - - dynamic class based - events - known From 7dc66de5a563ab7b53a34b9bc8838d5af2aa470e Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 16:19:40 +1100 Subject: [PATCH 0689/2609] wip --- pennant.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index b35c063a452..50d4aafe682 100644 --- a/pennant.md +++ b/pennant.md @@ -4,6 +4,7 @@ - [Installation](#installation) - [Configuration](#configuration) - [Defining Features](#defining-features) +- [Checking Features](#checking-features) - [Events](#events) @@ -81,7 +82,10 @@ As you can see, we have set the following rules for our feature definition: - Any high traffic customers should not be using the new API. - Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being activated. -You may then check the value of this feature flag: + +## Checking Features + +The state of a feature flag may be resolved via the `Feature` facade. By default, features are checked against the currently authenticated user. ```php Date: Wed, 8 Feb 2023 16:22:26 +1100 Subject: [PATCH 0690/2609] wip --- pennant.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 50d4aafe682..2bdbc2232df 100644 --- a/pennant.md +++ b/pennant.md @@ -93,11 +93,15 @@ The state of a feature flag may be resolved via the `Feature` facade. By default namespace App\Http\Controllers; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Laravel\Pennant\Feature; class PodcastController { - public function index(Request $request) + /** + * Display a listing of the resource. + */ + public function index(Request $request): Response { if (Feature::isActive('new-api')) { return $this->resolveNewApiResponse($request); @@ -105,6 +109,8 @@ class PodcastController return $this->resolveLegacyApiResponse($request); } + + // ... } ``` From 4747e2051acbf7da8cd73ce5000cdb9bfea1b938 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 16:52:23 +1100 Subject: [PATCH 0691/2609] wip --- pennant.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pennant.md b/pennant.md index 2bdbc2232df..2a9eeb4bd56 100644 --- a/pennant.md +++ b/pennant.md @@ -4,6 +4,7 @@ - [Installation](#installation) - [Configuration](#configuration) - [Defining Features](#defining-features) + - [Class Based Feature](#class-based-features) - [Checking Features](#checking-features) - [Events](#events) @@ -82,9 +83,42 @@ As you can see, we have set the following rules for our feature definition: - Any high traffic customers should not be using the new API. - Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being activated. + +### Class Based Features + +You may also create class based features. Class based features should implement a `define` method: + +```php +isInternalTeamMember()) { + return true; + } + + if ($user->isHighTrafficCustomer()) { + return false; + } + + return Lottery::odds(1 / 100); + } +} +``` + +There is no need to register class based features in a service provider. + ## Checking Features + + The state of a feature flag may be resolved via the `Feature` facade. By default, features are checked against the currently authenticated user. ```php From 4fa66adbeb9bf2a70f2fb1d4a6f942b9815be44c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 16:53:29 +1100 Subject: [PATCH 0692/2609] wip --- pennant.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pennant.md b/pennant.md index 2a9eeb4bd56..27e6298093a 100644 --- a/pennant.md +++ b/pennant.md @@ -86,7 +86,9 @@ As you can see, we have set the following rules for our feature definition: ### Class Based Features -You may also create class based features. Class based features should implement a `define` method: +You may also create class based features. Unlike Closure based definitions, there is no need to register class based features in a service provider. + +To create a feature class you should implement the `define` method: ```php ## Checking Features From 60f0dfca821f5003ad8eb2de3b62f78822221cc6 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 8 Feb 2023 17:01:20 +1100 Subject: [PATCH 0693/2609] wip --- pennant.md | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/pennant.md b/pennant.md index 27e6298093a..1aa18e6021b 100644 --- a/pennant.md +++ b/pennant.md @@ -6,6 +6,7 @@ - [Defining Features](#defining-features) - [Class Based Feature](#class-based-features) - [Checking Features](#checking-features) + - [In-Memory Cache](#in-memory-cache) - [Events](#events) @@ -88,7 +89,7 @@ As you can see, we have set the following rules for our feature definition: You may also create class based features. Unlike Closure based definitions, there is no need to register class based features in a service provider. -To create a feature class you should implement the `define` method: +When create a feature class you will need to implement the `define` method: ```php **Note** Feature classes are resolved via the container, so you may inject dependencies into the classes constructor when needed. ## Checking Features +The state of a feature flag may be resolved via the `Feature` facade using the features name. By default, features are checked against the currently authenticated user. +```php +resolveNewApiResponse($request); + } + + return $this->resolveLegacyApiResponse($request); + } -The state of a feature flag may be resolved via the `Feature` facade. By default, features are checked against the currently authenticated user. + // ... +} +``` + +If you are using a class based feature, you should use class name when checking the features state: ```php resolveNewApiResponse($request); } @@ -149,6 +179,9 @@ class PodcastController } ``` + +### In-Memory Cache + - Defining features - string based - class based From f39a7f5b4b7f008b518b3717bb48608ba1ee677a Mon Sep 17 00:00:00 2001 From: Matthew Davis Date: Wed, 8 Feb 2023 14:31:23 +0000 Subject: [PATCH 0694/2609] Fix typo in Starter Kits documentation. (#8523) --- starter-kits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index 65013958619..b4fab3552d0 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -28,7 +28,7 @@ Breeze provides a wonderful starting point for beginning a fresh Laravel applica #### Laravel Bootcamp -If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Breeze. It's a great way to get a tour of everything the Laravel and Breeze have to offer. +If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Breeze. It's a great way to get a tour of everything that Laravel and Breeze have to offer. ### Installation @@ -67,7 +67,7 @@ If you would like Breeze to include "dark mode" support when scaffolding your ap php artisan breeze:install --dark ``` -> **Note** +> **Note** > To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). From 80a43ace39cd908a71de28509505cd893ca74c9b Mon Sep 17 00:00:00 2001 From: Saleh Hashemi <81674631+salehhashemi1992@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:07:04 +0330 Subject: [PATCH 0695/2609] [9.x] Add notFound response helper (#8524) --- http-client.md | 1 + 1 file changed, 1 insertion(+) diff --git a/http-client.md b/http-client.md index 96ef8118283..6c79a99960f 100644 --- a/http-client.md +++ b/http-client.md @@ -49,6 +49,7 @@ The `get` method returns an instance of `Illuminate\Http\Client\Response`, which $response->successful() : bool; $response->redirect(): bool; $response->failed() : bool; + $response->notFound() : bool; $response->serverError() : bool; $response->clientError() : bool; $response->header($header) : string; From a520cd6fec5af8a68fba393b4d002e98705d9736 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 02:23:55 +1100 Subject: [PATCH 0696/2609] [9.x] Document `transformOnServe` Vite configuration (#8514) * Adds documentation on the transformOnServe vite configuration option * formatting --------- Co-authored-by: Taylor Otwell --- vite.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/vite.md b/vite.md index 23932319826..1400e29c941 100644 --- a/vite.md +++ b/vite.md @@ -27,6 +27,7 @@ - [Subresource Integrity (SRI)](#subresource-integrity-sri) - [Arbitrary Attributes](#arbitrary-attributes) - [Advanced Customization](#advanced-customization) + - [Correcting Dev Server URLs](#correcting-dev-server-urls) ## Introduction @@ -792,3 +793,42 @@ export default defineConfig({ }, }); ``` + + +### Correcting Dev Server URLs + +Some plugins within the Vite ecosystem assume that URLs which begin with a forward-slash will always point to the Vite dev server. However, due to the nature of the Laravel integration, this is not the case. + +For example, the `vite-imagetools` plugin outputs URLs like the following while Vite is serving your assets: + +```html + +``` + +The `vite-imagetools` plugin is expecting that the output URL will be intercepted by Vite and the plugin may then handle all URLs that start with `/@imagetools`. If you are using plugins that are expecting this behaviour, you will need to manually correct the URLs. You can do this in your `vite.config.js` file by using the `transformOnServe` option. + +In this particular example, we will append the dev server URL to all occurrences of `/@imagetools` within the generated code: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import { imagetools } from 'vite-imagetools'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + transformOnServe: (code, devServerUrl) => code.replaceAll('/@imagetools', devServerUrl+'/@imagetools'), + }), + imagetools(), + ], +}); +``` + +Now, while Vite is serving Assets, it will output URLs that point to the Vite dev server: + +```html +- ++ +``` + From a3d5460aa7b522c512a087af8b0a0747ab3405b9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 8 Feb 2023 15:38:58 +0000 Subject: [PATCH 0697/2609] Applies minor fixes on process module (#8525) --- processes.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/processes.md b/processes.md index cbf28678ca6..cf90a1f6236 100644 --- a/processes.md +++ b/processes.md @@ -41,7 +41,7 @@ Of course, the `Illuminate\Contracts\Console\Process\ProcessResult` instance ret ```php $result = Process::run('ls -la'); -$result->successful(): +$result->successful(); $result->failed(); $result->exitCode(); $result->output(); @@ -224,7 +224,7 @@ while ($process->running()) { Like the `run` method, output may also be gathered in real-time from asynchronous processes by passing a closure as the second argument to the `start` method. The closure will receive two arguments: the "type" of output (`stdout` or `stderr`) and the output string itself: ```php -$process = Process::start('bash import.sh', function ($type, $output) { +$process = Process::start('bash import.sh', function (string $type, string $output) { echo $output; }); @@ -246,7 +246,7 @@ $pool = Process::pool(function (Pool $pool) { $pool->path(__DIR__)->command('bash import-1.sh'); $pool->path(__DIR__)->command('bash import-2.sh'); $pool->path(__DIR__)->command('bash import-3.sh'); -})->start(function ($type, $output, $key) { +})->start(function (string $type, string $output, int $key) { // ... }); @@ -280,14 +280,14 @@ echo $first->output(); ### Naming Pool Processes -Accessing process pool results via a numeric key is not very expressive; therefore, Laravel allows you to assign string keys to each process within a pool via the `as` method. This key will also be passed to the closure provided to the `start` method, allowing you to determine which process the real-time output belongs to: +Accessing process pool results via a numeric key is not very expressive; therefore, Laravel allows you to assign string keys to each process within a pool via the `as` method. This key will also be passed to the closure provided to the `start` method, allowing you to determine which process the output belongs to: ```php $pool = Process::pool(function (Pool $pool) { $pool->as('first')->command('bash import-1.sh'); $pool->as('second')->command('bash import-2.sh'); $pool->as('third')->command('bash import-3.sh'); -})->start(function ($type, $output, $key) { +})->start(function (string $type, string $output, string $key) { // ... }); @@ -339,6 +339,8 @@ When testing this route, we can instruct Laravel to return a fake, successful pr namespace Tests\Feature; +use Illuminate\Console\Process\PendingProcess; +use Illuminate\Contracts\Console\Process\ProcessResult; use Illuminate\Support\Facades\Process; use Tests\TestCase; @@ -354,7 +356,7 @@ class ExampleTest extends TestCase Process::assertRan('bash import.sh'); // Or, inspecting the process configuration... - Process::assertRan(function ($process, $result) { + Process::assertRan(function (PendingProcess $process, ProcessResult $result) { return $process->command === 'bash import.sh' && $process->timeout === 60; }); @@ -494,7 +496,7 @@ Process::assertDidntRun('ls -la'); Like the `assertRan` method, the `assertDidntRun` method also accepts a closure, which will receive an instance of a process and a process result, allowing you to inspect the process' configured options. If this closure returns `true`, the assertion will "fail": ```php -Process::assertDidntRun(fn ($process, $result) => +Process::assertDidntRun(fn (PendingProcess $process, ProcessResult $result) => $process->command === 'ls -la' ); ``` @@ -513,7 +515,7 @@ Process::assertRanTimes('ls -la', times: 3); The `assertRanTimes` method also accepts a closure, which will receive an instance of a process and a process result, allowing you to inspect the process' configured options. If this closure returns `true` and the process was invoked the specified number of times, the assertion will "pass": ```php -Process::assertRanTimes(function ($process, $result) { +Process::assertRanTimes(function (PendingProcess $process, ProcessResult $result) { return $process->command === 'ls -la'; }, times: 3); ``` From c3fc92168a0dd2b8c97097eac073f766e3f9950c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 8 Feb 2023 13:17:49 -0600 Subject: [PATCH 0698/2609] lang updates (#8529) --- fortify.md | 4 +++- helpers.md | 7 +++++-- localization.md | 19 ++++++++++++++++--- packages.md | 20 ++++++++++---------- passwords.md | 5 ++++- structure.md | 6 ------ validation.md | 16 ++++++++++++++-- 7 files changed, 52 insertions(+), 25 deletions(-) diff --git a/fortify.md b/fortify.md index ddb88916361..95dd50b5b83 100644 --- a/fortify.md +++ b/fortify.md @@ -423,7 +423,9 @@ The `/forgot-password` endpoint expects a string `email` field. The name of this If the password reset link request was successful, Fortify will redirect the user back to the `/forgot-password` endpoint and send an email to the user with a secure link they can use to reset their password. If the request was an XHR request, a 200 HTTP response will be returned. -After being redirected back to the `/forgot-password` endpoint after a successful request, the `status` session variable may be used to display the status of the password reset link request attempt. The value of this session variable will match one of the translation strings defined within your application's `passwords` [language file](/docs/{{version}}/localization): +After being redirected back to the `/forgot-password` endpoint after a successful request, the `status` session variable may be used to display the status of the password reset link request attempt. + +The value of the `$status` session variable will match one of the translation strings defined within your application's `passwords` [language file](/docs/{{version}}/localization). If you would like to customize this value and have not published Laravel's language files, you may do so via the `lang:publish` Artisan command: ```html @if (session('status')) diff --git a/helpers.md b/helpers.md index 85806f552e1..c69dfc4165f 100644 --- a/helpers.md +++ b/helpers.md @@ -1220,6 +1220,9 @@ The `lang_path` function returns the fully qualified path to your application's $path = lang_path('en/messages.php'); +> **Note** +> By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. + #### `mix()` {.collection-method} @@ -1260,7 +1263,7 @@ The `storage_path` function returns the fully qualified path to your application #### `__()` {.collection-method} -The `__` function translates the given translation string or translation key using your [localization files](/docs/{{version}}/localization): +The `__` function translates the given translation string or translation key using your [language files](/docs/{{version}}/localization): echo __('Welcome to our application'); @@ -2131,7 +2134,7 @@ If no argument is provided to the `str` function, the function returns an instan #### `trans()` {.collection-method} -The `trans` function translates the given translation key using your [localization files](/docs/{{version}}/localization): +The `trans` function translates the given translation key using your [language files](/docs/{{version}}/localization): echo trans('messages.welcome'); diff --git a/localization.md b/localization.md index 2c7b5ce58c8..9bae943210b 100644 --- a/localization.md +++ b/localization.md @@ -1,6 +1,7 @@ # Localization - [Introduction](#introduction) + - [Publishing The Language Files](#publishing-the-language-files) - [Configuring The Locale](#configuring-the-locale) - [Pluralization Language](#pluralization-language) - [Defining Translation Strings](#defining-translation-strings) @@ -14,9 +15,12 @@ ## Introduction +> **Note** +> By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. + Laravel's localization features provide a convenient way to retrieve strings in various languages, allowing you to easily support multiple languages within your application. -Laravel provides two ways to manage translation strings. First, language strings may be stored in files within the `lang` directory. Within this directory, there may be subdirectories for each language supported by the application. This is the approach Laravel uses to manage translation strings for built-in Laravel features such as validation error messages: +Laravel provides two ways to manage translation strings. First, language strings may be stored in files within the application's `lang` directory. Within this directory, there may be subdirectories for each language supported by the application. This is the approach Laravel uses to manage translation strings for built-in Laravel features such as validation error messages: /lang /en @@ -32,6 +36,15 @@ Or, translation strings may be defined within JSON files that are placed within We'll discuss each approach to managing translation strings within this documentation. + +### Publishing The Language Files + +By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files or create your own, you should scaffold the `lang` directory via the `lang:publish` Artisan command. The `lang:publish` command will create the `lang` directory in your application and publish the default set of language files used by Laravel: + +```shell +php artisan lang:publish +``` + ### Configuring The Locale @@ -120,7 +133,7 @@ All language files return an array of keyed strings. For example: For applications with a large number of translatable strings, defining every string with a "short key" can become confusing when referencing the keys in your views and it is cumbersome to continually invent keys for every translation string supported by your application. -For this reason, Laravel also provides support for defining translation strings using the "default" translation of the string as the key. Translation files that use translation strings as keys are stored as JSON files in the `lang` directory. For example, if your application has a Spanish translation, you should create a `lang/es.json` file: +For this reason, Laravel also provides support for defining translation strings using the "default" translation of the string as the key. Language files that use translation strings as keys are stored as JSON files in the `lang` directory. For example, if your application has a Spanish translation, you should create a `lang/es.json` file: ```json { @@ -130,7 +143,7 @@ For this reason, Laravel also provides support for defining translation strings #### Key / File Conflicts -You should not define translation string keys that conflict with other translation filenames. For example, translating `__('Action')` for the "NL" locale while a `nl/action.php` file exists but a `nl.json` file does not exist will result in the translator returning the contents of `nl/action.php`. +You should not define translation string keys that conflict with other translation filenames. For example, translating `__('Action')` for the "NL" locale while a `nl/action.php` file exists but a `nl.json` file does not exist will result in the translator returning the entire contents of `nl/action.php`. ## Retrieving Translation Strings diff --git a/packages.md b/packages.md index 5b558b92ac0..3021f1fbf31 100644 --- a/packages.md +++ b/packages.md @@ -8,7 +8,7 @@ - [Configuration](#configuration) - [Migrations](#migrations) - [Routes](#routes) - - [Translations](#translations) + - [Language Files](#language-files) - [Views](#views) - [View Components](#view-components) - ["About" Artisan Command](#about-artisan-command) @@ -80,7 +80,7 @@ You may disable package discovery for all packages using the `*` character insid ## Service Providers -[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. +[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 language 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). @@ -157,10 +157,10 @@ If your package contains [database migrations](/docs/{{version}}/migrations), yo 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 + +### Language Files -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: +If your package contains [language 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 package services. @@ -170,14 +170,14 @@ If your package contains [translation files](/docs/{{version}}/localization), yo $this->loadTranslationsFrom(__DIR__.'/../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: +Package translation lines 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: echo trans('courier::messages.welcome'); - -#### Publishing Translations + +#### Publishing Language Files -If you would like to publish your package's translations to the application's `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: +If you would like to publish your package's language files to the application's `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 language files for the `courier` package, you may do the following: /** * Bootstrap any package services. @@ -191,7 +191,7 @@ If you would like to publish your package's translations to the application's `l ]); } -Now, when users of your package execute Laravel's `vendor:publish` Artisan command, your package's translations will be published to the specified publish location. +Now, when users of your package execute Laravel's `vendor:publish` Artisan command, your package's language files will be published to the specified publish location. ### Views diff --git a/passwords.md b/passwords.md index 31993dbf810..14651a4f727 100644 --- a/passwords.md +++ b/passwords.md @@ -86,6 +86,9 @@ Before moving on, let's examine this route in more detail. First, the request's 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 `lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. +> **Note** +> By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. + You may be 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). > **Note** @@ -146,7 +149,7 @@ Before moving on, let's examine this route in more detail. First, the request's 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 `lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. +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 `lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. If your application does not contain a `lang` directory, you may create it using the `lang:publish` Artisan command. Before moving on, you may be 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). diff --git a/structure.md b/structure.md index 22063a71627..1a62b5d9642 100644 --- a/structure.md +++ b/structure.md @@ -6,7 +6,6 @@ - [The `bootstrap` Directory](#the-bootstrap-directory) - [The `config` Directory](#the-config-directory) - [The `database` Directory](#the-database-directory) - - [The `lang` Directory](#the-lang-directory) - [The `public` Directory](#the-public-directory) - [The `resources` Directory](#the-resources-directory) - [The `routes` Directory](#the-routes-directory) @@ -59,11 +58,6 @@ The `config` directory, as the name implies, contains all of your application's The `database` directory contains your database migrations, model factories, and seeds. If you wish, you may also use this directory to hold an SQLite database. - -#### The Lang Directory - -The `lang` directory houses all of your application's language files. - #### The Public Directory diff --git a/validation.md b/validation.md index 8490dce803b..674f2df2a19 100644 --- a/validation.md +++ b/validation.md @@ -199,7 +199,10 @@ So, in our example, the user will be redirected to our controller's `create` met Laravel's built-in validation rules each have an error message that is located in your application's `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). +In addition, you may copy this file to another 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). + +> **Warning** +> By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. #### XHR Requests & Validation @@ -736,7 +739,10 @@ The `has` method may be used to determine if any error messages exist for a give Laravel's built-in validation rules each have an error message that is located in your application's `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). +In addition, you may copy this file to another 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). + +> **Warning** +> By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. #### Custom Messages For Specific Attributes @@ -759,6 +765,9 @@ Many of Laravel's built-in error messages include an `:attribute` placeholder th 'email' => 'email address', ], +> **Warning** +> By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. + ### Specifying Values In Language Files @@ -782,6 +791,9 @@ Instead of displaying `cc` as the payment type value, you may specify a more use ], ], +> **Warning** +> By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. + After defining this value, the validation rule will produce the following error message: ```none From 70cca6c380298eef41ed726fdd8d4553ca2997c8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 8 Feb 2023 13:20:24 -0600 Subject: [PATCH 0699/2609] wip --- upgrade.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/upgrade.md b/upgrade.md index c6c16296e4d..b4efa237ea6 100644 --- a/upgrade.md +++ b/upgrade.md @@ -21,6 +21,7 @@ - [Model "Dates" Property](#model-dates-property) - [Redis Cache Tags](#redis-cache-tags) - [Service Mocking](#service-mocking) +- [The Language Directory](#language-directory)
    @@ -163,6 +164,19 @@ protected $casts = [ The `getBaseQuery` method on the `Illuminate\Database\Eloquent\Relations\Relation` class has been renamed to `toBase`. +### Localization + + +#### The Language Directory + +**Likelihood Of Impact: None** + +Though not relevant to existing applications, the Laravel application skeleton no longer contains the `lang` directory by default. Instead, when writing new Laravel applications, it may be published using the `lang:publish` Artisan command: + +```shell +php artisan lang:publish +``` + ### Logging From f96ba156013923392e09dfc30627be39d1f7b574 Mon Sep 17 00:00:00 2001 From: bnzo <17174973+bnzo@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:21:48 +0100 Subject: [PATCH 0700/2609] Update vite.md (#8530) Typo --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 1400e29c941..091ea4e6e65 100644 --- a/vite.md +++ b/vite.md @@ -682,7 +682,7 @@ Vite::useCspNonce($nonce); ### Subresource Integrity (SRI) -If your Vite manifest includes `integrity` hashes for your assets, Laravel will automatically add the `integrity` attribute on any script and style tags it generates in order to enforce [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). By default, Vite does not include the `integrity` hash in its manifest, but you may enable it by installing the [`vite-plugin-manifest-uri`](https://www.npmjs.com/package/vite-plugin-manifest-sri) NPM plugin: +If your Vite manifest includes `integrity` hashes for your assets, Laravel will automatically add the `integrity` attribute on any script and style tags it generates in order to enforce [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). By default, Vite does not include the `integrity` hash in its manifest, but you may enable it by installing the [`vite-plugin-manifest-sri`](https://www.npmjs.com/package/vite-plugin-manifest-sri) NPM plugin: ```shell npm install --save-dev vite-plugin-manifest-sri From 7a3dfdadc618f43e6829ab26ceb27b43acddff6c Mon Sep 17 00:00:00 2001 From: Eric Horstmanshof Date: Wed, 8 Feb 2023 21:22:39 +0100 Subject: [PATCH 0701/2609] [9.x] Changed vagrant download URL to new URL (#8528) The downloads page was previously located at /downloads.html, but it changed recently to: https://developer.hashicorp.com/vagrant/downloads See the download button at: https://www.vagrantup.com This PR changes the link to the new correct one. --- homestead.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homestead.md b/homestead.md index e54c80d1d08..81d3a93805e 100644 --- a/homestead.md +++ b/homestead.md @@ -139,7 +139,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M ### First Steps -Before launching your Homestead environment, you must install [Vagrant](https://www.vagrantup.com/downloads.html) as well as one of the following supported providers: +Before launching your Homestead environment, you must install [Vagrant](https://developer.hashicorp.com/vagrant/downloads) as well as one of the following supported providers: - [VirtualBox 6.1.x](https://www.virtualbox.org/wiki/Downloads) - [Parallels](https://www.parallels.com/products/desktop/) From 1bc8509c8d3416d1409abdf7f0fa892146c6c689 Mon Sep 17 00:00:00 2001 From: Ollie Crook Date: Wed, 8 Feb 2023 20:22:58 +0000 Subject: [PATCH 0702/2609] typo: thorough -> thoroughly (#8527) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 6f0e31688ca..6d3bdb7548a 100644 --- a/releases.md +++ b/releases.md @@ -67,7 +67,7 @@ _Application skeleton and stub type-hints were contributed by [Nuno Maduro](http On its initial release, Laravel utilized all of the type-hinting features available in PHP at the time. However, many new features have been added to PHP in the subsequent years, including additional primitive type-hints, return types, and union types. -Laravel 10.x thorough updates the application skeleton and all stubs utilized by the framework to introduce argument and return types to all method signatures. In addition, extraneous "doc block" type-hint information has been deleted: +Laravel 10.x thoroughly updates the application skeleton and all stubs utilized by the framework to introduce argument and return types to all method signatures. In addition, extraneous "doc block" type-hint information has been deleted: ```php Date: Thu, 9 Feb 2023 02:17:37 +0330 Subject: [PATCH 0703/2609] [9.x] Add remaining response helpers (#8526) * [9.x] Add remaining response helpers and sort them * fix function params * Update http-client.md * Update http-client.md * formatting * Update http-client.md --------- Co-authored-by: Taylor Otwell --- http-client.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/http-client.md b/http-client.md index 6c79a99960f..4e0055f7091 100644 --- a/http-client.md +++ b/http-client.md @@ -41,16 +41,13 @@ To make requests, you may use the `head`, `get`, `post`, `put`, `patch`, and `de The `get` method returns an instance of `Illuminate\Http\Client\Response`, which provides a variety of methods that may be used to inspect the response: $response->body() : string; - $response->json($key = null) : array|mixed; + $response->json($key = null, $default = null) : array|mixed; $response->object() : object; $response->collect($key = null) : Illuminate\Support\Collection; $response->status() : int; - $response->ok() : bool; $response->successful() : bool; $response->redirect(): bool; $response->failed() : bool; - $response->notFound() : bool; - $response->serverError() : bool; $response->clientError() : bool; $response->header($header) : string; $response->headers() : array; @@ -59,6 +56,25 @@ The `Illuminate\Http\Client\Response` object also implements the PHP `ArrayAcces return Http::get('http://example.com/users/1')['name']; +In addition to the response methods listed above, the following methods may be used to determine if the response has a given status code: + + $response->ok() : bool; // 200 OK + $response->created() : bool; // 201 Created + $response->accepted() : bool; // 202 Accepted + $response->noContent() : bool; // 204 No Content + $response->movedPermanently() : bool; // 301 Moved Permanently + $response->found() : bool; // 302 Found + $response->badRequest() : bool; // 400 Bad Request + $response->unauthorized() : bool; // 401 Unauthorized + $response->paymentRequired() : bool; // 402 Payment Required + $response->forbidden() : bool; // 403 Forbidden + $response->notFound() : bool; // 404 Not Found + $response->requestTimeout() : bool; // 408 Request Timeout + $response->conflict() : bool; // 409 Conflict + $response->unprocessableEntity() : bool; // 422 Unprocessable Entity + $response->tooManyRequests() : bool; // 429 Too Many Requests + $response->serverError() : bool; // 500 Internal Server Error + #### URI Templates From 8f406283bc601f15d5cbdfbc35de4b618524a53c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 09:56:11 +1100 Subject: [PATCH 0704/2609] wip --- pennant.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pennant.md b/pennant.md index 1aa18e6021b..51697049f86 100644 --- a/pennant.md +++ b/pennant.md @@ -78,7 +78,7 @@ class AppServiceProvider extends ServiceProvider } ``` -As you can see, we have set the following rules for our feature definition: +As you can see, we have the following rules for our feature definition: - All internal team members should be using the new API. - Any high traffic customers should not be using the new API. @@ -115,12 +115,12 @@ class NewApi } ``` -> **Note** Feature classes are resolved via the container, so you may inject dependencies into the classes constructor when needed. +> **Note** Feature classes are resolved via the container, so you may inject dependencies into the class's constructor when needed. ## Checking Features -The state of a feature flag may be resolved via the `Feature` facade using the features name. By default, features are checked against the currently authenticated user. +To check if a feature is active, you should use the `isActive` method on the `Feature` facade. By default, features are checked against the currently authenticated user. ```php ### In-Memory Cache +When checking the state of a feature, Pennant will create a in-memory cache of the result. If you are using the database driver, this means that re-checking the same feature flag throughout a single request will not trigger subsequent database queries. It also ensures you have a consistent result for the duration of the request. + +If you need to manually flush the in-memory cache, you may use the `flushCache` method on the `Feature` facade. + + Feature::flushCache(); + + - Defining features - string based - class based From f19004423f76f36bc7849e14e9425e4b28a0014f Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 10:19:15 +1100 Subject: [PATCH 0705/2609] wip --- pennant.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pennant.md b/pennant.md index 51697049f86..b9663da3b62 100644 --- a/pennant.md +++ b/pennant.md @@ -84,12 +84,18 @@ As you can see, we have the following rules for our feature definition: - Any high traffic customers should not be using the new API. - Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being activated. +The first time the `new-api` feature is checked for a given user, the result of the definition Closure will be persisted by the underlying driver. This means that the next time the feature is check against the same user, the value will be retrieved from storage rather than decided by the feature's definition. + +If your feature definition only returns a lottery, you may omit the Closure completely. + + Feature::define('site-redesign', Lottery::odds(1, 1000)); + ### Class Based Features You may also create class based features. Unlike Closure based definitions, there is no need to register class based features in a service provider. -When create a feature class you will need to implement the `define` method: +When create a feature class you will need to implement the `resolve` method: ```php isInternalTeamMember()) { return true; @@ -188,23 +194,18 @@ If you need to manually flush the in-memory cache, you may use the `flushCache` Feature::flushCache(); - -- Defining features - - string based - - class based - - dynamic class based (event is fired) - - ## Events +Pennant dispatches a few events that may be useful for tracking the feature flags throughout your application. + ### `Illuminate\Pennant\Events\RetrievingKnownFeature` -This event is dispatched the first time a known feature is resolved during a request for the given scope. This may be useful to create and track metrics against the feature flags that are in-use throughout your application. +This event is dispatched the first time a known feature is resolved during a request for a specific scope. This may be useful to create and track metrics against the feature flags that are in-use throughout your application. ### `Illuminate\Pennant\Events\RetrievingUnknownFeature` -This event is dispatched thefirst time an unknown feature is resolved during a request for the given scope. This may be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. +This event is dispatched the first time an unknown feature is resolved during a request for the specific scope. This may be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. You may like to listen for this event and report or throw an exception when it occurs. @@ -233,7 +234,7 @@ class EventServiceProvider extends ServiceProvider ### `Illuminate\Pennant\Events\DynamicallyDefiningFeature` -This event is dispatched whenever a feature is being dynamically defined for the first time during a request. +This event is dispatched whenever an unregistered class based feature is being dynamically defined for the first time during a request. From b918a2ccaf0e9bfd83c421f0251dcf62acd772a8 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 12:34:28 +1100 Subject: [PATCH 0706/2609] wip --- pennant.md | 201 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 176 insertions(+), 25 deletions(-) diff --git a/pennant.md b/pennant.md index b9663da3b62..e4f8eab52ca 100644 --- a/pennant.md +++ b/pennant.md @@ -3,10 +3,14 @@ - [Introduction](#introduction) - [Installation](#installation) - [Configuration](#configuration) -- [Defining Features](#defining-features) - - [Class Based Feature](#class-based-features) +- [Creating Features](#creating-features) + - [Class Based Features](#class-based-features) - [Checking Features](#checking-features) + - [Conditional Execution](#conditional-execution) - [In-Memory Cache](#in-memory-cache) +- [Scope](#scope) + - [Default Scope](#default-scope) + - [Identifying Scope](#identifying-scope) - [Events](#events) @@ -40,10 +44,10 @@ php artisan migrate After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to select the default driver and configure the individual drivers. - -## Defining Features + +## Creating Features -A feature definition is simply a Closure that returns the initial value for the specific feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature flag check, which would commonly be the current user. +A feature is a Closure that returns the initial value for the specific feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature check, which would commonly be the currently authenticated user. In this example, we will define a feature for rolling out a new API implementation incrementally to our application's users. @@ -78,24 +82,24 @@ class AppServiceProvider extends ServiceProvider } ``` -As you can see, we have the following rules for our feature definition: +As you can see, we have the following rules for our feature: - All internal team members should be using the new API. - Any high traffic customers should not be using the new API. - Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being activated. -The first time the `new-api` feature is checked for a given user, the result of the definition Closure will be persisted by the underlying driver. This means that the next time the feature is check against the same user, the value will be retrieved from storage rather than decided by the feature's definition. +The first time the `new-api` feature is checked for a given user, the result of the Closure will be persisted by the underlying driver. The next time the feature is checked against the same user, the value will be retrieved from storage and the Closure will not be invoked. -If your feature definition only returns a lottery, you may omit the Closure completely. +If a feature definition only returns a lottery, you may omit the Closure completely. Feature::define('site-redesign', Lottery::odds(1, 1000)); ### Class Based Features -You may also create class based features. Unlike Closure based definitions, there is no need to register class based features in a service provider. +Class based features are also supported. Unlike Closure based definitions, there is no need to register class based features in a service provider. -When create a feature class you will need to implement the `resolve` method: +When creating a feature class you will need to implement the `resolve` method: ```php isInternalTeamMember()) { return true; @@ -126,7 +133,7 @@ class NewApi ## Checking Features -To check if a feature is active, you should use the `isActive` method on the `Feature` facade. By default, features are checked against the currently authenticated user. +To check if a feature is active, you should use the `active` method on the `Feature` facade. By default, features are checked against the currently authenticated user. ```php resolveNewApiResponse($request); } @@ -155,7 +162,7 @@ class PodcastController } ``` -For class based features, you should use class name when checking the features state: +For class based features, you should use the class name when checking the feature: ```php resolveNewApiResponse($request); } @@ -185,15 +192,165 @@ class PodcastController } ``` +There are some additional methods that may be handy when checking if a feature is active or not: + + // Check if all the features are active... + Feature::allAreActive(['billing-v2', 'payments-v2']); + + // Check if any of the features are active... + Feature::someAreActive(['billing-v2', 'payments-v2']); + + // Check if a feature is inactive... + Feature::inactive('billing-v2'); + + // Check if all the features are active... + Feature::allAreInactive(['billing-v2', 'payments-v2']); + + // Check if any of the features are active... + Feature::someAreInactive(['billing-v2', 'payments-v2']); + + +### Conditional Execution + +Pennant offers the ability to conditionally execute a specific code block in a fluent manner. This is achieved via the `when` and `unless` methods. + + $this->resolveNewApiResponse($request), + fn () => $this->resolveLegacyApiResponse($request), + ); + } + + // ... + } + ### In-Memory Cache -When checking the state of a feature, Pennant will create a in-memory cache of the result. If you are using the database driver, this means that re-checking the same feature flag throughout a single request will not trigger subsequent database queries. It also ensures you have a consistent result for the duration of the request. +When checking a feature, Pennant will create a in-memory cache of the result. If you are using the database driver, this means that re-checking the same feature flag throughout a single request will not trigger subsequent database queries. It also ensures you have a consistent result for the duration of the request. If you need to manually flush the in-memory cache, you may use the `flushCache` method on the `Feature` facade. Feature::flushCache(); + +## Scope + +As previous mentioned, by default features are checked against the currently authenticated user. This may not always suit your needs. + +Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want our oldest teams have a slower rollout than our newer teams. + + use App\Models\Team; + use Carbon\Carbon; + use Illuminate\Support\Lottery; + + Feature::define('billing-v2', function (Team $team) { + if ($team->created_at->isAfter(new Carbon('1st Jan, 2023'))) { + return true; + } + + if ($team->created_at->isAfter(new Carbon('1st Jan, 2019'))) { + return Lottery::odds(1 / 100); + } + + return Lottery::odds(1 / 1000); + }); + +You will notice that the Closure receives an instance of the team model. To check if this feature is active for a user's team, you should pass the team to the `for` method on the `Feature` facade: + + use Laravel\Pennant\Feature; + + if (Feature::for($user->team)->active('billing-v2')) { + return redirect()->to('/billing/v2'); + } + + // ... + + +### Default Scope + +It is possible to customize the default scope used when checking features. Suppose all your features are checked against the currently authenticated user's team, rather than having the call `Feature::for($user->team)` on every feature check, you may instead specify the default scope in a service provider. + +```php +user()->team; + }); + + // ... + } +} +``` + +If no scope is provided via the `for` method, all feature checks will now be done against the currently authenticated user's team. + + Feature::active('billing-v2'); + + // Now equivalent to... + + Feature::for($user->team)->active('billing-v2'); + + +### Identifying Scope + +When resolving the value of a feature, you may need to customize the value that is passed to a Pennant driver as the scope for the feature check. To do this, your scope should implement the `FeatureScopeable` contract. + +Imagine you are using two different feature drivers in our application: the built-in database driver and a 3rd party "Flag Rocket" driver. The "Flag Rocket" driver requires a `FlagRocketUser` to identify a user. Implementing the `toFeatureIdentifier` method allows you to customize the scope passed to the Flag Rocket driver. + +```php + $this, + 'flag-rocket' => new FlagRocketUser($this->flag_rocket_id), + }; + } +} +``` + +The Flag Rocket driver will now receive an instance of the `FlagRocketUser` to handle it as needed. + ## Events @@ -201,11 +358,11 @@ Pennant dispatches a few events that may be useful for tracking the feature flag ### `Illuminate\Pennant\Events\RetrievingKnownFeature` -This event is dispatched the first time a known feature is resolved during a request for a specific scope. This may be useful to create and track metrics against the feature flags that are in-use throughout your application. +This event is dispatched the first time a known feature is resolved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are in-use throughout your application. ### `Illuminate\Pennant\Events\RetrievingUnknownFeature` -This event is dispatched the first time an unknown feature is resolved during a request for the specific scope. This may be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. +This event is dispatched the first time an unknown feature is resolved during a request for the specific scope. This event can be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. You may like to listen for this event and report or throw an exception when it occurs. @@ -234,14 +391,8 @@ class EventServiceProvider extends ServiceProvider ### `Illuminate\Pennant\Events\DynamicallyDefiningFeature` -This event is dispatched whenever an unregistered class based feature is being dynamically defined for the first time during a request. - - +This event is dispatched whenever a class based feature is being dynamically checked for the first time during a request. -- events - - known - - unknown - - dynamic - helpers - global From 406b6e30ef7736e68d33fe95bd4de0918a81328e Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 13:00:26 +1100 Subject: [PATCH 0707/2609] wip --- pennant.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/pennant.md b/pennant.md index e4f8eab52ca..1cf048b2e1c 100644 --- a/pennant.md +++ b/pennant.md @@ -7,10 +7,12 @@ - [Class Based Features](#class-based-features) - [Checking Features](#checking-features) - [Conditional Execution](#conditional-execution) + - [Blade Directive](#blade-directive) - [In-Memory Cache](#in-memory-cache) - [Scope](#scope) - [Default Scope](#default-scope) - [Identifying Scope](#identifying-scope) +- [Rich Feature Values](#rich-feature-values) - [Events](#events) @@ -239,6 +241,19 @@ Pennant offers the ability to conditionally execute a specific code block in a f // ... } + +### Blade Directive + +To make checking features in Blade files a seamless experience, Pennant also offers a `@feature` directive. + +```blade +@feature('billing-2.0') + +@else + +@endfeature +``` + ### In-Memory Cache @@ -351,6 +366,32 @@ class User extends Model implements FeatureScopeable The Flag Rocket driver will now receive an instance of the `FlagRocketUser` to handle it as needed. + +## Rich Feature Values + +Until now, we have shown features as being in a binary state, that is they are either active or inactive, but Pennant allows you to store rich values as well. + +Imagine you are testing 3 new colors for the "Buy now" button of your application. Instead of returning `true` or `false` from the feature definition, you may instead return a string. + + use Illuminate\Support\Arr; + use Laravel\Pennant\Feature; + + Feature::define('checkout-button', function (User $user) { + return Arr::random([ + 'blue-sapphire', + 'seafoam-green', + 'tart-orange', + ]); + }); + +You may retrieve the value of the `'checkout-button'` feature using the `value` method on the `Feature` facade. + + $color = Feature::value('checkout-button'); + +- Blade + +> **Note** You may return an array as a rich value. The value returned will be JSON encoded when stored and JSON decoded when retrieved. + ## Events From 90737285548d845abbf65649eef2b4d4c313bbbb Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 13:08:06 +1100 Subject: [PATCH 0708/2609] wip --- pennant.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pennant.md b/pennant.md index 1cf048b2e1c..c1cd38922b2 100644 --- a/pennant.md +++ b/pennant.md @@ -388,9 +388,19 @@ You may retrieve the value of the `'checkout-button'` feature using the `value` $color = Feature::value('checkout-button'); -- Blade +The Blade directive also makes it easy to conditionally render content based on the current value of the feature. -> **Note** You may return an array as a rich value. The value returned will be JSON encoded when stored and JSON decoded when retrieved. +```blade +@feature('checkout-button', 'blue-sapphire') + +@elsefeature('checkout-button', 'seafoam-green') + +@elsefeature('checkout-button', 'tart-orange') + +@endfeature +``` + +> **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. ## Events From b876f88f5b2dd3c83953474e9ba0a34adb1762e9 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 13:09:58 +1100 Subject: [PATCH 0709/2609] wip --- pennant.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pennant.md b/pennant.md index c1cd38922b2..ab3fa0518c7 100644 --- a/pennant.md +++ b/pennant.md @@ -13,6 +13,7 @@ - [Default Scope](#default-scope) - [Identifying Scope](#identifying-scope) - [Rich Feature Values](#rich-feature-values) +- [Updating Values](#updating-values) - [Events](#events) @@ -402,6 +403,9 @@ The Blade directive also makes it easy to conditionally render content based on > **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. + +## Updating Values + ## Events From b5e21de7cee6d769a757f16c621cc43453b1d320 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 14:43:55 +1100 Subject: [PATCH 0710/2609] wip --- pennant.md | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/pennant.md b/pennant.md index ab3fa0518c7..6e00d7b263c 100644 --- a/pennant.md +++ b/pennant.md @@ -14,6 +14,7 @@ - [Identifying Scope](#identifying-scope) - [Rich Feature Values](#rich-feature-values) - [Updating Values](#updating-values) + - [Bulk Updates](#bulk-updates) - [Events](#events) @@ -198,19 +199,19 @@ class PodcastController There are some additional methods that may be handy when checking if a feature is active or not: // Check if all the features are active... - Feature::allAreActive(['billing-v2', 'payments-v2']); + Feature::allAreActive(['new-api', 'new-admin-design']); // Check if any of the features are active... - Feature::someAreActive(['billing-v2', 'payments-v2']); + Feature::someAreActive(['new-api', 'new-admin-design']); // Check if a feature is inactive... - Feature::inactive('billing-v2'); + Feature::inactive('new-api'); // Check if all the features are active... - Feature::allAreInactive(['billing-v2', 'payments-v2']); + Feature::allAreInactive(['new-api', 'new-admin-design']); // Check if any of the features are active... - Feature::someAreInactive(['billing-v2', 'payments-v2']); + Feature::someAreInactive(['new-api', 'new-admin-design']); ### Conditional Execution @@ -377,7 +378,7 @@ Imagine you are testing 3 new colors for the "Buy now" button of your applicatio use Illuminate\Support\Arr; use Laravel\Pennant\Feature; - Feature::define('checkout-button', function (User $user) { + Feature::define('purchase-button', function (User $user) { return Arr::random([ 'blue-sapphire', 'seafoam-green', @@ -385,18 +386,18 @@ Imagine you are testing 3 new colors for the "Buy now" button of your applicatio ]); }); -You may retrieve the value of the `'checkout-button'` feature using the `value` method on the `Feature` facade. +You may retrieve the value of the `'purchase-button'` feature using the `value` method on the `Feature` facade. - $color = Feature::value('checkout-button'); + $color = Feature::value('purchase-button'); The Blade directive also makes it easy to conditionally render content based on the current value of the feature. ```blade -@feature('checkout-button', 'blue-sapphire') +@feature('purchase-button', 'blue-sapphire') -@elsefeature('checkout-button', 'seafoam-green') +@elsefeature('purchase-button', 'seafoam-green') -@elsefeature('checkout-button', 'tart-orange') +@elsefeature('purchase-button', 'tart-orange') @endfeature ``` @@ -406,6 +407,23 @@ The Blade directive also makes it easy to conditionally render content based on ## Updating Values +When a feature's value is resolved for the first time, the underlying driver will persist the result. This is handy to ensure a consistent experience for your users across requests, but often you will need to make changes to the feature's persisted value. You may use the `activate` and `deactivate` methods to toggle a feature on or off. + + // Activate the feature for the default scope... + Feature::activate('new-api'); + + // Deactivate the feature for the given scope... + Feature::for($user->team)->deactivate('billing-v2'); + +It is also possible to manually set rich value for a feature by passing through a second argument to the `activate` method. + + Feature::activate('purchase-button', 'seafoam-green'); + + +### Bulk Updates + + + ## Events From 456814b0a821144e2caaff57ec838a3d37130754 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 15:14:36 +1100 Subject: [PATCH 0711/2609] wip --- pennant.md | 75 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/pennant.md b/pennant.md index 6e00d7b263c..6b8583a2c0e 100644 --- a/pennant.md +++ b/pennant.md @@ -15,6 +15,8 @@ - [Rich Feature Values](#rich-feature-values) - [Updating Values](#updating-values) - [Bulk Updates](#bulk-updates) + - [Purging Features](#purging-features) +- [Eager Loading](#eager-loading) - [Events](#events) @@ -407,7 +409,7 @@ The Blade directive also makes it easy to conditionally render content based on ## Updating Values -When a feature's value is resolved for the first time, the underlying driver will persist the result. This is handy to ensure a consistent experience for your users across requests, but often you will need to make changes to the feature's persisted value. You may use the `activate` and `deactivate` methods to toggle a feature on or off. +When a feature's value is resolved for the first time, the underlying driver will persist the result. This is handy to ensure a consistent experience for your users across requests, but you may want to manually update the feature's persisted value. To achieve this you can use the `activate` and `deactivate` methods to toggle a feature on or off. // Activate the feature for the default scope... Feature::activate('new-api'); @@ -419,10 +421,59 @@ It is also possible to manually set rich value for a feature by passing through Feature::activate('purchase-button', 'seafoam-green'); +If you just wish to forget the current value for a feature, so that the next time it is checked the value is resolved from the feature definition, you may use the `forget` method. + + Feature::forget('purchase-button'); + ### Bulk Updates +To make bulk updates you may use the `activateForEveryone` and `deactivateForEveryone` methods. + +Say you are now confident in the `'new-api'` features stability and the best `'purchase-button'` color to be using, you can update the stored value for all users: + + Feature::activateForEveryone('new-api'); + + Feature::activateForEveryone('purchase-button', 'seafoam-green'); + + +Alternatively, if you are having issues with a feature rollout and need to disable to feature for all users, you can disable to feature for all users: + + Feature::deactivateForEveryone('new-api'); + +> **Note** This will only update the stored values. You may also need to update the feature definition. + + +### Purging Features + + +## Eager Loading + +Although Pennant keeps an in-memory cache of all resolved feature values for a single request, it is still possible to run into performance issues. To alleviate this, Pennant offers the ability to eager load feature values. + +Imagine that we are checking if a feature is active within a loop: + + foreach ($users as $user) { + if (Feature::for($user)->active('notifications-beta')) { + $user->notify(new RegistrationSuccess); + } + } +Assuming we are using the database driver, we are going to be hitting the database for every user in the loop - potentially hundreds of times. With eager loading we can remove this potential performance bottleneck. + + Feature::for($users)->load('notifications-beta'); + + foreach ($users as $user) { + if (Feature::for($user)->active('notifications-beta')) { + $user->notify(new RegistrationSuccess); + } + } + +Once in place we no longer trigger any database queries inside the loop. + +To load values only when it has not already been loaded, use the `loadMissing` method: + + Feature::for($users)->loadMissing('notifications-beta'); ## Events @@ -465,25 +516,3 @@ class EventServiceProvider extends ServiceProvider ### `Illuminate\Pennant\Events\DynamicallyDefiningFeature` This event is dispatched whenever a class based feature is being dynamically checked for the first time during a request. - - -- helpers - - global - - blade - -- bulk updates - - purge - -- customising an objects scope -- default scope - -- eager loading + loadMissing - -- attaching feature to objects (this should be improved) - $user->feature('foo')->active(); - -- using the array driver for lottery based features. -- using the array driver for package features - -- array driver for testing -- custom drivers From 64574bb72daf8e7fea6e8b082b27a14b2d2208b1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 15:22:26 +1100 Subject: [PATCH 0712/2609] wip --- pennant.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pennant.md b/pennant.md index 6b8583a2c0e..14ba99170a2 100644 --- a/pennant.md +++ b/pennant.md @@ -446,6 +446,22 @@ Alternatively, if you are having issues with a feature rollout and need to disab ### Purging Features +It can be useful to purge an entire feature from storage, whether you have removed the feature from your system or you need have made adjustments to the features definition that you would like to rollout to all users. + +You may completely remove all persisted values for a feature using the `purge` method on the `Feature` facade. + + Feature::purge('new-api'); + +If you would like to purge _all_ features from storage, you may do so by calling `purge` without any arguments. + + Feature::purge(); + +As it can be useful to do this as part of your deployment pipeline, we have also included an artisan command to help out. + +```sh +php artisan pennant:purge new-api +``` + ## Eager Loading From ba5eb0dd00592ac2d4a46d8355e406d8678335d9 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 16:00:39 +1100 Subject: [PATCH 0713/2609] wip --- pennant.md | 110 ++++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/pennant.md b/pennant.md index 14ba99170a2..e777a0820e7 100644 --- a/pennant.md +++ b/pennant.md @@ -13,10 +13,10 @@ - [Default Scope](#default-scope) - [Identifying Scope](#identifying-scope) - [Rich Feature Values](#rich-feature-values) +- [Eager Loading](#eager-loading) - [Updating Values](#updating-values) - [Bulk Updates](#bulk-updates) - [Purging Features](#purging-features) -- [Eager Loading](#eager-loading) - [Events](#events) @@ -53,7 +53,7 @@ After publishing Pennant's assets, its configuration file will be located at `co ## Creating Features -A feature is a Closure that returns the initial value for the specific feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature check, which would commonly be the currently authenticated user. +When creating a feature you will want to provide a name and a Closure that returns the initial value for the specific feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature check, which would commonly be the currently authenticated user. In this example, we will define a feature for rolling out a new API implementation incrementally to our application's users. @@ -92,7 +92,7 @@ As you can see, we have the following rules for our feature: - All internal team members should be using the new API. - Any high traffic customers should not be using the new API. -- Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being activated. +- Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being active. The first time the `new-api` feature is checked for a given user, the result of the Closure will be persisted by the underlying driver. The next time the feature is checked against the same user, the value will be retrieved from storage and the Closure will not be invoked. @@ -103,7 +103,7 @@ If a feature definition only returns a lottery, you may omit the Closure complet ### Class Based Features -Class based features are also supported. Unlike Closure based definitions, there is no need to register class based features in a service provider. +Class based features are also supported. Unlike Closure based definitions, there is no need to register a class based feature in a service provider. When creating a feature class you will need to implement the `resolve` method: @@ -198,22 +198,22 @@ class PodcastController } ``` -There are some additional methods that may be handy when checking if a feature is active or not: +There are some additional methods that may come in handy when checking if a feature is active or not: // Check if all the features are active... - Feature::allAreActive(['new-api', 'new-admin-design']); + Feature::allAreActive(['new-api', 'site-redesign']); // Check if any of the features are active... - Feature::someAreActive(['new-api', 'new-admin-design']); + Feature::someAreActive(['new-api', 'site-redesign']); // Check if a feature is inactive... Feature::inactive('new-api'); // Check if all the features are active... - Feature::allAreInactive(['new-api', 'new-admin-design']); + Feature::allAreInactive(['new-api', 'site-redesign']); // Check if any of the features are active... - Feature::someAreInactive(['new-api', 'new-admin-design']); + Feature::someAreInactive(['new-api', 'site-redesign']); ### Conditional Execution @@ -251,7 +251,7 @@ Pennant offers the ability to conditionally execute a specific code block in a f To make checking features in Blade files a seamless experience, Pennant also offers a `@feature` directive. ```blade -@feature('billing-2.0') +@feature('site-redesign') @else @@ -270,9 +270,9 @@ If you need to manually flush the in-memory cache, you may use the `flushCache` ## Scope -As previous mentioned, by default features are checked against the currently authenticated user. This may not always suit your needs. +As previous mentioned, features are checked against the currently authenticated user by default. This may not always suit your needs. For one off feature checks, it is possible to specify the scope you would like to check the feature against. -Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want our oldest teams have a slower rollout than our newer teams. +Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want our oldest teams have a slower rollout than the newer teams. Your feature Closure might look something like the following: use App\Models\Team; use Carbon\Carbon; @@ -290,7 +290,7 @@ Imagine you have built a new billing experience that you are rolling out to enti return Lottery::odds(1 / 1000); }); -You will notice that the Closure receives an instance of the team model. To check if this feature is active for a user's team, you should pass the team to the `for` method on the `Feature` facade: +You will notice that the Closure we have defined is not expecting a `User`, but is instead expecting a `Team` model. To check if this feature is active for a user's team, you should pass the team to the `for` method before calling `active`. use Laravel\Pennant\Feature; @@ -303,7 +303,7 @@ You will notice that the Closure receives an instance of the team model. To chec ### Default Scope -It is possible to customize the default scope used when checking features. Suppose all your features are checked against the currently authenticated user's team, rather than having the call `Feature::for($user->team)` on every feature check, you may instead specify the default scope in a service provider. +It is also possible to customize the default scope used when checking features. Suppose all your features are checked against the currently authenticated user's team; rather than having the call `Feature::for($user->team)` on every feature check, you may instead specify the default scope to use in a service provider. ```php team)->active('billing-v2'); ### Identifying Scope -When resolving the value of a feature, you may need to customize the value that is passed to a Pennant driver as the scope for the feature check. To do this, your scope should implement the `FeatureScopeable` contract. +Implementing the `FeatureScopeable` contract on your scope objects allows you to customize how the scope is resolved for the underlying Pennant drivers. -Imagine you are using two different feature drivers in our application: the built-in database driver and a 3rd party "Flag Rocket" driver. The "Flag Rocket" driver requires a `FlagRocketUser` to identify a user. Implementing the `toFeatureIdentifier` method allows you to customize the scope passed to the Flag Rocket driver. +Imagine you are using two different feature drivers in a single application, perhaps the built-in database driver and a 3rd party "Flag Rocket" driver. The "Flag Rocket" driver requires a `FlagRocketUser` to identify a user while the database driver can handle an Eloquent model. Implementing the `toFeatureIdentifier` method allows you to customize the scope passed to each driver. ```php $this, - 'flag-rocket' => new FlagRocketUser($this->flag_rocket_id), + 'flag-rocket' => FlagRocketUser::fromId($this->flag_rocket_id), }; } } ``` -The Flag Rocket driver will now receive an instance of the `FlagRocketUser` to handle it as needed. - ## Rich Feature Values @@ -388,7 +386,7 @@ Imagine you are testing 3 new colors for the "Buy now" button of your applicatio ]); }); -You may retrieve the value of the `'purchase-button'` feature using the `value` method on the `Feature` facade. +You may retrieve the value of the `'purchase-button'` feature using the `value` method. $color = Feature::value('purchase-button'); @@ -406,10 +404,41 @@ The Blade directive also makes it easy to conditionally render content based on > **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. + +## Eager Loading + +Although Pennant keeps an in-memory cache of all resolved features for a single request, it is still possible to run into performance issues. To alleviate this, Pennant offers the ability to eager load feature values. + +Imagine that we are checking if a feature is active within a loop: + + foreach ($users as $user) { + if (Feature::for($user)->active('notifications-beta')) { + $user->notify(new RegistrationSuccess); + } + } + +Assuming we are using the database driver, we are going to be hitting the database for every user in the loop - potentially hundreds of queries. With eager loading we can remove this potential performance bottleneck. + + Feature::for($users)->load('notifications-beta'); + + foreach ($users as $user) { + if (Feature::for($user)->active('notifications-beta')) { + $user->notify(new RegistrationSuccess); + } + } + +Once in place we no longer trigger any database queries inside the loop. + +To load values only when it has not already been loaded, use the `loadMissing` method: + + Feature::for($users)->loadMissing('notifications-beta'); + ## Updating Values -When a feature's value is resolved for the first time, the underlying driver will persist the result. This is handy to ensure a consistent experience for your users across requests, but you may want to manually update the feature's persisted value. To achieve this you can use the `activate` and `deactivate` methods to toggle a feature on or off. +When a feature's value is resolved for the first time, the underlying driver will persist the result. This is handy to ensure a consistent experience for your users across requests, but you may also want to manually update the feature's persisted value. + +To achieve this you can use the `activate` and `deactivate` methods to toggle a feature on or off. // Activate the feature for the default scope... Feature::activate('new-api'); @@ -430,18 +459,18 @@ If you just wish to forget the current value for a feature, so that the next tim To make bulk updates you may use the `activateForEveryone` and `deactivateForEveryone` methods. -Say you are now confident in the `'new-api'` features stability and the best `'purchase-button'` color to be using, you can update the stored value for all users: +Say you are now confident in the `'new-api'` feature's stability and have landed on the best `'purchase-button'` color to be using, you can update the stored value for all users accordingly. Feature::activateForEveryone('new-api'); Feature::activateForEveryone('purchase-button', 'seafoam-green'); -Alternatively, if you are having issues with a feature rollout and need to disable to feature for all users, you can disable to feature for all users: +Alternatively, if you are having issues with a feature rollout you may need to deactivate the feature for all users instead. Feature::deactivateForEveryone('new-api'); -> **Note** This will only update the stored values. You may also need to update the feature definition. +> **Note** This will only update the existing stored values. You may also need to update the feature definition in your code. ### Purging Features @@ -462,35 +491,6 @@ As it can be useful to do this as part of your deployment pipeline, we have also php artisan pennant:purge new-api ``` - -## Eager Loading - -Although Pennant keeps an in-memory cache of all resolved feature values for a single request, it is still possible to run into performance issues. To alleviate this, Pennant offers the ability to eager load feature values. - -Imagine that we are checking if a feature is active within a loop: - - foreach ($users as $user) { - if (Feature::for($user)->active('notifications-beta')) { - $user->notify(new RegistrationSuccess); - } - } - -Assuming we are using the database driver, we are going to be hitting the database for every user in the loop - potentially hundreds of times. With eager loading we can remove this potential performance bottleneck. - - Feature::for($users)->load('notifications-beta'); - - foreach ($users as $user) { - if (Feature::for($user)->active('notifications-beta')) { - $user->notify(new RegistrationSuccess); - } - } - -Once in place we no longer trigger any database queries inside the loop. - -To load values only when it has not already been loaded, use the `loadMissing` method: - - Feature::for($users)->loadMissing('notifications-beta'); - ## Events From 1fc41342e03ccaed722cd780620d5d5719fdb6dc Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 16:03:58 +1100 Subject: [PATCH 0714/2609] wip --- pennant.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pennant.md b/pennant.md index e777a0820e7..f2c61921148 100644 --- a/pennant.md +++ b/pennant.md @@ -94,7 +94,7 @@ As you can see, we have the following rules for our feature: - Any high traffic customers should not be using the new API. - Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being active. -The first time the `new-api` feature is checked for a given user, the result of the Closure will be persisted by the underlying driver. The next time the feature is checked against the same user, the value will be retrieved from storage and the Closure will not be invoked. +The first time the `new-api` feature is checked for a given user, the result of the Closure will be stored by the underlying driver. The next time the feature is checked against the same user, the value will be retrieved from storage and the Closure will not be invoked. If a feature definition only returns a lottery, you may omit the Closure completely. @@ -436,7 +436,7 @@ To load values only when it has not already been loaded, use the `loadMissing` m ## Updating Values -When a feature's value is resolved for the first time, the underlying driver will persist the result. This is handy to ensure a consistent experience for your users across requests, but you may also want to manually update the feature's persisted value. +When a feature's value is resolved for the first time, the underlying driver will store the result. This is handy to ensure a consistent experience for your users across requests, but you may also want to manually update the feature's stored value. To achieve this you can use the `activate` and `deactivate` methods to toggle a feature on or off. @@ -475,9 +475,9 @@ Alternatively, if you are having issues with a feature rollout you may need to d ### Purging Features -It can be useful to purge an entire feature from storage, whether you have removed the feature from your system or you need have made adjustments to the features definition that you would like to rollout to all users. +It can be useful to purge an entire feature from storage, whether you have removed the feature from your system or you have made adjustments to the features definition that you would like to freshly rollout to all users. -You may completely remove all persisted values for a feature using the `purge` method on the `Feature` facade. +You may completely remove all stored values for a feature using the `purge` method. Feature::purge('new-api'); @@ -485,7 +485,7 @@ If you would like to purge _all_ features from storage, you may do so by calling Feature::purge(); -As it can be useful to do this as part of your deployment pipeline, we have also included an artisan command to help out. +As it can be useful to do this as part of your deployment pipeline, we have also included a handy Artisan. ```sh php artisan pennant:purge new-api @@ -494,7 +494,7 @@ php artisan pennant:purge new-api ## Events -Pennant dispatches a few events that may be useful for tracking the feature flags throughout your application. +Pennant dispatches a few events that may be useful for tracking feature flags throughout your application. ### `Illuminate\Pennant\Events\RetrievingKnownFeature` From ba02bb0b7d5f9916272c40c4a9d191a8487b8f89 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 16:50:43 +1100 Subject: [PATCH 0715/2609] wip --- pennant.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennant.md b/pennant.md index f2c61921148..70935f7bf7c 100644 --- a/pennant.md +++ b/pennant.md @@ -48,12 +48,12 @@ php artisan migrate ## Configuration -After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to select the default driver and configure the individual drivers. +After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to select the default driver and more. ## Creating Features -When creating a feature you will want to provide a name and a Closure that returns the initial value for the specific feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature check, which would commonly be the currently authenticated user. +When creating a feature you will need to provide a name and a Closure. The Closure should return the initial value for the feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature check, which would commonly be the currently authenticated user. In this example, we will define a feature for rolling out a new API implementation incrementally to our application's users. @@ -498,11 +498,11 @@ Pennant dispatches a few events that may be useful for tracking feature flags th ### `Illuminate\Pennant\Events\RetrievingKnownFeature` -This event is dispatched the first time a known feature is resolved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are in-use throughout your application. +This event is dispatched the first time a known feature is retrieved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are in-use throughout your application. ### `Illuminate\Pennant\Events\RetrievingUnknownFeature` -This event is dispatched the first time an unknown feature is resolved during a request for the specific scope. This event can be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. +This event is dispatched the first time an unknown feature is retrieved during a request for the specific scope. This event can be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. You may like to listen for this event and report or throw an exception when it occurs. From 4c64e38f44f9d2630bbdb0b437e3f56eee0a6382 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 16:54:13 +1100 Subject: [PATCH 0716/2609] wip --- pennant.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennant.md b/pennant.md index 70935f7bf7c..5ba282c000f 100644 --- a/pennant.md +++ b/pennant.md @@ -55,7 +55,7 @@ After publishing Pennant's assets, its configuration file will be located at `co When creating a feature you will need to provide a name and a Closure. The Closure should return the initial value for the feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature check, which would commonly be the currently authenticated user. -In this example, we will define a feature for rolling out a new API implementation incrementally to our application's users. +In this example, we will define a feature for incrementally rolling out a new API to our application's users. ```php From 5de2ee6bc3538b5cd60cc13c55a6ef932bbe0732 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 17:06:47 +1100 Subject: [PATCH 0717/2609] wip --- pennant.md | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/pennant.md b/pennant.md index 5ba282c000f..cdf66d15866 100644 --- a/pennant.md +++ b/pennant.md @@ -248,20 +248,20 @@ Pennant offers the ability to conditionally execute a specific code block in a f ### Blade Directive -To make checking features in Blade files a seamless experience, Pennant also offers a `@feature` directive. +To make checking features in Blade a seamless experience, Pennant also offers a `@feature` directive. ```blade @feature('site-redesign') - + @else - + @endfeature ``` ### In-Memory Cache -When checking a feature, Pennant will create a in-memory cache of the result. If you are using the database driver, this means that re-checking the same feature flag throughout a single request will not trigger subsequent database queries. It also ensures you have a consistent result for the duration of the request. +When checking a feature, Pennant will create an in-memory cache of the result. If you are using the database driver, this means that re-checking the same feature flag throughout a single request will not trigger additional database queries. It also ensures you have a consistent result for the duration of the request. If you need to manually flush the in-memory cache, you may use the `flushCache` method on the `Feature` facade. @@ -270,9 +270,9 @@ If you need to manually flush the in-memory cache, you may use the `flushCache` ## Scope -As previous mentioned, features are checked against the currently authenticated user by default. This may not always suit your needs. For one off feature checks, it is possible to specify the scope you would like to check the feature against. +As previously mentioned, features are checked against the currently authenticated user by default. This may not always suit your needs. For one off feature checks, it is possible to specify the scope you would like to check the feature against. -Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want our oldest teams have a slower rollout than the newer teams. Your feature Closure might look something like the following: +Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want the oldest teams have a slower rollout than the newer teams. Your feature Closure might look something like the following: use App\Models\Team; use Carbon\Carbon; @@ -290,7 +290,7 @@ Imagine you have built a new billing experience that you are rolling out to enti return Lottery::odds(1 / 1000); }); -You will notice that the Closure we have defined is not expecting a `User`, but is instead expecting a `Team` model. To check if this feature is active for a user's team, you should pass the team to the `for` method before calling `active`. +You will notice that the Closure we have defined is not expecting a `User`, but is instead expecting a `Team` model. To check if this feature is active for a user's team, you should pass the team to the `for` method. use Laravel\Pennant\Feature; @@ -303,7 +303,7 @@ You will notice that the Closure we have defined is not expecting a `User`, but ### Default Scope -It is also possible to customize the default scope used when checking features. Suppose all your features are checked against the currently authenticated user's team; rather than having the call `Feature::for($user->team)` on every feature check, you may instead specify the default scope to use in a service provider. +It is also possible to customize the default scope used when checking features. Suppose all your features are checked against the currently authenticated user's team; rather than having to call `Feature::for($user->team)` on every feature check, you may instead specify the default scope to use in a service provider. ```php ### Identifying Scope -Implementing the `FeatureScopeable` contract on your scope objects allows you to customize how the scope is resolved for the underlying Pennant drivers. +Implementing the `FeatureScopeable` contract on your scope objects allows you to customize what is passed to the underlying Pennant drivers. -Imagine you are using two different feature drivers in a single application, perhaps the built-in database driver and a 3rd party "Flag Rocket" driver. The "Flag Rocket" driver requires a `FlagRocketUser` to identify a user while the database driver can handle an Eloquent model. Implementing the `toFeatureIdentifier` method allows you to customize the scope passed to each driver. +Imagine you are using two different feature drivers in a single application, perhaps the built-in database driver and a 3rd party "Flag Rocket" driver. The "Flag Rocket" driver requires a `FlagRocketUser` to identify a user. The database driver, however, can handle an Eloquent model. Implementing the `toFeatureIdentifier` method allows you to customize the scope passed to each driver. ```php + @elsefeature('purchase-button', 'seafoam-green') - + @elsefeature('purchase-button', 'tart-orange') - + @endfeature ``` @@ -431,7 +431,11 @@ Once in place we no longer trigger any database queries inside the loop. To load values only when it has not already been loaded, use the `loadMissing` method: - Feature::for($users)->loadMissing('notifications-beta'); + Feature::for($users)->loadMissing([ + 'new-api', + 'purchase-button', + 'notifications-beta', + ]); ## Updating Values @@ -450,7 +454,7 @@ It is also possible to manually set rich value for a feature by passing through Feature::activate('purchase-button', 'seafoam-green'); -If you just wish to forget the current value for a feature, so that the next time it is checked the value is resolved from the feature definition, you may use the `forget` method. +If you wish to forget the stored value for a feature, so that the next time it is checked the value is resolved from the feature definition, you may use the `forget` method. Feature::forget('purchase-button'); @@ -475,7 +479,7 @@ Alternatively, if you are having issues with a feature rollout you may need to d ### Purging Features -It can be useful to purge an entire feature from storage, whether you have removed the feature from your system or you have made adjustments to the features definition that you would like to freshly rollout to all users. +It can be useful to purge an entire feature from storage, whether you have removed the feature from your system or you have made adjustments to the features definition that you would like to rollout fresh to all users. You may completely remove all stored values for a feature using the `purge` method. From 1b19a6ee46a8771ec54f592b0240a4339b27dff0 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 21:23:20 +1100 Subject: [PATCH 0718/2609] wip --- documentation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation.md b/documentation.md index 54cdd558ee3..8201ee66cb2 100644 --- a/documentation.md +++ b/documentation.md @@ -88,6 +88,7 @@ - [Mix](/docs/{{version}}/mix) - [Octane](/docs/{{version}}/octane) - [Passport](/docs/{{version}}/passport) + - [Pennant](/docs/{{version}}/pennant) - [Pint](/docs/{{version}}/pint) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) From 0ac2b66b1e6b634e55d90c683d347727da815ebe Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 21:36:32 +1100 Subject: [PATCH 0719/2609] wip --- pennant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index cdf66d15866..fd702a6ae83 100644 --- a/pennant.md +++ b/pennant.md @@ -272,7 +272,7 @@ If you need to manually flush the in-memory cache, you may use the `flushCache` As previously mentioned, features are checked against the currently authenticated user by default. This may not always suit your needs. For one off feature checks, it is possible to specify the scope you would like to check the feature against. -Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want the oldest teams have a slower rollout than the newer teams. Your feature Closure might look something like the following: +Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want the oldest teams to have a slower rollout than the newer teams. Your feature Closure might look something like the following: use App\Models\Team; use Carbon\Carbon; From 196abe3f3c96dcdfe4cdc10169f1f72813bcc23c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 21:59:44 +1100 Subject: [PATCH 0720/2609] wip --- pennant.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennant.md b/pennant.md index fd702a6ae83..6406081c751 100644 --- a/pennant.md +++ b/pennant.md @@ -278,7 +278,7 @@ Imagine you have built a new billing experience that you are rolling out to enti use Carbon\Carbon; use Illuminate\Support\Lottery; - Feature::define('billing-v2', function (Team $team) { + Feature::define('billing-v2', function (Team $team): mixed { if ($team->created_at->isAfter(new Carbon('1st Jan, 2023'))) { return true; } @@ -419,7 +419,7 @@ Imagine that we are checking if a feature is active within a loop: Assuming we are using the database driver, we are going to be hitting the database for every user in the loop - potentially hundreds of queries. With eager loading we can remove this potential performance bottleneck. - Feature::for($users)->load('notifications-beta'); + Feature::for($users)->load(['notifications-beta']); foreach ($users as $user) { if (Feature::for($user)->active('notifications-beta')) { From e58f08fb2eb9c2078401bb4109c88ee758ebba9b Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 Feb 2023 22:09:11 +1100 Subject: [PATCH 0721/2609] wip --- pennant.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennant.md b/pennant.md index 6406081c751..cddf0ec59b7 100644 --- a/pennant.md +++ b/pennant.md @@ -278,7 +278,7 @@ Imagine you have built a new billing experience that you are rolling out to enti use Carbon\Carbon; use Illuminate\Support\Lottery; - Feature::define('billing-v2', function (Team $team): mixed { + Feature::define('billing-v2', function (Team $team) { if ($team->created_at->isAfter(new Carbon('1st Jan, 2023'))) { return true; } @@ -386,7 +386,7 @@ Imagine you are testing 3 new colors for the "Buy now" button of your applicatio ]); }); -You may retrieve the value of the `'purchase-button'` feature using the `value` method. +You may retrieve the value of the `purchase-button` feature using the `value` method. $color = Feature::value('purchase-button'); @@ -463,7 +463,7 @@ If you wish to forget the stored value for a feature, so that the next time it i To make bulk updates you may use the `activateForEveryone` and `deactivateForEveryone` methods. -Say you are now confident in the `'new-api'` feature's stability and have landed on the best `'purchase-button'` color to be using, you can update the stored value for all users accordingly. +Say you are now confident in the `new-api` feature's stability and have landed on the best `'purchase-button'` color to be using, you can update the stored value for all users accordingly. Feature::activateForEveryone('new-api'); From fc23c8580d39d86901e9b2ce55940bb5c94cd91b Mon Sep 17 00:00:00 2001 From: emargareten <46111162+emargareten@users.noreply.github.com> Date: Thu, 9 Feb 2023 17:30:29 +0200 Subject: [PATCH 0722/2609] Document usage of `$tablesToTruncate` when using `DatabaseTruncation` (#8533) * Update dusk.md * Update dusk.md * Update dusk.md --------- Co-authored-by: Taylor Otwell --- dusk.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index ba9dd6c4d6f..c0e70e8762a 100644 --- a/dusk.md +++ b/dusk.md @@ -194,7 +194,16 @@ The `DatabaseTruncation` trait will migrate your database on the first test in o use DatabaseTruncation; } -By default, this trait will not truncate the `migrations` table. If you would like to further customize the tables that are excluded from truncation, you may define an `$exceptTables` property on your test class: +By default, this trait will truncate all tables except the `migrations` table. If you would like to customize the tables that should be truncated, you may define a `$tablesToTruncate` property on your test class: + + /** + * Indicates which tables should be truncated. + * + * @var array + */ + protected $tablesToTruncate = ['users']; + +Alternatively, you may define an `$exceptTables` property on your test class to specify which tables should be excluded from truncation: /** * Indicates which tables should be excluded from truncation. From 8a845ba47d02d78083277a9ee547c431e55acf87 Mon Sep 17 00:00:00 2001 From: Jason McCreary Date: Thu, 9 Feb 2023 13:19:04 -0500 Subject: [PATCH 0723/2609] Clarify MySQL credentials (#8534) * Clarify MySQL credentials * Update sail.md --------- Co-authored-by: Taylor Otwell --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 3b6ae08a697..247bdd97422 100644 --- a/sail.md +++ b/sail.md @@ -219,7 +219,7 @@ In addition, the first time the MySQL container starts, it will create two datab 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. +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 and the access credentials correspond to the values of your `DB_USERNAME` and `DB_PASSWORD` environment variables. Or, you may connect as the `root` user, which also utilizes the value of your `DB_PASSWORD` environment variable as its password. ### Redis From ea46a18118a56cb403efb5ef8f7ed7b55f74f12a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Feb 2023 13:36:04 -0600 Subject: [PATCH 0724/2609] fixes --- pennant.md | 213 ++++++++++++++++++++++++++--------------------------- 1 file changed, 106 insertions(+), 107 deletions(-) diff --git a/pennant.md b/pennant.md index cddf0ec59b7..97c2c29298e 100644 --- a/pennant.md +++ b/pennant.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Installation](#installation) - [Configuration](#configuration) -- [Creating Features](#creating-features) +- [Defining Features](#defining-features) - [Class Based Features](#class-based-features) - [Checking Features](#checking-features) - [Conditional Execution](#conditional-execution) @@ -22,7 +22,7 @@ ## Introduction -[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flag package, without any cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. +[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flag package - without the cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. ## Installation @@ -39,7 +39,7 @@ Next, you should publish the Pennant configuration and migration files using the php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider" ``` -Finally, you should run the database migrations. This will create a `features` table that Pennant uses to power the database driver: +Finally, you should run your application's database migrations. This will create a `features` table that Pennant uses to power its `database` driver: ```shell php artisan migrate @@ -48,23 +48,26 @@ php artisan migrate ## Configuration -After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to select the default driver and more. +After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to specify the default storage mechanism that will be used by Pennant to store resolved feature flag values. - -## Creating Features +Pennant includes support for storing resolved feature flag values in an in-memory array via the `array` driver. Or, Pennant can store resolved feature flag values persistently in a relational database via the `database` driver, which is the default storage mechanism used by Pennant. -When creating a feature you will need to provide a name and a Closure. The Closure should return the initial value for the feature. Typically, features are defined in a service provider using the `Feature` facade. The Closure will be passed the "scope" for the feature check, which would commonly be the currently authenticated user. + +## Defining Features -In this example, we will define a feature for incrementally rolling out a new API to our application's users. +To define a feature, you may use the `define` method offered by the `Feature` facade. You will need to provide a name for the feature, as well as a closure that will be invoked to resolve the feature's initial value. + +Typically, features are defined in a service provider using the `Feature` facade. The closure will receive the "scope" for the feature check. Most commonly, the scope is the currently authenticated user. In this example, we will define a feature for incrementally rolling out a new API to our application's users: ```php isInternalTeamMember()) { - return true; - } - - if ($user->isHighTrafficCustomer()) { - return false; - } - - return Lottery::odds(1 / 100); + Feature::define('new-api', fn (User $user) => match (true) { + $user->isInternalTeamMember() => true, + $user->isHighTrafficCustomer() => false, + default => Lottery::odds(1 / 100), }); } } @@ -92,20 +89,20 @@ As you can see, we have the following rules for our feature: - All internal team members should be using the new API. - Any high traffic customers should not be using the new API. -- Otherwise the feature should be randomly assigned to users with a 1 in 100 chance of being active. +- Otherwise, the feature should be randomly assigned to users with a 1 in 100 chance of being active. -The first time the `new-api` feature is checked for a given user, the result of the Closure will be stored by the underlying driver. The next time the feature is checked against the same user, the value will be retrieved from storage and the Closure will not be invoked. +The first time the `new-api` feature is checked for a given user, the result of the closure will be stored by the storage driver. The next time the feature is checked against the same user, the value will be retrieved from storage and the closure will not be invoked. -If a feature definition only returns a lottery, you may omit the Closure completely. +For convenience, if a feature definition only returns a lottery, you may omit the closure completely: Feature::define('site-redesign', Lottery::odds(1, 1000)); ### Class Based Features -Class based features are also supported. Unlike Closure based definitions, there is no need to register a class based feature in a service provider. +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. -When creating a feature class you will need to implement the `resolve` method: +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, typically, the scope will be the currently authenticated user: ```php isInternalTeamMember()) { - return true; - } - - if ($user->isHighTrafficCustomer()) { - return false; - } - - return Lottery::odds(1 / 100); + return match (true) { + $user->isInternalTeamMember() => true, + $user->isHighTrafficCustomer() => false, + default => Lottery::odds(1 / 100), + }; } } ``` -> **Note** Feature classes are resolved via the container, so you may inject dependencies into the class's constructor when needed. +> **Note** Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. ## Checking Features -To check if a feature is active, you should use the `active` method on the `Feature` facade. By default, features are checked against the currently authenticated user. +To determine if a feature is active, you may use the `active` method on the `Feature` facade. By default, features are checked against the currently authenticated user: ```php resolveNewApiResponse($request); - } - - return $this->resolveLegacyApiResponse($request); + return Feature::active('new-api') + ? $this->resolveNewApiResponse($request) + : $this->resolveLegacyApiResponse($request); } // ... } ``` -For class based features, you should use the class name when checking the feature: +For class based features, you should provide the class name when checking the feature: ```php resolveNewApiResponse($request); - } - - return $this->resolveLegacyApiResponse($request); + return Feature::active(NewApi::class) + ? $this->resolveNewApiResponse($request) + : $this->resolveLegacyApiResponse($request); } // ... } ``` -There are some additional methods that may come in handy when checking if a feature is active or not: +Pennant also offers some additional convenience methods that may prove useful when determining if a feature is active or not: - // Check if all the features are active... + // Determine if all of the given features are active... Feature::allAreActive(['new-api', 'site-redesign']); - // Check if any of the features are active... + // Determine if any of the given features are active... Feature::someAreActive(['new-api', 'site-redesign']); - // Check if a feature is inactive... + // Determine if a feature is inactive... Feature::inactive('new-api'); - // Check if all the features are inactive... + // Determine if all of the given features are inactive... Feature::allAreInactive(['new-api', 'site-redesign']); - // Check if any of the features are inactive... + // Determine if any of the given features are inactive... Feature::someAreInactive(['new-api', 'site-redesign']); ### Conditional Execution -Pennant offers the ability to conditionally execute a specific code block in a fluent manner. This is achieved via the `when` and `unless` methods. +The `when` method may be used to fluently execute a given closure if a feature is active. Additionally, a second closure may be provided and will be executed if the feature is inactive: $this->resolveLegacyApiResponse($request), + fn () => $this->resolveNewApiResponse($request), + ); + ### Blade Directive -To make checking features in Blade a seamless experience, Pennant also offers a `@feature` directive. +To make checking features in Blade a seamless experience, Pennant offers a `@feature` directive: ```blade @feature('site-redesign') @@ -261,18 +257,18 @@ To make checking features in Blade a seamless experience, Pennant also offers a ### In-Memory Cache -When checking a feature, Pennant will create an in-memory cache of the result. If you are using the database driver, this means that re-checking the same feature flag throughout a single request will not trigger additional database queries. It also ensures you have a consistent result for the duration of the request. +When checking a feature, Pennant will create an in-memory cache of the result. If you are using the `database` driver, this means that re-checking the same feature flag within a single request will not trigger additional database queries. This also ensures that the feature has a consistent result for the duration of the request. -If you need to manually flush the in-memory cache, you may use the `flushCache` method on the `Feature` facade. +If you need to manually flush the in-memory cache, you may use the `flushCache` method offered by the `Feature` facade: Feature::flushCache(); ## Scope -As previously mentioned, features are checked against the currently authenticated user by default. This may not always suit your needs. For one off feature checks, it is possible to specify the scope you would like to check the feature against. +As previously discussed, features are typically checked against the currently authenticated user. However, this may not always suit your needs. Therefore, it is possible to specify the scope you would like to check a given feature against. -Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You want the oldest teams to have a slower rollout than the newer teams. Your feature Closure might look something like the following: +Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You would like the oldest teams to have a slower rollout than the newer teams. Your feature resolution closure might look something like the following: use App\Models\Team; use Carbon\Carbon; @@ -290,7 +286,7 @@ Imagine you have built a new billing experience that you are rolling out to enti return Lottery::odds(1 / 1000); }); -You will notice that the Closure we have defined is not expecting a `User`, but is instead expecting a `Team` model. To check if this feature is active for a user's team, you should pass the team to the `for` method. +You will notice that the closure we have defined is not expecting a `User`, but is instead expecting a `Team` model. To determine if this feature is active for a user's team, you should pass the team to the `for` method offered by the `Feature` facade: use Laravel\Pennant\Feature; @@ -303,15 +299,15 @@ You will notice that the Closure we have defined is not expecting a `User`, but ### Default Scope -It is also possible to customize the default scope used when checking features. Suppose all your features are checked against the currently authenticated user's team; rather than having to call `Feature::for($user->team)` on every feature check, you may instead specify the default scope to use in a service provider. +It is also possible to customize the default scope used when checking features. Suppose all of your features are checked against the currently authenticated user's team. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: ```php team)->active('billing-v2'); ### Identifying Scope -Implementing the `FeatureScopeable` contract on your scope objects allows you to customize what is passed to the underlying Pennant drivers. +Pennant's built-in `array` and `database` storage drivers know how to properly store scope identifiers for all PHP primitive as well as Eloquent models. However, if your application implements a third-party Pennant driver, that driver may not know how to properly store an identifier for an Eloquent model or other custom types in your application. + +In light of this, Pennant allows you to format scope values for storage by implementing the `FeatureScopeable` contract on the objects in your application that are used as Pennant scopes. -Imagine you are using two different feature drivers in a single application, perhaps the built-in database driver and a 3rd party "Flag Rocket" driver. The "Flag Rocket" driver requires a `FlagRocketUser` to identify a user. The database driver, however, can handle an Eloquent model. Implementing the `toFeatureIdentifier` method allows you to customize the scope passed to each driver. +For example, imagine you are using two different feature drivers in a single application: the built-in `database` driver and a third-party "Flag Rocket" driver. The "Flag Rocket" driver does not know how to properly store an Eloquent model. Instead, it requires a `FlagRocketUser` instance. By implementing the `toFeatureIdentifier` defined by the `FeatureScopeable` contract, we can customize the storable scope value provided to each driver used by our application: ```php ## Rich Feature Values -Until now, we have shown features as being in a binary state, that is they are either active or inactive, but Pennant allows you to store rich values as well. +Until now, we have primarily shown features as being in a binary state, meaning they are either "active" or "inactive", but Pennant also allows you to store rich values as well. -Imagine you are testing 3 new colors for the "Buy now" button of your application. Instead of returning `true` or `false` from the feature definition, you may instead return a string. +For example, imagine you are testing three new colors for the "Buy now" button of your application. Instead of returning `true` or `false` from the feature definition, you may instead return a string: use Illuminate\Support\Arr; use Laravel\Pennant\Feature; - Feature::define('purchase-button', function (User $user) { - return Arr::random([ - 'blue-sapphire', - 'seafoam-green', - 'tart-orange', - ]); - }); + Feature::define('purchase-button', fn (User $user) => Arr::random( + 'blue-sapphire', + 'seafoam-green', + 'tart-orange', + )); -You may retrieve the value of the `purchase-button` feature using the `value` method. +You may retrieve the value of the `purchase-button` feature using the `value` method: $color = Feature::value('purchase-button'); -The Blade directive also makes it easy to conditionally render content based on the current value of the feature. +Pennant's included Blade directive also makes it easy to conditionally render content based on the current value of the feature: ```blade @feature('purchase-button', 'blue-sapphire') @@ -407,9 +403,11 @@ The Blade directive also makes it easy to conditionally render content based on ## Eager Loading -Although Pennant keeps an in-memory cache of all resolved features for a single request, it is still possible to run into performance issues. To alleviate this, Pennant offers the ability to eager load feature values. +Although Pennant keeps an in-memory cache of all resolved features for a single request, it is still possible to encounter performance issues. To alleviate this, Pennant offers the ability to eager load feature values. -Imagine that we are checking if a feature is active within a loop: +To illustrate this, imagine that we are checking if a feature is active within a loop: + + use Laravel\Pennant\Feature; foreach ($users as $user) { if (Feature::for($user)->active('notifications-beta')) { @@ -417,7 +415,7 @@ Imagine that we are checking if a feature is active within a loop: } } -Assuming we are using the database driver, we are going to be hitting the database for every user in the loop - potentially hundreds of queries. With eager loading we can remove this potential performance bottleneck. +Assuming we are using the database driver, this code will execute a database query for every user in the loop - executing potentially hundreds of queries. However, using Pennant's `load` method, we can remove this potential performance bottleneck by eager loading the feature values for a collection of users or scopes: Feature::for($users)->load(['notifications-beta']); @@ -427,9 +425,7 @@ Assuming we are using the database driver, we are going to be hitting the databa } } -Once in place we no longer trigger any database queries inside the loop. - -To load values only when it has not already been loaded, use the `loadMissing` method: +To load feature values only when they have not already been loaded, you may use the `loadMissing` method: Feature::for($users)->loadMissing([ 'new-api', @@ -440,9 +436,11 @@ To load values only when it has not already been loaded, use the `loadMissing` m ## Updating Values -When a feature's value is resolved for the first time, the underlying driver will store the result. This is handy to ensure a consistent experience for your users across requests, but you may also want to manually update the feature's stored value. +When a feature's value is resolved for the first time, the underlying driver will store the result in storage. This is often necessary to ensure a consistent experience for your users across requests. However, at times, you may want to manually update the feature's stored value. -To achieve this you can use the `activate` and `deactivate` methods to toggle a feature on or off. +To accomplish this, you may use the `activate` and `deactivate` methods to toggle a feature "on" or "off": + + use Laravel\Pennant\Feature; // Activate the feature for the default scope... Feature::activate('new-api'); @@ -450,46 +448,47 @@ To achieve this you can use the `activate` and `deactivate` methods to toggle a // Deactivate the feature for the given scope... Feature::for($user->team)->deactivate('billing-v2'); -It is also possible to manually set rich value for a feature by passing through a second argument to the `activate` method. +It is also possible to manually set a rich value for a feature by providing a second argument to the `activate` method: Feature::activate('purchase-button', 'seafoam-green'); -If you wish to forget the stored value for a feature, so that the next time it is checked the value is resolved from the feature definition, you may use the `forget` method. +To instruct Pennant to forget the stored value for a feature, you may use the `forget` method. When the feature is checked again, Pennant will resolve the feature's value from its feature definition: Feature::forget('purchase-button'); ### Bulk Updates -To make bulk updates you may use the `activateForEveryone` and `deactivateForEveryone` methods. +To update stored feature values in bulk, you may use the `activateForEveryone` and `deactivateForEveryone` methods. -Say you are now confident in the `new-api` feature's stability and have landed on the best `'purchase-button'` color to be using, you can update the stored value for all users accordingly. +For example, imagine you are now confident in the `new-api` feature's stability and have landed on the best `'purchase-button'` color for your checkout flow - you can update the stored value for all users accordingly: + + use Laravel\Pennant\Feature; Feature::activateForEveryone('new-api'); Feature::activateForEveryone('purchase-button', 'seafoam-green'); - -Alternatively, if you are having issues with a feature rollout you may need to deactivate the feature for all users instead. +Alternatively, you may deactivate the feature for all users: Feature::deactivateForEveryone('new-api'); -> **Note** This will only update the existing stored values. You may also need to update the feature definition in your code. +> **Note** This will only update the resolved feature values that have been stored by Pennant's storage driver. You will also need to update the feature definition in your application. ### Purging Features -It can be useful to purge an entire feature from storage, whether you have removed the feature from your system or you have made adjustments to the features definition that you would like to rollout fresh to all users. +Sometimes, it can be useful to purge an entire feature from storage. This is typically necessary if you have removed the feature from your application or you have made adjustments to the feature's definition that you would like to rollout to all users. -You may completely remove all stored values for a feature using the `purge` method. +You may remove all stored values for a feature using the `purge` method: Feature::purge('new-api'); -If you would like to purge _all_ features from storage, you may do so by calling `purge` without any arguments. +If you would like to purge _all_ features from storage, you may invoke the `purge` method without any arguments: Feature::purge(); -As it can be useful to do this as part of your deployment pipeline, we have also included a handy Artisan. +As it can be useful to purge features as part of your application's deployment pipeline, Pennant includes a `pennant:purge` Artisan command: ```sh php artisan pennant:purge new-api @@ -498,17 +497,17 @@ php artisan pennant:purge new-api ## Events -Pennant dispatches a few events that may be useful for tracking feature flags throughout your application. +Pennant dispatches a variety of events that can be useful when tracking feature flags throughout your application. -### `Illuminate\Pennant\Events\RetrievingKnownFeature` +### `Laravel\Pennant\Events\RetrievingKnownFeature` -This event is dispatched the first time a known feature is retrieved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are in-use throughout your application. +This event is dispatched the first time a known feature is retrieved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are being used throughout your application. -### `Illuminate\Pennant\Events\RetrievingUnknownFeature` +### `Laravel\Pennant\Events\RetrievingUnknownFeature` -This event is dispatched the first time an unknown feature is retrieved during a request for the specific scope. This event can be useful if you have intended to remove a feature flag, but left some stray references to it throughout your application. +This event is dispatched the first time an unknown feature is retrieved during a request for a specific scope. This event can be useful if you have intended to remove a feature flag, but may have accidentally left some stray references to it throughout your application. -You may like to listen for this event and report or throw an exception when it occurs. +For example, you may find it useful to listen for this event and `report` or throw an exception when it occurs: ```php feature}].")); + report("Resolving unknown feature [{$event->feature}]."); }); } } ``` -### `Illuminate\Pennant\Events\DynamicallyDefiningFeature` +### `Laravel\Pennant\Events\DynamicallyDefiningFeature` -This event is dispatched whenever a class based feature is being dynamically checked for the first time during a request. +This event is dispatched when a class based feature is being dynamically checked for the first time during a request. From b92359331272a1eec6d7c952ec918162a6de1777 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Feb 2023 13:38:45 -0600 Subject: [PATCH 0725/2609] wip --- pennant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 97c2c29298e..1374479498a 100644 --- a/pennant.md +++ b/pennant.md @@ -102,7 +102,7 @@ For convenience, if a feature definition only returns a lottery, you may omit th 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. -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, typically, the scope will be the currently authenticated user: +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 Date: Fri, 10 Feb 2023 06:56:14 +1100 Subject: [PATCH 0726/2609] [10.x] Pennant (#8531) * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * fixes * wip --------- Co-authored-by: Taylor Otwell --- documentation.md | 1 + pennant.md | 537 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 538 insertions(+) create mode 100644 pennant.md diff --git a/documentation.md b/documentation.md index 011105b19ba..c35f8d35aa5 100644 --- a/documentation.md +++ b/documentation.md @@ -89,6 +89,7 @@ - [Mix](/docs/{{version}}/mix) - [Octane](/docs/{{version}}/octane) - [Passport](/docs/{{version}}/passport) + - [Pennant](/docs/{{version}}/pennant) - [Pint](/docs/{{version}}/pint) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) diff --git a/pennant.md b/pennant.md new file mode 100644 index 00000000000..1374479498a --- /dev/null +++ b/pennant.md @@ -0,0 +1,537 @@ +# Laravel Pennant + +- [Introduction](#introduction) +- [Installation](#installation) +- [Configuration](#configuration) +- [Defining Features](#defining-features) + - [Class Based Features](#class-based-features) +- [Checking Features](#checking-features) + - [Conditional Execution](#conditional-execution) + - [Blade Directive](#blade-directive) + - [In-Memory Cache](#in-memory-cache) +- [Scope](#scope) + - [Default Scope](#default-scope) + - [Identifying Scope](#identifying-scope) +- [Rich Feature Values](#rich-feature-values) +- [Eager Loading](#eager-loading) +- [Updating Values](#updating-values) + - [Bulk Updates](#bulk-updates) + - [Purging Features](#purging-features) +- [Events](#events) + + +## Introduction + +[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flag package - without the cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. + + +## Installation + +First, install Pennant into your project using the Composer package manager: + +```shell +composer require laravel/pennant +``` + +Next, you should publish the Pennant configuration and migration files using the `vendor:publish` Artisan command: + +```shell +php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider" +``` + +Finally, you should run your application's database migrations. This will create a `features` table that Pennant uses to power its `database` driver: + +```shell +php artisan migrate +``` + + +## Configuration + +After publishing Pennant's assets, its configuration file will be located at `config/pennant.php`. This configuration file allows you to specify the default storage mechanism that will be used by Pennant to store resolved feature flag values. + +Pennant includes support for storing resolved feature flag values in an in-memory array via the `array` driver. Or, Pennant can store resolved feature flag values persistently in a relational database via the `database` driver, which is the default storage mechanism used by Pennant. + + +## Defining Features + +To define a feature, you may use the `define` method offered by the `Feature` facade. You will need to provide a name for the feature, as well as a closure that will be invoked to resolve the feature's initial value. + +Typically, features are defined in a service provider using the `Feature` facade. The closure will receive the "scope" for the feature check. Most commonly, the scope is the currently authenticated user. In this example, we will define a feature for incrementally rolling out a new API to our application's users: + +```php + match (true) { + $user->isInternalTeamMember() => true, + $user->isHighTrafficCustomer() => false, + default => Lottery::odds(1 / 100), + }); + } +} +``` + +As you can see, we have the following rules for our feature: + +- All internal team members should be using the new API. +- Any high traffic customers should not be using the new API. +- Otherwise, the feature should be randomly assigned to users with a 1 in 100 chance of being active. + +The first time the `new-api` feature is checked for a given user, the result of the closure will be stored by the storage driver. The next time the feature is checked against the same user, the value will be retrieved from storage and the closure will not be invoked. + +For convenience, if a feature definition only returns a lottery, you may omit the closure completely: + + Feature::define('site-redesign', Lottery::odds(1, 1000)); + + +### 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. + +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 +isInternalTeamMember() => true, + $user->isHighTrafficCustomer() => false, + default => Lottery::odds(1 / 100), + }; + } +} +``` + +> **Note** Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. + + +## Checking Features + +To determine if a feature is active, you may use the `active` method on the `Feature` facade. By default, features are checked against the currently authenticated user: + +```php +resolveNewApiResponse($request) + : $this->resolveLegacyApiResponse($request); + } + + // ... +} +``` + +For class based features, you should provide the class name when checking the feature: + +```php +resolveNewApiResponse($request) + : $this->resolveLegacyApiResponse($request); + } + + // ... +} +``` + +Pennant also offers some additional convenience methods that may prove useful when determining if a feature is active or not: + + // Determine if all of the given features are active... + Feature::allAreActive(['new-api', 'site-redesign']); + + // Determine if any of the given features are active... + Feature::someAreActive(['new-api', 'site-redesign']); + + // Determine if a feature is inactive... + Feature::inactive('new-api'); + + // Determine if all of the given features are inactive... + Feature::allAreInactive(['new-api', 'site-redesign']); + + // Determine if any of the given features are inactive... + Feature::someAreInactive(['new-api', 'site-redesign']); + + +### Conditional Execution + +The `when` method may be used to fluently execute a given closure if a feature is active. Additionally, a second closure may be provided and will be executed if the feature is inactive: + + $this->resolveNewApiResponse($request), + fn () => $this->resolveLegacyApiResponse($request), + ); + } + + // ... + } + +The `unless` method serves as the inverse of the `when` method, executing the first closure if the feature is inactive: + + return Feature::unless(NewApi::class, + fn () => $this->resolveLegacyApiResponse($request), + fn () => $this->resolveNewApiResponse($request), + ); + + +### Blade Directive + +To make checking features in Blade a seamless experience, Pennant offers a `@feature` directive: + +```blade +@feature('site-redesign') + +@else + +@endfeature +``` + + +### In-Memory Cache + +When checking a feature, Pennant will create an in-memory cache of the result. If you are using the `database` driver, this means that re-checking the same feature flag within a single request will not trigger additional database queries. This also ensures that the feature has a consistent result for the duration of the request. + +If you need to manually flush the in-memory cache, you may use the `flushCache` method offered by the `Feature` facade: + + Feature::flushCache(); + + +## Scope + +As previously discussed, features are typically checked against the currently authenticated user. However, this may not always suit your needs. Therefore, it is possible to specify the scope you would like to check a given feature against. + +Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You would like the oldest teams to have a slower rollout than the newer teams. Your feature resolution closure might look something like the following: + + use App\Models\Team; + use Carbon\Carbon; + use Illuminate\Support\Lottery; + + Feature::define('billing-v2', function (Team $team) { + if ($team->created_at->isAfter(new Carbon('1st Jan, 2023'))) { + return true; + } + + if ($team->created_at->isAfter(new Carbon('1st Jan, 2019'))) { + return Lottery::odds(1 / 100); + } + + return Lottery::odds(1 / 1000); + }); + +You will notice that the closure we have defined is not expecting a `User`, but is instead expecting a `Team` model. To determine if this feature is active for a user's team, you should pass the team to the `for` method offered by the `Feature` facade: + + use Laravel\Pennant\Feature; + + if (Feature::for($user->team)->active('billing-v2')) { + return redirect()->to('/billing/v2'); + } + + // ... + + +### Default Scope + +It is also possible to customize the default scope used when checking features. Suppose all of your features are checked against the currently authenticated user's team. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: + +```php +user()->team; + }); + + // ... + } +} +``` + +If no explicit scope is provided via the `for` method, the feature check will now use the currently authenticated user's team as the default scope: + + Feature::active('billing-v2'); + + // Is now equivalent to... + + Feature::for($user->team)->active('billing-v2'); + + +### Identifying Scope + +Pennant's built-in `array` and `database` storage drivers know how to properly store scope identifiers for all PHP primitive as well as Eloquent models. However, if your application implements a third-party Pennant driver, that driver may not know how to properly store an identifier for an Eloquent model or other custom types in your application. + +In light of this, Pennant allows you to format scope values for storage by implementing the `FeatureScopeable` contract on the objects in your application that are used as Pennant scopes. + +For example, imagine you are using two different feature drivers in a single application: the built-in `database` driver and a third-party "Flag Rocket" driver. The "Flag Rocket" driver does not know how to properly store an Eloquent model. Instead, it requires a `FlagRocketUser` instance. By implementing the `toFeatureIdentifier` defined by the `FeatureScopeable` contract, we can customize the storable scope value provided to each driver used by our application: + +```php + $this, + 'flag-rocket' => FlagRocketUser::fromId($this->flag_rocket_id), + }; + } +} +``` + + +## Rich Feature Values + +Until now, we have primarily shown features as being in a binary state, meaning they are either "active" or "inactive", but Pennant also allows you to store rich values as well. + +For example, imagine you are testing three new colors for the "Buy now" button of your application. Instead of returning `true` or `false` from the feature definition, you may instead return a string: + + use Illuminate\Support\Arr; + use Laravel\Pennant\Feature; + + Feature::define('purchase-button', fn (User $user) => Arr::random( + 'blue-sapphire', + 'seafoam-green', + 'tart-orange', + )); + +You may retrieve the value of the `purchase-button` feature using the `value` method: + + $color = Feature::value('purchase-button'); + +Pennant's included Blade directive also makes it easy to conditionally render content based on the current value of the feature: + +```blade +@feature('purchase-button', 'blue-sapphire') + +@elsefeature('purchase-button', 'seafoam-green') + +@elsefeature('purchase-button', 'tart-orange') + +@endfeature +``` + +> **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. + + +## Eager Loading + +Although Pennant keeps an in-memory cache of all resolved features for a single request, it is still possible to encounter performance issues. To alleviate this, Pennant offers the ability to eager load feature values. + +To illustrate this, imagine that we are checking if a feature is active within a loop: + + use Laravel\Pennant\Feature; + + foreach ($users as $user) { + if (Feature::for($user)->active('notifications-beta')) { + $user->notify(new RegistrationSuccess); + } + } + +Assuming we are using the database driver, this code will execute a database query for every user in the loop - executing potentially hundreds of queries. However, using Pennant's `load` method, we can remove this potential performance bottleneck by eager loading the feature values for a collection of users or scopes: + + Feature::for($users)->load(['notifications-beta']); + + foreach ($users as $user) { + if (Feature::for($user)->active('notifications-beta')) { + $user->notify(new RegistrationSuccess); + } + } + +To load feature values only when they have not already been loaded, you may use the `loadMissing` method: + + Feature::for($users)->loadMissing([ + 'new-api', + 'purchase-button', + 'notifications-beta', + ]); + + +## Updating Values + +When a feature's value is resolved for the first time, the underlying driver will store the result in storage. This is often necessary to ensure a consistent experience for your users across requests. However, at times, you may want to manually update the feature's stored value. + +To accomplish this, you may use the `activate` and `deactivate` methods to toggle a feature "on" or "off": + + use Laravel\Pennant\Feature; + + // Activate the feature for the default scope... + Feature::activate('new-api'); + + // Deactivate the feature for the given scope... + Feature::for($user->team)->deactivate('billing-v2'); + +It is also possible to manually set a rich value for a feature by providing a second argument to the `activate` method: + + Feature::activate('purchase-button', 'seafoam-green'); + +To instruct Pennant to forget the stored value for a feature, you may use the `forget` method. When the feature is checked again, Pennant will resolve the feature's value from its feature definition: + + Feature::forget('purchase-button'); + + +### Bulk Updates + +To update stored feature values in bulk, you may use the `activateForEveryone` and `deactivateForEveryone` methods. + +For example, imagine you are now confident in the `new-api` feature's stability and have landed on the best `'purchase-button'` color for your checkout flow - you can update the stored value for all users accordingly: + + use Laravel\Pennant\Feature; + + Feature::activateForEveryone('new-api'); + + Feature::activateForEveryone('purchase-button', 'seafoam-green'); + +Alternatively, you may deactivate the feature for all users: + + Feature::deactivateForEveryone('new-api'); + +> **Note** This will only update the resolved feature values that have been stored by Pennant's storage driver. You will also need to update the feature definition in your application. + + +### Purging Features + +Sometimes, it can be useful to purge an entire feature from storage. This is typically necessary if you have removed the feature from your application or you have made adjustments to the feature's definition that you would like to rollout to all users. + +You may remove all stored values for a feature using the `purge` method: + + Feature::purge('new-api'); + +If you would like to purge _all_ features from storage, you may invoke the `purge` method without any arguments: + + Feature::purge(); + +As it can be useful to purge features as part of your application's deployment pipeline, Pennant includes a `pennant:purge` Artisan command: + +```sh +php artisan pennant:purge new-api +``` + + +## Events + +Pennant dispatches a variety of events that can be useful when tracking feature flags throughout your application. + +### `Laravel\Pennant\Events\RetrievingKnownFeature` + +This event is dispatched the first time a known feature is retrieved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are being used throughout your application. + +### `Laravel\Pennant\Events\RetrievingUnknownFeature` + +This event is dispatched the first time an unknown feature is retrieved during a request for a specific scope. This event can be useful if you have intended to remove a feature flag, but may have accidentally left some stray references to it throughout your application. + +For example, you may find it useful to listen for this event and `report` or throw an exception when it occurs: + +```php +feature}]."); + }); + } +} +``` + +### `Laravel\Pennant\Events\DynamicallyDefiningFeature` + +This event is dispatched when a class based feature is being dynamically checked for the first time during a request. From 938ee7434d1c28d2a865850d1456c7ec257d6c07 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Feb 2023 13:56:50 -0600 Subject: [PATCH 0727/2609] specifying the scope --- pennant.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/pennant.md b/pennant.md index 1374479498a..17f03c58bb1 100644 --- a/pennant.md +++ b/pennant.md @@ -10,6 +10,7 @@ - [Blade Directive](#blade-directive) - [In-Memory Cache](#in-memory-cache) - [Scope](#scope) + - [Specifying The Scope](#specifying-the-scope) - [Default Scope](#default-scope) - [Identifying Scope](#identifying-scope) - [Rich Feature Values](#rich-feature-values) @@ -159,6 +160,14 @@ class PodcastController } ``` +Although features are checked against the currently authenticated user by default, you may easily check the feature against another user or scope. To accomplish this, use the `for` method offered by the `Feature` facade: + +```php +return Feature::for($user)->active('new-api') + ? $this->resolveNewApiResponse($request) + : $this->resolveLegacyApiResponse($request); +``` + For class based features, you should provide the class name when checking the feature: ```php @@ -266,13 +275,23 @@ If you need to manually flush the in-memory cache, you may use the `flushCache` ## Scope -As previously discussed, features are typically checked against the currently authenticated user. However, this may not always suit your needs. Therefore, it is possible to specify the scope you would like to check a given feature against. + +### Specifying The Scope -Imagine you have built a new billing experience that you are rolling out to entire teams at once, rather than individual users. You would like the oldest teams to have a slower rollout than the newer teams. Your feature resolution closure might look something like the following: +As discussed, features are typically checked against the currently authenticated user. However, this may not always suit your needs. Therefore, it is possible to specify the scope you would like to check a given feature against via the `Feature` facade's `for` method: + +```php +return Feature::for($user)->active('new-api') + ? $this->resolveNewApiResponse($request) + : $this->resolveLegacyApiResponse($request); +``` + +Of course, feature scopes are not limited to "users". Imagine you have built a new billing experience that you are rolling out to entire teams rather than individual users. Perhaps you would like the oldest teams to have a slower rollout than the newer teams. Your feature resolution closure might look something like the following: use App\Models\Team; use Carbon\Carbon; use Illuminate\Support\Lottery; + use Laravel\Pennant\Feature; Feature::define('billing-v2', function (Team $team) { if ($team->created_at->isAfter(new Carbon('1st Jan, 2023'))) { @@ -288,8 +307,6 @@ Imagine you have built a new billing experience that you are rolling out to enti You will notice that the closure we have defined is not expecting a `User`, but is instead expecting a `Team` model. To determine if this feature is active for a user's team, you should pass the team to the `for` method offered by the `Feature` facade: - use Laravel\Pennant\Feature; - if (Feature::for($user->team)->active('billing-v2')) { return redirect()->to('/billing/v2'); } @@ -299,7 +316,7 @@ You will notice that the closure we have defined is not expecting a `User`, but ### Default Scope -It is also possible to customize the default scope used when checking features. Suppose all of your features are checked against the currently authenticated user's team. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: +It is also possible to customize the default scope Pennant uses to check features. For example, maybe all of your features are checked against the currently authenticated user's team instead of the user. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: ```php ### Identifying Scope -Pennant's built-in `array` and `database` storage drivers know how to properly store scope identifiers for all PHP primitive as well as Eloquent models. However, if your application implements a third-party Pennant driver, that driver may not know how to properly store an identifier for an Eloquent model or other custom types in your application. +Pennant's built-in `array` and `database` storage drivers know how to properly store scope identifiers for all PHP data types as well as Eloquent models. However, if your application utilizes a third-party Pennant driver, that driver may not know how to properly store an identifier for an Eloquent model or other custom types in your application. In light of this, Pennant allows you to format scope values for storage by implementing the `FeatureScopeable` contract on the objects in your application that are used as Pennant scopes. From 39d08535dc23413a0e18ea9abd8d1e2d88dd8d31 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Feb 2023 17:08:40 -0600 Subject: [PATCH 0728/2609] update example --- cache.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cache.md b/cache.md index f6572bc01e4..9bd3493c147 100644 --- a/cache.md +++ b/cache.md @@ -334,8 +334,8 @@ Atomic locks allow for the manipulation of distributed locks without worrying ab 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... + Cache::lock('foo', 10)->get(function () { + // Lock acquired for 10 seconds and automatically released... }); If the lock is not available at the moment you request it, you may instruct Laravel to wait for a specified number of seconds. If the lock can not be acquired within the specified time limit, an `Illuminate\Contracts\Cache\LockTimeoutException` will be thrown: From f2abc832afca92b6e861a1a97597f4399cea9dae Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Feb 2023 17:29:54 -0600 Subject: [PATCH 0729/2609] update process namespaces --- processes.md | 16 ++++++++-------- releases.md | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/processes.md b/processes.md index cf90a1f6236..0a49278484a 100644 --- a/processes.md +++ b/processes.md @@ -36,7 +36,7 @@ $result = Process::run('ls -la'); return $result->output(); ``` -Of course, the `Illuminate\Contracts\Console\Process\ProcessResult` instance returned by the `run` method offers a variety of helpful methods that may be used to inspect the process result: +Of course, the `Illuminate\Contracts\Process\ProcessResult` instance returned by the `run` method offers a variety of helpful methods that may be used to inspect the process result: ```php $result = Process::run('ls -la'); @@ -51,7 +51,7 @@ $result->errorOutput(); #### Throwing Exceptions -If you have a process result and would like to throw an instance of `Illuminate\Console\Process\Exceptions\ProcessFailedException` if the exit code is greater than zero (thus indicating failure), you may use the `throw` and `throwIf` methods. If the process did not fail, the process result instance will be returned: +If you have a process result and would like to throw an instance of `Illuminate\Process\Exceptions\ProcessFailedException` if the exit code is greater than zero (thus indicating failure), you may use the `throw` and `throwIf` methods. If the process did not fail, the process result instance will be returned: ```php $result = Process::run('ls -la')->throw(); @@ -76,7 +76,7 @@ $result = Process::path(__DIR__)->run('ls -la'); #### Timeouts -By default, processes will throw an instance of `Illuminate\Console\Process\Exceptions\ProcessTimedOutException` after executing for more than 60 seconds. However, you can customize this behavior via the `timeout` method: +By default, processes will throw an instance of `Illuminate\Process\Exceptions\ProcessTimedOutException` after executing for more than 60 seconds. However, you can customize this behavior via the `timeout` method: ```php $result = Process::timeout(120)->run('bash import.sh'); @@ -234,12 +234,12 @@ $result = $process->wait(); ## Concurrent Processes -Laravel also makes it a breeze to manage a pool of concurrent, asynchronous processes, allowing you to easily execute many tasks simultaneously. To get started, invoke the `pool` method, which accepts a closure that receives an instance of `Illuminate\Console\Process\Pool`. +Laravel also makes it a breeze to manage a pool of concurrent, asynchronous processes, allowing you to easily execute many tasks simultaneously. To get started, invoke the `pool` method, which accepts a closure that receives an instance of `Illuminate\Process\Pool`. Within this closure, you may define the processes that belong to the pool. Once a process pool is started via the `start` method, you may access the [collection](/docs/{{version}}/collections) of running processes via the `running` method: ```php -use Illuminate\Console\Process\Pool; +use Illuminate\Process\Pool; use Illuminate\Support\Facades\Process; $pool = Process::pool(function (Pool $pool) { @@ -339,8 +339,8 @@ When testing this route, we can instruct Laravel to return a fake, successful pr namespace Tests\Feature; -use Illuminate\Console\Process\PendingProcess; -use Illuminate\Contracts\Console\Process\ProcessResult; +use Illuminate\Process\PendingProcess; +use Illuminate\Contracts\Process\ProcessResult; use Illuminate\Support\Facades\Process; use Tests\TestCase; @@ -480,7 +480,7 @@ Process::assertRan(fn ($process, $result) => ); ``` -The `$process` passed to the `assertRan` closure is an instance of `Illuminate\Console\Process\PendingProcess`, while the `$result` is an instance of `Illuminate\Contracts\Console\Process\ProcessResult`. +The `$process` passed to the `assertRan` closure is an instance of `Illuminate\Process\PendingProcess`, while the `$result` is an instance of `Illuminate\Contracts\Process\ProcessResult`. #### assertDidntRun diff --git a/releases.md b/releases.md index 6d3bdb7548a..8507c36c5c4 100644 --- a/releases.md +++ b/releases.md @@ -160,7 +160,7 @@ return $result->output(); Processes may even be started in pools, allowing for the convenient execution and management of concurrent processes: ```php -use Illuminate\Console\Process\Pool; +use Illuminate\Process\Pool; use Illuminate\Support\Facades\Pool; [$first, $second, $third] = Process::concurrently(function (Pool $pool) { From 32fbcf13310c071ee8e0f4c07e4e91c5223f1fba Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 10 Feb 2023 13:03:46 +1100 Subject: [PATCH 0730/2609] [10.x] Documents nullable scope for Pennant (#8537) * Documents nullable scope for Pennant * Update pennant.md * Update pennant.md * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pennant.md b/pennant.md index d798e76b45b..fcad942eef4 100644 --- a/pennant.md +++ b/pennant.md @@ -12,6 +12,7 @@ - [Scope](#scope) - [Specifying The Scope](#specifying-the-scope) - [Default Scope](#default-scope) + - [Nullable Scope](#nullable-scope) - [Identifying Scope](#identifying-scope) - [Rich Feature Values](#rich-feature-values) - [Eager Loading](#eager-loading) @@ -358,6 +359,25 @@ Feature::active('billing-v2'); Feature::for($user->team)->active('billing-v2'); ``` + +### Nullable Scope + +If the scope you are passing to a feature is potentially `null`, you should account for that in your feature's definition. A `null` scope may occur if you check a feature within an Artisan command, queued job, or unauthenticated route. Since there is usually not an authenticated user in these contexts, the default scope will be `null`. If you do not always [explictly specify your feature scope](#specifying-the-scope), you should ensure the scope's type is nullable and handle the `null` scope value in your feature definition logic: + +```php +use App\Models\User; +use Illuminate\Support\Lottery; +use Laravel\Pennant\Feature; + +Feature::define('new-api', fn (User $user) => match (true) {// [tl! remove] +Feature::define('new-api', fn (User|null $user) => match (true) {// [tl! add] + $user === null => true,// [tl! add] + $user->isInternalTeamMember() => true, + $user->isHighTrafficCustomer() => false, + default => Lottery::odds(1 / 100), +}); +``` + ### Identifying Scope From ad358b3bbcde58c11ea2464e16dacc4bd0253070 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 10 Feb 2023 13:10:12 +1100 Subject: [PATCH 0731/2609] [10.x] Notes on using Pennant outside of a HTTP context (#8538) * Documents handling scope outside of a http context * Update pennant.md * Update pennant.md * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pennant.md b/pennant.md index fcad942eef4..02eabf2f6c7 100644 --- a/pennant.md +++ b/pennant.md @@ -169,6 +169,12 @@ return Feature::for($user)->active('new-api') : $this->resolveLegacyApiResponse($request); ``` +> **Note** +> When using Pennant outside of an HTTP context, such as in an Artisan command or a queued job, you should typically [explicitly specify the feature's scope](#specifying-the-scope). Alternatively, you may define a [default scope](#default-scope) that accounts for both authenticated HTTP contexts and unauthenticated contexts. + + +#### Checking Class Based Features + For class based features, you should provide the class name when checking the feature: ```php @@ -330,6 +336,7 @@ It is also possible to customize the default scope Pennant uses to check feature namespace App\Providers; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\ServiceProvider; use Laravel\Pennant\Feature; @@ -340,9 +347,7 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - Feature::resolveScopeUsing(function ($driver) { - return request()->user()->team; - }); + Feature::resolveScopeUsing(fn ($driver) => Auth::user()?->team); // ... } From 662846d1a1028a473e1e8d0d296d472f270c2abf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Feb 2023 20:11:50 -0600 Subject: [PATCH 0732/2609] wip --- pennant.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 02eabf2f6c7..7694ea9e7a5 100644 --- a/pennant.md +++ b/pennant.md @@ -367,7 +367,9 @@ Feature::for($user->team)->active('billing-v2'); ### Nullable Scope -If the scope you are passing to a feature is potentially `null`, you should account for that in your feature's definition. A `null` scope may occur if you check a feature within an Artisan command, queued job, or unauthenticated route. Since there is usually not an authenticated user in these contexts, the default scope will be `null`. If you do not always [explictly specify your feature scope](#specifying-the-scope), you should ensure the scope's type is nullable and handle the `null` scope value in your feature definition logic: +If the scope you are passing to a feature is potentially `null`, you should account for that in your feature's definition. A `null` scope may occur if you check a feature within an Artisan command, queued job, or unauthenticated route. Since there is usually not an authenticated user in these contexts, the default scope will be `null`. + +If you do not always [explictly specify your feature scope](#specifying-the-scope) then you should ensure the scope's type is "nullable" and handle the `null` scope value within your feature definition logic: ```php use App\Models\User; From 5986682f331a82976c35bbb00453288f8ba4d096 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Feb 2023 21:57:38 -0600 Subject: [PATCH 0733/2609] generator command --- pennant.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 7694ea9e7a5..3f8d3a7d73f 100644 --- a/pennant.md +++ b/pennant.md @@ -102,7 +102,11 @@ 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. +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 +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: From 7477842590a489ecd2c1017324e3d680bce6fc8e Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 10 Feb 2023 14:45:21 +0000 Subject: [PATCH 0734/2609] [10.x] Documents `--profile` Artisan test command option (#8543) * Adds profiling tests documentation * Update testing.md --------- Co-authored-by: Taylor Otwell --- testing.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/testing.md b/testing.md index 00572e4881f..7545c27c3f1 100644 --- a/testing.md +++ b/testing.md @@ -6,6 +6,7 @@ - [Running Tests](#running-tests) - [Running Tests In Parallel](#running-tests-in-parallel) - [Reporting Test Coverage](#reporting-test-coverage) + - [Profiling Tests](#profiling-tests) ## Introduction @@ -205,3 +206,12 @@ You may use the `--min` option to define a minimum test coverage threshold for y ```shell php artisan test --coverage --min=80.3 ``` + + +### Profiling Tests + +The Artisan test runner also includes a convenient mechanism for listing your application's slowest tests. Invoke the `test` command with the `--profile` option to be presented with a list of your ten slowest tests, allowing you to easily investigate which tests can be improved to speed up your test suite: + +```shell +php artisan test --profile +``` From 647886b790b6dd985a162a57ea6ec4d645b3cb27 Mon Sep 17 00:00:00 2001 From: Khalil Bouzidi <49455868+Safemood@users.noreply.github.com> Date: Fri, 10 Feb 2023 15:54:53 +0100 Subject: [PATCH 0735/2609] add --hours= option to docs (#8541) * Update passport.md add --hours= options to docs * Update passport.md --------- Co-authored-by: Taylor Otwell --- passport.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/passport.md b/passport.md index 6c32397bae2..9f83c73ebaa 100644 --- a/passport.md +++ b/passport.md @@ -551,6 +551,9 @@ When tokens have been revoked or expired, you might want to purge them from the # Purge revoked and expired tokens and auth codes... php artisan passport:purge +# Only purge tokens expired for more than 6 hours... +php artisan passport:purge --hours=6 + # Only purge revoked tokens and auth codes... php artisan passport:purge --revoked From 8a472bff9b1de014efa13d2e04d5e5cf97844e65 Mon Sep 17 00:00:00 2001 From: Morten Scheel Date: Fri, 10 Feb 2023 15:56:01 +0100 Subject: [PATCH 0736/2609] Fix syntax error in example (#8540) --- pennant.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennant.md b/pennant.md index 3f8d3a7d73f..ca593238c0c 100644 --- a/pennant.md +++ b/pennant.md @@ -433,11 +433,11 @@ For example, imagine you are testing three new colors for the "Buy now" button o use Illuminate\Support\Arr; use Laravel\Pennant\Feature; -Feature::define('purchase-button', fn (User $user) => Arr::random( +Feature::define('purchase-button', fn (User $user) => Arr::random([ 'blue-sapphire', 'seafoam-green', 'tart-orange', -)); +])); ``` You may retrieve the value of the `purchase-button` feature using the `value` method: From 399ace7a0774ffda07736b8554b2928e76b36833 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 10 Feb 2023 09:42:09 -0600 Subject: [PATCH 0737/2609] wip --- pennant.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pennant.md b/pennant.md index ca593238c0c..2c5dea4f69e 100644 --- a/pennant.md +++ b/pennant.md @@ -559,7 +559,11 @@ Sometimes, it can be useful to purge an entire feature from storage. This is typ You may remove all stored values for a feature using the `purge` method: ```php +// Purging a single feature... Feature::purge('new-api'); + +// Purging multiple features... +Feature::purge(['new-api', 'purchase-button']); ``` If you would like to purge _all_ features from storage, you may invoke the `purge` method without any arguments: @@ -572,6 +576,8 @@ As it can be useful to purge features as part of your application's deployment p ```sh php artisan pennant:purge new-api + +php artisan pennant:purge new-api purchase-button ``` From 7f37ebd5c48a1e9d9514809ca89258432eb7b78b Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 10 Feb 2023 09:43:54 -0600 Subject: [PATCH 0738/2609] use promoted properties (#8536) using promoted properties makes the docs more succinct. I did not switch to promoted properties when the properties were not explicitly defined in the code already. --- blade.md | 23 ++++------------------- broadcasting.md | 14 +++----------- collections.md | 12 +++--------- container.md | 28 ++++++---------------------- contracts.md | 14 +++----------- controllers.md | 12 +++--------- eloquent-mutators.md | 14 +++----------- events.md | 14 +++----------- horizon.md | 14 +++----------- mail.md | 42 +++++++++--------------------------------- queues.md | 28 ++++++---------------------- views.md | 14 +++----------- 12 files changed, 49 insertions(+), 180 deletions(-) diff --git a/blade.md b/blade.md index 25b7cdf533b..ebdb6362c8a 100644 --- a/blade.md +++ b/blade.md @@ -735,28 +735,13 @@ You should define all of the component's data attributes in its class constructo class Alert extends Component { - /** - * The alert type. - * - * @var string - */ - public $type; - - /** - * The alert message. - * - * @var string - */ - public $message; - /** * Create the component instance. */ - public function __construct(string $type, string $message) - { - $this->type = $type; - $this->message = $message; - } + public function __construct( + public string $type, + public string $message, + ) {} /** * Get the view / contents that represent the component. diff --git a/broadcasting.md b/broadcasting.md index 8ccb49cf6e3..9ef9a750286 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -375,20 +375,12 @@ 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. */ - public function __construct(User $user) - { - $this->user = $user; - } + public function __construct( + public User $user, + ) {} /** * Get the channels the event should broadcast on. diff --git a/collections.md b/collections.md index b74a6c727c6..ad0a9052e7f 100644 --- a/collections.md +++ b/collections.md @@ -1655,18 +1655,12 @@ The `pipeInto` method creates a new instance of the given class and passes the c class ResourceCollection { - /** - * The Collection instance. - */ - public $collection; - /** * Create a new ResourceCollection instance. */ - public function __construct(Collection $collection) - { - $this->collection = $collection; - } + public function __construct( + public Collection $collection, + ) {} } $collection = collect([1, 2, 3]); diff --git a/container.md b/container.md index 11cab659106..840f223c2bf 100644 --- a/container.md +++ b/container.md @@ -36,20 +36,12 @@ Let's look at a simple example: class UserController extends Controller { - /** - * The user repository implementation. - * - * @var UserRepository - */ - protected $users; - /** * Create a new controller instance. */ - public function __construct(UserRepository $users) - { - $this->users = $users; - } + public function __construct( + protected UserRepository $users, + ) {} /** * Show the profile for the given user. @@ -395,20 +387,12 @@ For example, you may type-hint a repository defined by your application in a con class UserController extends Controller { - /** - * The user repository instance. - * - * @var \App\Repositories\UserRepository - */ - protected $users; - /** * Create a new controller instance. */ - public function __construct(UserRepository $users) - { - $this->users = $users; - } + public function __construct( + protected UserRepository $users, + ) {} /** * Show the user with the given ID. diff --git a/contracts.md b/contracts.md index 51da8d56c8d..39052057333 100644 --- a/contracts.md +++ b/contracts.md @@ -48,20 +48,12 @@ For example, take a look at this event listener: class CacheOrderInformation { - /** - * The Redis factory implementation. - * - * @var \Illuminate\Contracts\Redis\Factory - */ - protected $redis; - /** * Create a new event handler instance. */ - public function __construct(Factory $redis) - { - $this->redis = $redis; - } + public function __construct( + protected Factory $redis, + ) {} /** * Handle the event. diff --git a/controllers.md b/controllers.md index 1f28209ee62..8f32ddffdac 100644 --- a/controllers.md +++ b/controllers.md @@ -465,18 +465,12 @@ The Laravel [service container](/docs/{{version}}/container) is used to resolve class UserController extends Controller { - /** - * The user repository instance. - */ - protected $users; - /** * Create a new controller instance. */ - public function __construct(UserRepository $users) - { - $this->users = $users; - } + public function __construct( + protected UserRepository $users, + ) {} } diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 32b91eb1c65..3ec715fb6a5 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -654,20 +654,12 @@ A classic example of an inbound only cast is a "hashing" cast. For example, we m class Hash implements CastsInboundAttributes { - /** - * The hashing algorithm. - * - * @var string - */ - protected $algorithm; - /** * Create a new cast class instance. */ - public function __construct(string $algorithm = null) - { - $this->algorithm = $algorithm; - } + public function __construct( + protected string $algorithm = null, + ) {} /** * Prepare the given value for storage. diff --git a/events.md b/events.md index 1161f461c7a..b2c85cdddd9 100644 --- a/events.md +++ b/events.md @@ -200,20 +200,12 @@ An event class is essentially a data container which holds the information relat { use Dispatchable, InteractsWithSockets, SerializesModels; - /** - * The order instance. - * - * @var \App\Models\Order - */ - public $order; - /** * Create a new event instance. */ - public function __construct(Order $order) - { - $this->order = $order; - } + public function __construct( + public Order $order, + ) {} } As you can see, this event class contains no logic. It is a container for the `App\Models\Order` instance that was purchased. The `SerializesModels` trait used by the event will gracefully serialize any Eloquent models if the event object is serialized using PHP's `serialize` function, such as when utilizing [queued listeners](#queued-event-listeners). diff --git a/horizon.md b/horizon.md index f8971617a16..9025f5f01fb 100644 --- a/horizon.md +++ b/horizon.md @@ -304,20 +304,12 @@ Horizon allows you to assign “tags” to jobs, including mailables, broadcast { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - /** - * The video instance. - * - * @var \App\Models\Video - */ - public $video; - /** * Create a new job instance. */ - public function __construct(Video $video) - { - $this->video = $video; - } + public function __construct( + public Video $video, + ) {} /** * Execute the job. diff --git a/mail.md b/mail.md index af1762f5225..754437b3fa9 100644 --- a/mail.md +++ b/mail.md @@ -281,20 +281,12 @@ Typically, you will want to pass some data to your view that you can utilize whe { use Queueable, SerializesModels; - /** - * The order instance. - * - * @var \App\Models\Order - */ - public $order; - /** * Create a new message instance. */ - public function __construct(Order $order) - { - $this->order = $order; - } + public function __construct( + public Order $order, + ) {} /** * Get the message content definition. @@ -332,20 +324,12 @@ If you would like to customize the format of your email's data before it is sent { use Queueable, SerializesModels; - /** - * The order instance. - * - * @var \App\Models\Order - */ - protected $order; - /** * Create a new message instance. */ - public function __construct(Order $order) - { - $this->order = $order; - } + public function __construct( + protected Order $order, + ) {} /** * Get the message content definition. @@ -1059,21 +1043,13 @@ Laravel includes a variety of mail transports; however, you may wish to write yo class MailchimpTransport extends AbstractTransport { - /** - * The Mailchimp API client. - * - * @var \MailchimpTransactional\ApiClient - */ - protected $client; - /** * Create a new Mailchimp transport instance. */ - public function __construct(ApiClient $client) - { + public function __construct( + protected ApiClient $client, + ) { parent::__construct(); - - $this->client = $client; } /** diff --git a/queues.md b/queues.md index 375ad77ec60..16b5d8d8653 100644 --- a/queues.md +++ b/queues.md @@ -181,20 +181,12 @@ Job classes are very simple, normally containing only a `handle` method that is { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - /** - * The podcast instance. - * - * @var \App\Models\Podcast - */ - public $podcast; - /** * Create a new job instance. */ - public function __construct(Podcast $podcast) - { - $this->podcast = $podcast; - } + public function __construct( + public Podcast $podcast, + ) {} /** * Execute the job. @@ -1747,20 +1739,12 @@ When a particular job fails, you may want to send an alert to your users or reve { use InteractsWithQueue, Queueable, SerializesModels; - /** - * The podcast instance. - * - * @var \App\Podcast - */ - public $podcast; - /** * Create a new job instance. */ - public function __construct(Podcast $podcast) - { - $this->podcast = $podcast; - } + public function __construct( + public Podcast $podcast, + ) {} /** * Execute the job. diff --git a/views.md b/views.md index e0ad54f993a..b73ea69dcdc 100644 --- a/views.md +++ b/views.md @@ -200,20 +200,12 @@ Now that we have registered the composer, the `compose` method of the `App\View\ class ProfileComposer { - /** - * The user repository implementation. - * - * @var \App\Repositories\UserRepository - */ - protected $users; - /** * Create a new profile composer. */ - public function __construct(UserRepository $users) - { - $this->users = $users; - } + public function __construct( + protected UserRepository $users, + ) {} /** * Bind data to the view. From b90de31cedff6aba557cf035c08328909264e7ab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 10 Feb 2023 10:12:46 -0600 Subject: [PATCH 0739/2609] pennant middleware --- pennant.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pennant.md b/pennant.md index 2c5dea4f69e..eeb34ac7ae8 100644 --- a/pennant.md +++ b/pennant.md @@ -8,6 +8,7 @@ - [Checking Features](#checking-features) - [Conditional Execution](#conditional-execution) - [Blade Directive](#blade-directive) + - [Middleware](#middleware) - [In-Memory Cache](#in-memory-cache) - [Scope](#scope) - [Specifying The Scope](#specifying-the-scope) @@ -276,6 +277,53 @@ To make checking features in Blade a seamless experience, Pennant offers a `@fea @endfeature ``` + +### Middleware + +Pennant also includes a [middleware](/docs/{{version}}/middleware) that may be used to verify the currently authenticated user has access to a feature before a route is even invoked. To get started, you should add a middleware alias for the `EnsureFeaturesAreActive` middleware to your application's `app/Http/Kernel.php` file: + +```php +use Laravel\Pennant\Middleware\EnsureFeaturesAreActive; + +protected $middlewareAliases = [ + // ... + 'features' => EnsureFeaturesAreActive::class, +]; +``` + +Next, you may assign the middleware to a route and specify the features that are required to access the route. If any of the specified features are inactive for the currently authenticated user, a `400 Bad Request` HTTP response will be returned by the route. Multiple features may be specified using a comma-delimited list: + +```php +Route::get('/api/servers', function () { + // ... +})->middleware(['features:new-api,servers-api']); +``` + + +#### Customizing The Response + +If you would like to customize the response that is returned by the middleware when one of the listed features is inactive, you may use the `whenInvalid` method provided by the `EnsureFeaturesAreActive` middleware. Typically, this method should be invoked within the `boot` method of one of your application's service providers: + +```php +use Illuminate\Http\Request; +use Illuminate\Http\Response; +use Laravel\Pennant\Middleware\EnsureFeaturesAreActive; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + EnsureFeaturesAreActive::whenInactive( + function (Request $request, array $features) { + return new Response(status: 403); + } + ); + + // ... +} +``` + ### In-Memory Cache From 198e665b20ccb817018cad82247c89e669f2bbbe Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Fri, 10 Feb 2023 16:55:34 +0000 Subject: [PATCH 0740/2609] Typo (#8544) --- pennant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index eeb34ac7ae8..7b596422440 100644 --- a/pennant.md +++ b/pennant.md @@ -502,7 +502,7 @@ Pennant's included Blade directive also makes it easy to conditionally render co @elsefeature('purchase-button', 'seafoam-green') @elsefeature('purchase-button', 'tart-orange') - + @endfeature ``` From 80b3b657aa633819dea6b082787ba7b61ae410b1 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 10 Feb 2023 19:44:46 +0000 Subject: [PATCH 0741/2609] Improves property promotion docs (#8546) --- blade.md | 32 +++++++++++++++----------------- cashier-paddle.md | 5 +---- collections.md | 9 ++++----- container.md | 28 ++++++++++------------------ helpers.md | 6 +++--- horizon.md | 2 +- http-tests.md | 4 ++-- localization.md | 4 +--- mail.md | 3 ++- notifications.md | 4 ++-- passport.md | 4 +--- queues.md | 2 +- validation.md | 4 +--- 13 files changed, 44 insertions(+), 63 deletions(-) diff --git a/blade.md b/blade.md index ebdb6362c8a..882a31e1cd3 100644 --- a/blade.md +++ b/blade.md @@ -768,10 +768,9 @@ Component constructor arguments should be specified using `camelCase`, while `ke /** * Create the component instance. */ - public function __construct(string $alertType) - { - $this->alertType = $alertType; - } + public function __construct( + public string $alertType, + ) {} The `$alertType` argument may be provided to the component like so: @@ -868,12 +867,11 @@ use App\Services\AlertCreator; /** * Create the component instance. */ -public function __construct(AlertCreator $creator, string $type, string $message) -{ - $this->creator = $creator; - $this->type = $type; - $this->message = $message; -} +public function __construct( + public AlertCreator $creator, + public string $type, + public string $message, +) {} ``` @@ -889,19 +887,19 @@ If you would like to prevent some public methods or properties from being expose class Alert extends Component { - /** - * The alert type. - * - * @var string - */ - public $type; - /** * The properties / methods that should not be exposed to the component template. * * @var array */ protected $except = ['type']; + + /** + * Create the component instance. + */ + public function __construct( + public string $type, + ) {} } diff --git a/cashier-paddle.md b/cashier-paddle.md index 141191151c9..d669ed67477 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -1232,11 +1232,8 @@ Alternatively, you can perform more precise customization by [listening](/docs/{ { /** * Handle received Paddle webhooks. - * - * @param \Laravel\Paddle\Events\WebhookReceived $event - * @return void */ - public function handle(WebhookReceived $event) + public function handle(WebhookReceived $event): void { if ($event->payload['alert_name'] === 'subscription_payment_failed') { // Handle the failed subscription payment... diff --git a/collections.md b/collections.md index ad0a9052e7f..6031e5e5b7e 100644 --- a/collections.md +++ b/collections.md @@ -1347,10 +1347,9 @@ The `mapInto()` method iterates over the collection, creating a new instance of /** * Create a new currency instance. */ - function __construct(string $code) - { - $this->code = $code; - } + function __construct( + public string $code + ) {} } $collection = collect(['USD', 'EUR', 'GBP']); @@ -1934,7 +1933,7 @@ The `reduceSpread` method reduces the collection to an array of values, passing [$creditsRemaining, $batch] = Image::where('status', 'unprocessed') ->get() - ->reduceSpread(function ($creditsRemaining, $batch, $image) { + ->reduceSpread(function (int $creditsRemaining, Collection $batch, Image $image) { if ($creditsRemaining >= $image->creditsRequired()) { $batch->push($image); diff --git a/container.md b/container.md index 840f223c2bf..b519f92a635 100644 --- a/container.md +++ b/container.md @@ -184,10 +184,9 @@ This statement tells the container that it should inject the `RedisEventPusher` /** * Create a new class instance. */ - public function __construct(EventPusher $pusher) - { - $this->pusher = $pusher; - } + public function __construct( + protected EventPusher $pusher + ) {} ### Contextual Binding @@ -247,13 +246,6 @@ Occasionally, you may have a class that receives an array of typed objects using class Firewall { - /** - * The logger instance. - * - * @var \App\Services\Logger - */ - protected $logger; - /** * The filter instances. * @@ -264,9 +256,10 @@ Occasionally, you may have a class that receives an array of typed objects using /** * Create a new class instance. */ - public function __construct(Logger $logger, Filter ...$filters) - { - $this->logger = $logger; + public function __construct( + protected Logger $logger, + Filter ...$filters, + ) { $this->filters = $filters; } } @@ -366,10 +359,9 @@ If you would like to have the Laravel container instance itself injected into a /** * Create a new class instance. */ - public function __construct(Container $container) - { - $this->container = $container; - } + public function __construct( + protected Container $container + ) {} ### Automatic Injection diff --git a/helpers.md b/helpers.md index c69dfc4165f..94daa8c59cc 100644 --- a/helpers.md +++ b/helpers.md @@ -928,7 +928,7 @@ You may also sort the array by the results of a given closure: ['name' => 'Chair'], ]; - $sorted = array_values(Arr::sortDesc($array, function ($value) { + $sorted = array_values(Arr::sortDesc($array, function (array $value) { return $value['name']; })); @@ -4072,8 +4072,8 @@ The `value` function returns the value it is given. However, if you pass a closu Additional arguments may be passed to the `value` function. If the first argument is a closure then the additional parameters will be passed to the closure as arguments, otherwise they will be ignored: - $result = value(function ($name) { - return $parameter; + $result = value(function (string $name) { + return $name; }, 'Taylor'); // 'Taylor' diff --git a/horizon.md b/horizon.md index 9025f5f01fb..136bd3e205d 100644 --- a/horizon.md +++ b/horizon.md @@ -143,7 +143,7 @@ Horizon exposes a dashboard at the `/horizon` URI. By default, you will only be #### Alternative Authentication Strategies -Remember that Laravel automatically injects the authenticated user into the gate closure. If your application is providing Horizon security via another method, such as IP restrictions, then your Horizon users may not need to "login". Therefore, you will need to change `function ($user)` closure signature above to `function ($user = null)` in order to force Laravel to not require authentication. +Remember that Laravel automatically injects the authenticated user into the gate closure. If your application is providing Horizon security via another method, such as IP restrictions, then your Horizon users may not need to "login". Therefore, you will need to change `function (User $user)` closure signature above to `function (User $user = null)` in order to force Laravel to not require authentication. ### Silenced Jobs diff --git a/http-tests.md b/http-tests.md index cba73690aac..735b4776317 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1056,7 +1056,7 @@ Assert that the session contains the given piece of data: If needed, a closure can be provided as the second argument to the `assertSessionHas` method. The assertion will pass if the closure returns `true`: - $response->assertSessionHas($key, function ($value) { + $response->assertSessionHas($key, function (User $value) { return $value->name === 'Taylor Otwell'; }); @@ -1069,7 +1069,7 @@ Assert that the session has a given value in the [flashed input array](/docs/{{v If needed, a closure can be provided as the second argument to the `assertSessionHasInput` method. The assertion will pass if the closure returns `true`: - $response->assertSessionHasInput($key, function ($value) { + $response->assertSessionHasInput($key, function (string $value) { return Crypt::decryptString($value) === 'secret'; }); diff --git a/localization.md b/localization.md index 9bae943210b..4246e5f8934 100644 --- a/localization.md +++ b/localization.md @@ -192,10 +192,8 @@ In these cases, Laravel allows you to register a custom formatting handler for t /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Lang::stringable(function (Money $money) { return $money->formatTo('en_GB'); diff --git a/mail.md b/mail.md index 754437b3fa9..b1d19f51e46 100644 --- a/mail.md +++ b/mail.md @@ -1039,6 +1039,7 @@ Laravel includes a variety of mail transports; however, you may wish to write yo use MailchimpTransactional\ApiClient; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\AbstractTransport; + use Symfony\Component\Mime\Address; use Symfony\Component\Mime\MessageConverter; class MailchimpTransport extends AbstractTransport @@ -1061,7 +1062,7 @@ Laravel includes a variety of mail transports; however, you may wish to write yo $this->client->messages->send(['message' => [ 'from_email' => $email->getFrom(), - 'to' => collect($email->getTo())->map(function ($email) { + 'to' => collect($email->getTo())->map(function (Address $email) { return ['email' => $email->getAddress(), 'type' => 'to']; })->all(), 'subject' => $email->getSubject(), diff --git a/notifications.md b/notifications.md index 8a26a2ec4a4..1716c9f6101 100644 --- a/notifications.md +++ b/notifications.md @@ -209,9 +209,9 @@ Or, if you would like to specify a specific queue connection that should be used /** * Determine which connections should be used for each notification channel. * - * @return array + * @return array */ - public function viaConnections() + public function viaConnections(): array { return [ 'mail' => 'redis', diff --git a/passport.md b/passport.md index 82e00b69837..ff59e9a03c6 100644 --- a/passport.md +++ b/passport.md @@ -255,10 +255,8 @@ Sometimes you may wish to customize the routes defined by Passport. To achieve t /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { Passport::ignoreRoutes(); } diff --git a/queues.md b/queues.md index 16b5d8d8653..a7d8a788157 100644 --- a/queues.md +++ b/queues.md @@ -954,7 +954,7 @@ Alternatively, you may specify the job's connection by calling the `onConnection /** * Create a new job instance. */ - public function __construct(): void + public function __construct() { $this->onConnection('sqs'); } diff --git a/validation.md b/validation.md index 674f2df2a19..4ef9e7a0cac 100644 --- a/validation.md +++ b/validation.md @@ -497,10 +497,8 @@ Likewise, if you need to normalize any request data after validation is complete /** * Handle a passed validation attempt. - * - * @return void */ - protected function passedValidation() + protected function passedValidation(): void { $this->replace(['name' => 'Taylor']); } From 7c0e1b48dd682786100236ebd1834ab02513d493 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 10 Feb 2023 13:59:02 -0600 Subject: [PATCH 0742/2609] wip --- mocking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocking.md b/mocking.md index f5f412879da..48122440881 100644 --- a/mocking.md +++ b/mocking.md @@ -188,7 +188,7 @@ You may use the `Bus` facade's `fake` method to prevent jobs from being dispatch } } -You may pass a closure to the available 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: +You may pass a closure to the assertion 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; From 09a64a1c27bae68ecbdc323d3562c7d48666cb65 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 11 Feb 2023 01:47:14 +0330 Subject: [PATCH 0743/2609] [10.x] Support native column modifying (#8456) * Update migrations.md * Update migrations.md * formatting --------- Co-authored-by: Taylor Otwell --- migrations.md | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/migrations.md b/migrations.md index be3f385af37..da5b09b1958 100644 --- a/migrations.md +++ b/migrations.md @@ -998,7 +998,7 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi }; > **Warning** -> Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. In addition, it is not possible to combine raw `default` expressions (using `DB::raw`) with column changes via the `change` method. +> Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. #### Column Order @@ -1014,10 +1014,22 @@ When using the MySQL database, the `after` method may be used to add columns aft ### Modifying Columns - -#### Prerequisites +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(); + }); + +When modifying a column, you must explicitly include all of the modifiers you want to keep on the column definition - any missing attribute will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column: -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: + Schema::table('users', function (Blueprint $table) { + $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); + }); + + +#### Modifying Columns On SQLite + +If your application is utilizing an SQLite database, you must install the `doctrine/dbal` package using the Composer package manager before modifying a column. 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 @@ -1034,25 +1046,7 @@ use Illuminate\Database\DBAL\TimestampType; ``` > **Warning** -> If your application is using Microsoft SQL Server, please ensure that you install `doctrine/dbal:^3.0`. - - -#### Updating Column Attributes - -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(); - }); - -We could also modify a column to be nullable: - - Schema::table('users', function (Blueprint $table) { - $table->string('name', 50)->nullable()->change(); - }); - -> **Warning** -> The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `double`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `tinyText`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). +> When using the `doctrine/dbal` package, the following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `double`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `tinyText`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, `ulid`, and `uuid`. ### Renaming Columns @@ -1091,7 +1085,6 @@ You may drop multiple columns from a table by passing an array of column names t $table->dropColumn(['votes', 'avatar', 'location']); }); - #### Dropping Columns On Legacy Databases From 3ebb71766688652de74cd1d5a4440a523cc39544 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 10 Feb 2023 16:40:07 -0600 Subject: [PATCH 0744/2609] add notes on ably drivers --- broadcasting.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/broadcasting.md b/broadcasting.md index 9ef9a750286..8e84dd40f87 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -115,6 +115,9 @@ The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) and [ ### Ably +> **Note** +> The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). + If you plan to broadcast your events using [Ably](https://ably.com), you should install the Ably PHP SDK using the Composer package manager: ```shell @@ -208,6 +211,9 @@ window.Echo = new Echo({ ### Ably +> **Note** +> The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). + [Laravel Echo](https://github.com/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: From 66f0294fdcf16e69967d4dcacd99b26fbf595d8b Mon Sep 17 00:00:00 2001 From: Adam Menczykowski Date: Sat, 11 Feb 2023 15:04:16 +0000 Subject: [PATCH 0745/2609] Correct whenInactive sentence (#8548) --- pennant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 7b596422440..054c33ed416 100644 --- a/pennant.md +++ b/pennant.md @@ -302,7 +302,7 @@ Route::get('/api/servers', function () { #### Customizing The Response -If you would like to customize the response that is returned by the middleware when one of the listed features is inactive, you may use the `whenInvalid` method provided by the `EnsureFeaturesAreActive` middleware. Typically, this method should be invoked within the `boot` method of one of your application's service providers: +If you would like to customize the response that is returned by the middleware when one of the listed features is inactive, you may use the `whenInactive` method provided by the `EnsureFeaturesAreActive` middleware. Typically, this method should be invoked within the `boot` method of one of your application's service providers: ```php use Illuminate\Http\Request; From 99cf56b0209950c6a80f25d5e7396f0082cf1093 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Sat, 11 Feb 2023 18:43:41 +0330 Subject: [PATCH 0746/2609] [9.x] Add original rate limiter in laravel (#8549) * add original rate limiter in laravel * Update routing.md * Update routing.md --------- Co-authored-by: Taylor Otwell --- routing.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 4aa397aab80..90d146d1ba5 100644 --- a/routing.md +++ b/routing.md @@ -671,7 +671,23 @@ Using the `Route::fallback` method, you may define a route that will be executed ### Defining Rate Limiters -Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this should be done within the `configureRateLimiting` method of your application's `App\Providers\RouteServiceProvider` class. +Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this should be done within the `configureRateLimiting` method of your application's `App\Providers\RouteServiceProvider` class, which already includes a rate limiter definition that is applied to the routes in your application's `routes/api.php` file: + +```php +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\RateLimiter; + +/** + * Configure the rate limiters for the application. + */ +protected function configureRateLimiting(): void +{ + RateLimiter::for('api', function (Request $request) { + return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); + }); +} +``` 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 to the rate limiter. Limit configuration are instances of the `Illuminate\Cache\RateLimiting\Limit` class. This class contains helpful "builder" methods so that you can quickly define your limit. The rate limiter name may be any string you wish: From b699d17c778f20600ab369769c6e7accbbbda0de Mon Sep 17 00:00:00 2001 From: Volodya Kurshudyan <70023120+xurshudyan@users.noreply.github.com> Date: Sat, 11 Feb 2023 21:14:58 +0400 Subject: [PATCH 0747/2609] [9.x] Add docs for pushQuietly method (#8547) * Add pushQuietly method * Update eloquent-relationships.md --------- Co-authored-by: Valodia Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 339910cbc95..84c91130955 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1860,6 +1860,10 @@ If you would like to `save` your model and all of its associated relationships, $post->push(); +The `pushQuietly` method may be used to save a model and its associated relationships without raising any events: + + $post->pushQuietly(); + ### The `create` Method From 1f9a3e3fa24902b3a84971527b0716ee7fc24950 Mon Sep 17 00:00:00 2001 From: Nico <3315078+nicolus@users.noreply.github.com> Date: Sun, 12 Feb 2023 21:00:14 +0100 Subject: [PATCH 0748/2609] Update VONAGE_SMS_FROM documentation (#8555) * Update VONAGE_SMS_FROM documentation Make it clearer that you should set this environment variable for Vonage notifications to work * Update notifications.md --------- Co-authored-by: Taylor Otwell --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 0f0119f8acb..65950c9562e 100644 --- a/notifications.md +++ b/notifications.md @@ -1016,7 +1016,7 @@ Sending SMS notifications in Laravel is powered by [Vonage](https://www.vonage.c The package includes a [configuration file](https://github.com/laravel/vonage-notification-channel/blob/3.x/config/vonage.php). However, you are not required to export this configuration file to your own application. You can simply use the `VONAGE_KEY` and `VONAGE_SECRET` environment variables to define your Vonage public and secret keys. -After defining your keys, you may set a `VONAGE_SMS_FROM` environment variable that defines the phone number that your SMS messages should be sent from by default. You may generate this phone number within the Vonage control panel: +After defining your keys, you should set a `VONAGE_SMS_FROM` environment variable that defines the phone number that your SMS messages should be sent from by default. You may generate this phone number within the Vonage control panel: VONAGE_SMS_FROM=15556666666 From dd7bbe1d796f943eb864bcc656bfb7efa9d1bdbf Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 13 Feb 2023 15:14:20 +0000 Subject: [PATCH 0749/2609] [10.x] Specifies that Laravel now requires Composer 2.2 or greater (#8564) * Specifies that Laravel now requires Composer 2.2 or greater * Makes version format similar to PHP one * Update upgrade.md * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/upgrade.md b/upgrade.md index b4efa237ea6..2e2a393d44d 100644 --- a/upgrade.md +++ b/upgrade.md @@ -59,6 +59,10 @@ Laravel now requires PHP 8.1.0 or greater. +#### Composer 2.2.0 Required + +Laravel now requires [Composer](https://getcomposer.org) 2.2.0 or greater. + #### Composer Dependencies You should update the following dependencies in your application's `composer.json` file: From 8baa576720a7ca6fb6fd1f7ad7ec440e3056c9b8 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 13 Feb 2023 15:15:38 +0000 Subject: [PATCH 0750/2609] Clarifies builder on `makeAllSearchableUsing` (#8562) --- scout.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scout.md b/scout.md index 08e1f8ed9f1..a0ee87e2199 100644 --- a/scout.md +++ b/scout.md @@ -395,6 +395,8 @@ php artisan scout:flush "App\Models\Post" If you would like to modify the query that is used to retrieve all of your models for batch importing, you may define a `makeAllSearchableUsing` method on your model. This is a great place to add any eager relationship loading that may be necessary before importing your models: + use Illuminate\Database\Eloquent\Builder; + /** * Modify the query used to retrieve models when making all of the models searchable. */ From 5252f1dbc87a41de347c2b5b57f07d70f2b6d2aa Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 14 Feb 2023 02:21:37 +1100 Subject: [PATCH 0751/2609] Add note to middleware about authentication (#8560) --- pennant.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 054c33ed416..14cb98f5014 100644 --- a/pennant.md +++ b/pennant.md @@ -296,9 +296,11 @@ Next, you may assign the middleware to a route and specify the features that are ```php Route::get('/api/servers', function () { // ... -})->middleware(['features:new-api,servers-api']); +})->middleware(['auth', 'features:new-api,servers-api']); ``` +> **Note** As the `feature` middleware will check against the currently authenticated user, you should ensure that any authentication related middleware is applied _before_ the `feature` middleware. + #### Customizing The Response From 05eaeaa221a87c95436a028ee21fc8bf5f7ab9ba Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 14 Feb 2023 02:25:55 +1100 Subject: [PATCH 0752/2609] [10.x] Documents passing array to `for` (#8556) * Documents passing array for `for` * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 14cb98f5014..b2e1f4d2f1c 100644 --- a/pennant.md +++ b/pennant.md @@ -380,10 +380,25 @@ if (Feature::for($user->team)->active('billing-v2')) { // ... ``` +The `for` method also accepts an array of scopes, which will check that the feature is active for all of the provided scopes: + +```php +Feature::define('improved-notifications', function (string $email) { + return str_ends_with($email, '@example.com'); +}); + +Feature::for([ + 'anthony@example.com', + 'taylor@laravel.com', +])->active('improved-notifications'); + +// false +``` + ### Default Scope -It is also possible to customize the default scope Pennant uses to check features. For example, maybe all of your features are checked against the currently authenticated user's team instead of the user. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: +Sometimes, you may need to customize the default scope Pennant uses to check features. For example, maybe all of your features are checked against the currently authenticated user's team instead of the user. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: ```php Date: Tue, 14 Feb 2023 02:41:05 +1100 Subject: [PATCH 0753/2609] [10.x] Documents `Feature::values()` (#8559) * Documents `Feature::values()` * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pennant.md b/pennant.md index b2e1f4d2f1c..ab4f35d301a 100644 --- a/pennant.md +++ b/pennant.md @@ -523,6 +523,17 @@ Pennant's included Blade directive also makes it easy to conditionally render co @endfeature ``` +If you would like to retrieve multiple feature values at once for a single scope, you may use the `values` method: + +```php +Feature::values(['new-api', 'purchase-button']); + +// [ +// 'new-api' => false, +// 'purchase-button' => 'tart-orange', +// ] +``` + > **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. From def6fd1074a5d93c08499a4adee970385384cb75 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 14 Feb 2023 02:43:40 +1100 Subject: [PATCH 0754/2609] Improves events documentation (#8558) --- pennant.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pennant.md b/pennant.md index ab4f35d301a..eb44da9fb9c 100644 --- a/pennant.md +++ b/pennant.md @@ -661,13 +661,17 @@ php artisan pennant:purge new-api purchase-button Pennant dispatches a variety of events that can be useful when tracking feature flags throughout your application. -### `Laravel\Pennant\Events\RetrievingKnownFeature` +### `Laravel\Pennant\Events\FeatureRetrieved` -This event is dispatched the first time a known feature is retrieved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are being used throughout your application. +This event is dispatched whenever a feature is retrieved and can be useful for creating and tracking metrics against a feature flag's usage throughout your application. -### `Laravel\Pennant\Events\RetrievingUnknownFeature` +### `Laravel\Pennant\Events\FeatureResolved` -This event is dispatched the first time an unknown feature is retrieved during a request for a specific scope. This event can be useful if you have intended to remove a feature flag, but may have accidentally left some stray references to it throughout your application. +This event is dispatched the first time a feature's value is resolved for a specific scope from it's definition or feature class resolver. + +### `Laravel\Pennant\Events\UnknownFeatureResolved` + +This event is dispatched the first time an unknown feature is resolved for a specific scope. This event can be useful if you have intended to remove a feature flag, but may have accidentally left some stray references to it throughout your application. For example, you may find it useful to listen for this event and `report` or throw an exception when it occurs: @@ -678,7 +682,7 @@ namespace App\Providers; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; -use Laravel\Pennant\Events\RetrievingUnknownFeature; +use Laravel\Pennant\Events\UnknownFeatureResolved; class EventServiceProvider extends ServiceProvider { @@ -687,13 +691,13 @@ class EventServiceProvider extends ServiceProvider */ public function boot(): void { - Event::listen(function (RetrievingUnknownFeature $event) { - report("Resolving unknown feature [{$event->feature}]."); + Event::listen(function (UnknownFeatureResolved $event) { + report("Unknown feature resolved [{$event->feature}]."); }); } } ``` -### `Laravel\Pennant\Events\DynamicallyDefiningFeature` +### `Laravel\Pennant\Events\DynamicallyRegisteringFeatureClass` This event is dispatched when a class based feature is being dynamically checked for the first time during a request. From aca2ca410144c3d814239d7c3f43c204bf4164ed Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 14 Feb 2023 04:13:05 +1100 Subject: [PATCH 0755/2609] [10.x] Add testing documentation (#8561) * Add testing docs to Pennant * formatting --------- Co-authored-by: Taylor Otwell --- pennant.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pennant.md b/pennant.md index eb44da9fb9c..86895f918f5 100644 --- a/pennant.md +++ b/pennant.md @@ -21,6 +21,7 @@ - [Bulk Updates](#bulk-updates) - [Purging Features](#purging-features) - [Events](#events) +- [Testing](#testing) ## Introduction @@ -701,3 +702,48 @@ class EventServiceProvider extends ServiceProvider ### `Laravel\Pennant\Events\DynamicallyRegisteringFeatureClass` This event is dispatched when a class based feature is being dynamically checked for the first time during a request. + + +## Testing + +When testing code that interacts with feature flags, the easiest way to control the feature flag's returned value in your tests is to simply re-define the feature. For example, imagine you have the following feature defined in one of your application's service provider: + +```php +use Illuminate\Support\Arr; +use Laravel\Pennant\Feature; + +Feature::define('purchase-button', fn () => Arr::random([ + 'blue-sapphire', + 'seafoam-green', + 'tart-orange', +])); +``` + +To modify the feature's returned value in your tests, you may re-define the feature at the beginning of the test. The following test will always pass, even though the `Arr::random()` implementation is still present in the service provider: + +```php +use Laravel\Pennant\Feature; + +public function test_it_can_control_feature_values() +{ + Feature::define('purchase-button', 'seafoam-green'); + + $this->assertSame('seafoam-green', Feature::value('purchase-button')); +} +``` + +The same approach may be used for class based features: + +```php +use App\Features\NewApi; +use Laravel\Pennant\Feature; + +public function test_it_can_control_feature_values() +{ + Feature::define(NewApi::class, true); + + $this->assertTrue(Feature::value(NewApi::class)); +} +``` + +If your feature is returning a `Lottery` instance, there are a handful of useful [testing helpers available](/docs/{{version}}/helpers#testing-lotteries). From 075b0ceebac575e24c9c00958e3f662d43809c00 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 13 Feb 2023 11:14:01 -0600 Subject: [PATCH 0756/2609] wip --- pennant.md | 102 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/pennant.md b/pennant.md index 86895f918f5..1691ef03832 100644 --- a/pennant.md +++ b/pennant.md @@ -20,6 +20,7 @@ - [Updating Values](#updating-values) - [Bulk Updates](#bulk-updates) - [Purging Features](#purging-features) +- [Testing](#testing) - [Events](#events) - [Testing](#testing) @@ -175,7 +176,7 @@ return Feature::for($user)->active('new-api') : $this->resolveLegacyApiResponse($request); ``` -> **Note** +> **Note** > When using Pennant outside of an HTTP context, such as in an Artisan command or a queued job, you should typically [explicitly specify the feature's scope](#specifying-the-scope). Alternatively, you may define a [default scope](#default-scope) that accounts for both authenticated HTTP contexts and unauthenticated contexts. @@ -254,7 +255,7 @@ The `when` method may be used to fluently execute a given closure if a feature i fn () => $this->resolveLegacyApiResponse($request), ); } - + // ... } @@ -297,11 +298,9 @@ Next, you may assign the middleware to a route and specify the features that are ```php Route::get('/api/servers', function () { // ... -})->middleware(['auth', 'features:new-api,servers-api']); +})->middleware(['features:new-api,servers-api']); ``` -> **Note** As the `feature` middleware will check against the currently authenticated user, you should ensure that any authentication related middleware is applied _before_ the `feature` middleware. - #### Customizing The Response @@ -381,25 +380,10 @@ if (Feature::for($user->team)->active('billing-v2')) { // ... ``` -The `for` method also accepts an array of scopes, which will check that the feature is active for all of the provided scopes: - -```php -Feature::define('improved-notifications', function (string $email) { - return str_ends_with($email, '@example.com'); -}); - -Feature::for([ - 'anthony@example.com', - 'taylor@laravel.com', -])->active('improved-notifications'); - -// false -``` - ### Default Scope -Sometimes, you may need to customize the default scope Pennant uses to check features. For example, maybe all of your features are checked against the currently authenticated user's team instead of the user. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: +It is also possible to customize the default scope Pennant uses to check features. For example, maybe all of your features are checked against the currently authenticated user's team instead of the user. Instead of having to call `Feature::for($user->team)` every time you check a feature, you may instead specify the team as the default scope. Typically, this should be done in one of your application's service providers: ```php false, -// 'purchase-button' => 'tart-orange', -// ] -``` - > **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. @@ -657,22 +630,63 @@ php artisan pennant:purge new-api php artisan pennant:purge new-api purchase-button ``` + +## Testing + +When testing code that interacts with feature flags, the easiest way to control the feature flag's returned value in your tests is to simply re-define the feature. For example, imagine you have the following feature defined in one of your application's service provider: + +```php +use Illuminate\Support\Arr; +use Laravel\Pennant\Feature; + +Feature::define('purchase-button', fn () => Arr::random([ + 'blue-sapphire', + 'seafoam-green', + 'tart-orange', +])); +``` + +To modify the feature's returned value in your tests, you may re-define the feature at the beginning of the test. The following test will always pass, even though the `Arr::random()` implementation is still present in the service provider: + +```php +use Laravel\Pennant\Feature; + +public function test_it_can_control_feature_values() +{ + Feature::define('purchase-button', 'seafoam-green'); + + $this->assertSame('seafoam-green', Feature::value('purchase-button')); +} +``` + +The same approach may be used for class based features: + +```php +use App\Features\NewApi; +use Laravel\Pennant\Feature; + +public function test_it_can_control_feature_values() +{ + Feature::define(NewApi::class, true); + + $this->assertTrue(Feature::value(NewApi::class)); +} +``` + +If your feature is returning a `Lottery` instance, there are a handful of useful [testing helpers available](/docs/{{version}}/helpers#testing-lotteries). + ## Events Pennant dispatches a variety of events that can be useful when tracking feature flags throughout your application. -### `Laravel\Pennant\Events\FeatureRetrieved` - -This event is dispatched whenever a feature is retrieved and can be useful for creating and tracking metrics against a feature flag's usage throughout your application. - -### `Laravel\Pennant\Events\FeatureResolved` +### `Laravel\Pennant\Events\RetrievingKnownFeature` -This event is dispatched the first time a feature's value is resolved for a specific scope from it's definition or feature class resolver. +This event is dispatched the first time a known feature is retrieved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are being used throughout your application. -### `Laravel\Pennant\Events\UnknownFeatureResolved` +### `Laravel\Pennant\Events\RetrievingUnknownFeature` -This event is dispatched the first time an unknown feature is resolved for a specific scope. This event can be useful if you have intended to remove a feature flag, but may have accidentally left some stray references to it throughout your application. +This event is dispatched the first time an unknown feature is retrieved during a request for a specific scope. This event can be useful if you have intended to remove a feature flag, but may have accidentally left some stray references to it throughout your application. For example, you may find it useful to listen for this event and `report` or throw an exception when it occurs: @@ -683,7 +697,7 @@ namespace App\Providers; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; -use Laravel\Pennant\Events\UnknownFeatureResolved; +use Laravel\Pennant\Events\RetrievingUnknownFeature; class EventServiceProvider extends ServiceProvider { @@ -692,14 +706,14 @@ class EventServiceProvider extends ServiceProvider */ public function boot(): void { - Event::listen(function (UnknownFeatureResolved $event) { - report("Unknown feature resolved [{$event->feature}]."); + Event::listen(function (RetrievingUnknownFeature $event) { + report("Resolving unknown feature [{$event->feature}]."); }); } } ``` -### `Laravel\Pennant\Events\DynamicallyRegisteringFeatureClass` +### `Laravel\Pennant\Events\DynamicallyDefiningFeature` This event is dispatched when a class based feature is being dynamically checked for the first time during a request. From 0b59c836ce692d6dc618e39173ed725edcfe2de9 Mon Sep 17 00:00:00 2001 From: Mohammad Emran Date: Mon, 13 Feb 2023 23:33:29 +0600 Subject: [PATCH 0757/2609] [10.x] Add documentation for custom pennant driver (#8553) * Add documentation for custom pennant driver * remove conflict --------- Co-authored-by: Taylor Otwell --- pennant.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 1691ef03832..06d88c1be14 100644 --- a/pennant.md +++ b/pennant.md @@ -21,8 +21,10 @@ - [Bulk Updates](#bulk-updates) - [Purging Features](#purging-features) - [Testing](#testing) +- [Adding Custom Pennant Drivers](#adding-custom-pennant-drivers) + - [Implementing The Driver](#implementing-the-driver) + - [Registering The Driver](#registering-the-driver) - [Events](#events) -- [Testing](#testing) ## Introduction @@ -675,6 +677,89 @@ public function test_it_can_control_feature_values() If your feature is returning a `Lottery` instance, there are a handful of useful [testing helpers available](/docs/{{version}}/helpers#testing-lotteries). + +## Adding Custom Pennant Drivers + + +#### Implementing The Driver + +If none of Pennant's existing storage drivers fit your application's needs, you may write your own storage driver. Your custom driver should implement the `Laravel\Pennant\Contracts\Driver` interface: + +```php + **Note** +> Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `RedisFeatureDriver`. + + +#### Registering The Driver + +Once your driver has been implemented, you are ready to register it with Laravel. To add additional drivers to Pennant, you may use the `extend` method provided by the `Feature` facade. You should call the `extend` method from the `boot` method of one of your application's [service provider](/docs/{{version}}/providers): + +```php +make('redis'), $app->make('events'), []); + }); + } +} +``` + +Once the driver has been registered, you may use the `redis` driver in your application's `config/pennant.php` configuration file: + + 'stores' => [ + + 'redis' => [ + 'driver' => 'redis', + 'connection' => null, + ], + + // ... + + ], + ## Events From fee9726bde2bd10d45947a5d459bf8992fa9e697 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 13 Feb 2023 11:51:34 -0600 Subject: [PATCH 0758/2609] document password --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 94daa8c59cc..3d33533066f 100644 --- a/helpers.md +++ b/helpers.md @@ -133,6 +133,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::padBoth](#method-str-padboth) [Str::padLeft](#method-str-padleft) [Str::padRight](#method-str-padright) +[Str::password](#method-str-password) [Str::plural](#method-str-plural) [Str::pluralStudly](#method-str-plural-studly) [Str::random](#method-str-random) @@ -1739,6 +1740,21 @@ The `Str::padRight` method wraps PHP's `str_pad` function, padding the right sid // 'James ' + +#### `Str::password()` {.collection-method} + +The `Str::password` method may be used to generate a secure, random password of a given length. The password will consist of a combination of letters, numbers, symbols, and spaces. By default, passwords are 32 characters long: + + use Illuminate\Support\Str; + + $password = Str::password(); + + // 'EbJo2vE-AS:U,$%_gkrV4n,q~1xy/-_4' + + $password = Str::password(12); + + // 'qwuar>#V|i]N' + #### `Str::plural()` {.collection-method} From 5ada8684ac410f63bdf2693ff79d8618518e39f1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 13 Feb 2023 12:07:10 -0600 Subject: [PATCH 0759/2609] document has features --- pennant.md | 96 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/pennant.md b/pennant.md index 06d88c1be14..da82835a858 100644 --- a/pennant.md +++ b/pennant.md @@ -7,6 +7,7 @@ - [Class Based Features](#class-based-features) - [Checking Features](#checking-features) - [Conditional Execution](#conditional-execution) + - [The `HasFeatures` Trait](#the-has-features-trait) - [Blade Directive](#blade-directive) - [Middleware](#middleware) - [In-Memory Cache](#in-memory-cache) @@ -178,6 +179,25 @@ return Feature::for($user)->active('new-api') : $this->resolveLegacyApiResponse($request); ``` +Pennant also offers some additional convenience methods that may prove useful when determining if a feature is active or not: + +```php +// Determine if all of the given features are active... +Feature::allAreActive(['new-api', 'site-redesign']); + +// Determine if any of the given features are active... +Feature::someAreActive(['new-api', 'site-redesign']); + +// Determine if a feature is inactive... +Feature::inactive('new-api'); + +// Determine if all of the given features are inactive... +Feature::allAreInactive(['new-api', 'site-redesign']); + +// Determine if any of the given features are inactive... +Feature::someAreInactive(['new-api', 'site-redesign']); +``` + > **Note** > When using Pennant outside of an HTTP context, such as in an Artisan command or a queued job, you should typically [explicitly specify the feature's scope](#specifying-the-scope). Alternatively, you may define a [default scope](#default-scope) that accounts for both authenticated HTTP contexts and unauthenticated contexts. @@ -212,25 +232,6 @@ class PodcastController } ``` -Pennant also offers some additional convenience methods that may prove useful when determining if a feature is active or not: - -```php -// Determine if all of the given features are active... -Feature::allAreActive(['new-api', 'site-redesign']); - -// Determine if any of the given features are active... -Feature::someAreActive(['new-api', 'site-redesign']); - -// Determine if a feature is inactive... -Feature::inactive('new-api'); - -// Determine if all of the given features are inactive... -Feature::allAreInactive(['new-api', 'site-redesign']); - -// Determine if any of the given features are inactive... -Feature::someAreInactive(['new-api', 'site-redesign']); -``` - ### Conditional Execution @@ -268,6 +269,63 @@ The `unless` method serves as the inverse of the `when` method, executing the fi fn () => $this->resolveNewApiResponse($request), ); + +### The `HasFeatures` Trait + +Pennant's `HasFeatures` trait may be added to your application's `User` model (or any other model that has features) to provide a fluent, convenient way to check features directly from the model: + +```php +features()->active('new-api')) { + // ... +} +``` + +Of course, the `features` method provides access to many other convenient methods for interacting with features: + +```php +// Values... +$value = $user->features()->value('purchase-button') +$values = $user->features()->values(['new-api', 'purchase-button']); + +// State... +$user->features()->active('new-api'); +$user->features()->allAreActive(['new-api', 'server-api']); +$user->features()->someAreActive(['new-api', 'server-api']); + +$user->features()->inactive('new-api'); +$user->features()->allAreInactive(['new-api', 'server-api']); +$user->features()->someAreInactive(['new-api', 'server-api']); + +// Conditional execution... +$user->features()->when('new-api', + fn () => /* ... */, + fn () => /* ... */, +); + +$user->features()->unless('new-api', + fn () => /* ... */, + fn () => /* ... */, +); +``` + ### Blade Directive From d10357f171872ba6fe1960594ad3494485228cef Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 13 Feb 2023 13:58:47 -0600 Subject: [PATCH 0760/2609] wip --- vite.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vite.md b/vite.md index 628ff0eb9c1..6a2a0ee7d8f 100644 --- a/vite.md +++ b/vite.md @@ -407,6 +407,9 @@ module.exports = { }; ``` +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Tailwind, PostCSS, and Vite configuration. Or, if you would like to use Tailwind and Laravel without using one of our starter kits, check out [Tailwind's installation guide for Laravel](https://tailwindcss.com/docs/guides/laravel). + ## Working With Blade & Routes From 78be9368b7fbfc0ef21d873e4d073d5cd63ea36c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 13 Feb 2023 15:49:01 -0600 Subject: [PATCH 0761/2609] Put fake documentation on the actual pages (#8565) * move fake documentation into the actual documentation * additional info * Update events.md * Update queues.md --- events.md | 124 ++++++++++- filesystem.md | 43 ++++ mail.md | 102 ++++++++- mocking.md | 538 ----------------------------------------------- notifications.md | 67 ++++++ queues.md | 136 ++++++++++++ upgrade.md | 2 +- 7 files changed, 468 insertions(+), 544 deletions(-) diff --git a/events.md b/events.md index b2c85cdddd9..65b08c4fd67 100644 --- a/events.md +++ b/events.md @@ -15,6 +15,9 @@ - [Event Subscribers](#event-subscribers) - [Writing Event Subscribers](#writing-event-subscribers) - [Registering Event Subscribers](#registering-event-subscribers) +- [Testing](#testing) + - [Faking A Subset Of Events](#faking-a-subset-of-events) + - [Scoped Events Fakes](#scoped-event-fakes) ## Introduction @@ -519,7 +522,7 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th OrderShipped::dispatchUnless($condition, $order); > **Note** -> When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](/docs/{{version}}/mocking#event-fake) makes it a cinch. +> When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) makes it a cinch. ## Event Subscribers @@ -634,3 +637,122 @@ After writing the subscriber, you are ready to register it with the event dispat UserEventSubscriber::class, ]; } + + +## Testing + +When testing code that dispatches events, you may wish to instruct Laravel to not actually execute the event's listeners, since the listener's code can be tested directly and separately of the code that dispatches the corresponding event. Of course, to test the listener itself, you may instantiate a listener instance and invoke the `handle` method directly in your test. + +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`, `assertNotDispatched`, and `assertNothingDispatched` methods: + + order->id === $order->id; + }); + +If you would simply like to assert that an event listener is listening to a given event, you may use the `assertListening` method: + + Event::assertListening( + OrderShipped::class, + SendShipmentNotification::class + ); + +> **Warning** +> 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. + + +### Faking A Subset Of Events + +If you only want to fake event listeners for a specific set of events, you may pass them to the `fake` or `fakeFor` method: + + /** + * Test order process. + */ + public function test_orders_can_be_processed(): void + { + Event::fake([ + OrderCreated::class, + ]); + + $order = Order::factory()->create(); + + Event::assertDispatched(OrderCreated::class); + + // Other events are dispatched as normal... + $order->update([...]); + } + +You may fake all events except for a set of specified events using the `except` method: + + Event::fake()->except([ + OrderCreated::class, + ]); + + +### Scoped Event Fakes + +If you only want to fake event listeners for a portion of your test, you may use the `fakeFor` method: + + create(); + + Event::assertDispatched(OrderCreated::class); + + return $order; + }); + + // Events are dispatched as normal and observers will run ... + $order->update([...]); + } + } diff --git a/filesystem.md b/filesystem.md index f7a8fae80f6..96a166b4c2d 100644 --- a/filesystem.md +++ b/filesystem.md @@ -21,6 +21,7 @@ - [File Visibility](#file-visibility) - [Deleting Files](#deleting-files) - [Directories](#directories) +- [Testing](#testing) - [Custom Filesystems](#custom-filesystems) @@ -608,6 +609,48 @@ Finally, the `deleteDirectory` method may be used to remove a directory and all Storage::deleteDirectory($directory); + +## Testing + +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: + + json('POST', '/photos', [ + UploadedFile::fake()->image('photo1.jpg'), + UploadedFile::fake()->image('photo2.jpg') + ]); + + // Assert one or more files were stored... + Storage::disk('photos')->assertExists('photo1.jpg'); + Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); + + // Assert one or more files were not stored... + Storage::disk('photos')->assertMissing('missing.jpg'); + Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); + + // Assert that a given directory is empty... + Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); + } + } + +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. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). + +> **Warning** +> The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). + ## Custom Filesystems diff --git a/mail.md b/mail.md index b1d19f51e46..77a736594b5 100644 --- a/mail.md +++ b/mail.md @@ -24,7 +24,9 @@ - [Rendering Mailables](#rendering-mailables) - [Previewing Mailables In The Browser](#previewing-mailables-in-the-browser) - [Localizing Mailables](#localizing-mailables) -- [Testing Mailables](#testing-mailables) +- [Testing](#testing-mailables) + - [Testing Mailable Content](#testing-mailable-content) + - [Testing Mailable Sending](#testing-mailable-sending) - [Mail & Local Development](#mail-and-local-development) - [Events](#events) - [Custom Transports](#custom-transports) @@ -929,7 +931,10 @@ Once you have implemented the interface, Laravel will automatically use the pref Mail::to($request->user())->send(new OrderShipped($order)); -## Testing Mailables +## Testing + + +### Testing Mailable Content Laravel provides a variety of methods for inspecting your mailable's structure. In addition, Laravel provides several convenient methods for testing that your mailable contains the content that you expect. These methods are: `assertSeeInHtml`, `assertDontSeeInHtml`, `assertSeeInOrderInHtml`, `assertSeeInText`, `assertDontSeeInText`, `assertSeeInOrderInText`, `assertHasAttachment`, `assertHasAttachedData`, `assertHasAttachmentFromStorage`, and `assertHasAttachmentFromStorageDisk`. @@ -968,9 +973,98 @@ As you might expect, the "HTML" assertions assert that the HTML version of your } -#### Testing Mailable Sending +### Testing Mailable Sending + +We suggest testing the content of your mailables separately from your tests that assert that a given mailable was "sent" to a specific user. Typically, the content of mailables it not relevant to the code you are testing, and it is sufficient to simply assert that Laravel was instructed to send a given mailable. + +You may use the `Mail` facade's `fake` method to prevent mail from being sent. After calling the `Mail` facade's `fake` method, you may then assert that mailables were instructed to be sent to users and even inspect the data the mailables received: + + 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 mailable: + + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) { + return $mail->hasTo($user->email) && + $mail->hasCc('...') && + $mail->hasBcc('...') && + $mail->hasReplyTo('...') && + $mail->hasFrom('...') && + $mail->hasSubject('...'); + }); + +The mailable instance also includes several helpful methods for examining the attachments on a mailable: + + use Illuminate\Mail\Mailables\Attachment; + + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { + return $mail->hasAttachment( + Attachment::fromPath('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf') + ); + }); + + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { + return $mail->hasAttachment( + Attachment::fromStorageDisk('s3', '/path/to/file') + ); + }); + + Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($pdfData) { + return $mail->hasAttachment( + Attachment::fromData(fn () => $pdfData, 'name.pdf') + ); + }); + +You may have noticed that there are two methods for asserting that mail was not sent: `assertNotSent` and `assertNotQueued`. Sometimes you may wish to assert that no mail was sent **or** queued. To accomplish this, you may use the `assertNothingOutgoing` and `assertNotOutgoing` methods: + + Mail::assertNothingOutgoing(); + + Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) { + return $mail->order->id === $order->id; + }); ## Mail & Local Development diff --git a/mocking.md b/mocking.md index 48122440881..a50179d57ad 100644 --- a/mocking.md +++ b/mocking.md @@ -4,17 +4,6 @@ - [Mocking Objects](#mocking-objects) - [Mocking Facades](#mocking-facades) - [Facade Spies](#facade-spies) -- [Bus Fake](#bus-fake) - - [Job Chains](#bus-job-chains) - - [Job Batches](#bus-job-batches) -- [Event Fake](#event-fake) - - [Scoped Event Fakes](#scoped-event-fakes) -- [HTTP Fake](#http-fake) -- [Mail Fake](#mail-fake) -- [Notification Fake](#notification-fake) -- [Process Fake](#process-fake) -- [Queue Fake](#queue-fake) -- [Storage Fake](#storage-fake) - [Interacting With Time](#interacting-with-time) @@ -142,533 +131,6 @@ If you would like to [spy](http://docs.mockery.io/en/latest/reference/spies.html Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10); } - -## Bus Fake - -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; - }); - - -#### Faking A Subset Of Jobs - -If you only want to prevent certain jobs from being dispatched, you may pass the jobs that should be faked to the `fake` method: - - /** - * Test order process. - */ - public function test_orders_can_be_shipped() - { - Bus::fake([ - ShipOrder::class, - ]); - - // ... - } - -You may fake all jobs except for a set of specified jobs using the `except` method: - - Bus::fake()->except([ - ShipOrder::class, - ]); - - -### 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, - ]); - -You may use the `assertDispatchedWithoutChain` method to assert that a job was pushed without a chain of jobs: - - Bus::assertDispatchedWithoutChain(ShipOrder::class); - - -### Job Batches - -The `Bus` facade's `assertBatched` method may be used to assert that a [batch of jobs](/docs/{{version}}/queues#job-batching) 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; - }); - - -#### Testing Job / Batch Interaction - -In addition, you may occasionally need to test an individual job's interaction with its underlying batch. For example, you may need to test if a job cancelled further processing for its batch. To accomplish this, you need to assign a fake batch to the job via the `withFakeBatch` method. The `withFakeBatch` method returns a tuple containing the job instance and the fake batch: - - [$job, $batch] = (new ShipOrder)->withFakeBatch(); - - $job->handle(); - - $this->assertTrue($batch->cancelled()); - $this->assertEmpty($batch->added); - - -## Event Fake - -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`, `assertNotDispatched`, and `assertNothingDispatched` methods: - - order->id === $order->id; - }); - -If you would simply like to assert that an event listener is listening to a given event, you may use the `assertListening` method: - - Event::assertListening( - OrderShipped::class, - SendShipmentNotification::class - ); - -> **Warning** -> 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. - - -#### Faking A Subset Of Events - -If you only want to fake event listeners for a specific set of events, you may pass them to the `fake` or `fakeFor` method: - - /** - * Test order process. - */ - public function test_orders_can_be_processed(): void - { - Event::fake([ - OrderCreated::class, - ]); - - $order = Order::factory()->create(); - - Event::assertDispatched(OrderCreated::class); - - // Other events are dispatched as normal... - $order->update([...]); - } - -You may fake all events except for a set of specified events using the `except` method: - - Event::fake()->except([ - OrderCreated::class, - ]); - - -### Scoped Event Fakes - -If you only want to fake event listeners for a portion of your test, you may use the `fakeFor` method: - - create(); - - Event::assertDispatched(OrderCreated::class); - - return $order; - }); - - // Events are dispatched as normal and observers will run ... - $order->update([...]); - } - } - - -## HTTP Fake - -The `Http` facade's `fake` method allows you to instruct the HTTP client to return stubbed / dummy responses when requests are made. For more information on faking outgoing HTTP requests, please consult the [HTTP Client testing documentation](/docs/{{version}}/http-client#testing). - - -## Mail Fake - -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; - }); - -When calling the `Mail` facade's assertion methods, the mailable instance accepted by the provided closure exposes helpful methods for examining the mailable: - - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) { - return $mail->hasTo($user->email) && - $mail->hasCc('...') && - $mail->hasBcc('...') && - $mail->hasReplyTo('...') && - $mail->hasFrom('...') && - $mail->hasSubject('...'); - }); - -The mailable instance also includes several helpful methods for examining the attachments on a mailable: - - use Illuminate\Mail\Mailables\Attachment; - - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { - return $mail->hasAttachment( - Attachment::fromPath('/path/to/file') - ->as('name.pdf') - ->withMime('application/pdf') - ); - }); - - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { - return $mail->hasAttachment( - Attachment::fromStorageDisk('s3', '/path/to/file') - ); - }); - - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($pdfData) { - return $mail->hasAttachment( - Attachment::fromData(fn () => $pdfData, 'name.pdf') - ); - }); - -You may have noticed that there are two methods for asserting that mail was not sent: `assertNotSent` and `assertNotQueued`. Sometimes you may wish to assert that no mail was sent **or** queued. To accomplish this, you may use the `assertNothingOutgoing` and `assertNotOutgoing` methods: - - Mail::assertNothingOutgoing(); - - Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) { - return $mail->order->id === $order->id; - }); - - -#### Testing Mailable Content - -We suggest testing the content of your mailables separately from your tests that assert that a given mailable was "sent" to a specific user. To learn how to test the content of your mailables, check out our documentation on the [testing mailables](/docs/{{version}}/mail#testing-mailables). - - -## Notification Fake - -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; - } - ); - - -#### On-Demand Notifications - -If the code you are testing sends [on-demand notifications](/docs/{{version}}/notifications#on-demand-notifications), you can test that the on-demand notification was sent via the `assertSentOnDemand` method: - - Notification::assertSentOnDemand(OrderShipped::class); - -By passing a closure as the second argument to the `assertSentOnDemand` method, you may determine if an on-demand notification was sent to the correct "route" address: - - Notification::assertSentOnDemand( - OrderShipped::class, - function (OrderShipped $notification, array $channels, object $notifiable) use ($user) { - return $notifiable->routes['mail'] === $user->email; - } - ); - - -## Process Fake - -The `Process` facade's `fake` method allows you to instruct the Laravel process services to return stubbed / dummy results when processes are invoked. For more information on faking processes, please consult the [process testing documentation](/docs/{{version}}/processes#testing). - - -## Queue Fake - -You may use the `Queue` facade's `fake` method to prevent queued jobs from being pushed to the [queue](/docs/{{version}}/queues). 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; - }); - -If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `fake` method: - - public function test_orders_can_be_shipped(): void - { - Queue::fake([ - ShipOrder::class, - ]); - - // Perform order shipping... - - // Assert a job was pushed twice... - Queue::assertPushed(ShipOrder::class, 2); - } - -> **Note** -> To test jobs that are batched or chained via the `Bus` facade, consult the documentation on utilizing the `Bus` facade's [batch assertions](#bus-job-batches) and [chain assertions](#bus-job-chains) - - -## 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 `Illuminate\Http\UploadedFile` class, greatly simplifies the testing of file uploads. For example: - - json('POST', '/photos', [ - UploadedFile::fake()->image('photo1.jpg'), - UploadedFile::fake()->image('photo2.jpg') - ]); - - // Assert one or more files were stored... - Storage::disk('photos')->assertExists('photo1.jpg'); - Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); - - // Assert one or more files were not stored... - Storage::disk('photos')->assertMissing('missing.jpg'); - Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); - - // Assert that a given directory is empty... - Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); - } - } - -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. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). - -> **Warning** -> The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). - ## Interacting With Time diff --git a/notifications.md b/notifications.md index 1716c9f6101..6871e2b5135 100644 --- a/notifications.md +++ b/notifications.md @@ -46,6 +46,7 @@ - [Slack Attachments](#slack-attachments) - [Routing Slack Notifications](#routing-slack-notifications) - [Localizing Notifications](#localizing-notifications) +- [Testing](#testing) - [Notification Events](#notification-events) - [Custom Channels](#custom-channels) @@ -1211,6 +1212,72 @@ Once you have implemented the interface, Laravel will automatically use the pref $user->notify(new InvoicePaid($invoice)); + +## Testing + +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 were instructed to be sent to users and even inspect the data the notifications received: + + order->id === $order->id; + } + ); + + +#### On-Demand Notifications + +If the code you are testing sends [on-demand notifications](#on-demand-notifications), you can test that the on-demand notification was sent via the `assertSentOnDemand` method: + + Notification::assertSentOnDemand(OrderShipped::class); + +By passing a closure as the second argument to the `assertSentOnDemand` method, you may determine if an on-demand notification was sent to the correct "route" address: + + Notification::assertSentOnDemand( + OrderShipped::class, + function (OrderShipped $notification, array $channels, object $notifiable) use ($user) { + return $notifiable->routes['mail'] === $user->email; + } + ); + ## Notification Events diff --git a/queues.md b/queues.md index a7d8a788157..17ba4a91987 100644 --- a/queues.md +++ b/queues.md @@ -44,6 +44,10 @@ - [Failed Job Events](#failed-job-events) - [Clearing Jobs From Queues](#clearing-jobs-from-queues) - [Monitoring Your Queues](#monitoring-your-queues) +- [Testing](#testing) + - [Faking A Subset Of Jobs](#faking-a-subset-of-jobs) + - [Testing Job Chains](#testing-job-chains) + - [Testing Job Batches](#testing-job-batches) - [Job Events](#job-events) @@ -1969,6 +1973,138 @@ public function boot(): void } ``` + +## Testing + +When testing code that dispatches jobs, you may wish to instruct Laravel to not actually execute the job itself, since the job's code can be tested directly and separately of the code that dispatches it. Of course, to test the job itself, you may instantiate a job instance and invoke the `handle` method directly in your test. + +You may use the `Queue` facade's `fake` method to prevent queued jobs from actually being pushed to the queue. 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; + }); + + +### Faking A Subset Of Jobs + +If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `fake` method: + + public function test_orders_can_be_shipped(): void + { + Queue::fake([ + ShipOrder::class, + ]); + + // Perform order shipping... + + // Assert a job was pushed twice... + Queue::assertPushed(ShipOrder::class, 2); + } + +You may fake all jobs except for a set of specified jobs using the `except` method: + + Queue::fake()->except([ + ShipOrder::class, + ]); + + +### Testing Job Chains + +To test job chains, you will need to utilize the `Bus` facade's faking capabilities. 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::fake(); + + // ... + + 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, + ]); + +You may use the `assertDispatchedWithoutChain` method to assert that a job was pushed without a chain of jobs: + + Bus::assertDispatchedWithoutChain(ShipOrder::class); + + +### Testing Job Batches + +The `Bus` facade's `assertBatched` method may be used to assert that a [batch of jobs](/docs/{{version}}/queues#job-batching) 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::fake(); + + // ... + + Bus::assertBatched(function (PendingBatch $batch) { + return $batch->name == 'import-csv' && + $batch->jobs->count() === 10; + }); + + +#### Testing Job / Batch Interaction + +In addition, you may occasionally need to test an individual job's interaction with its underlying batch. For example, you may need to test if a job cancelled further processing for its batch. To accomplish this, you need to assign a fake batch to the job via the `withFakeBatch` method. The `withFakeBatch` method returns a tuple containing the job instance and the fake batch: + + [$job, $batch] = (new ShipOrder)->withFakeBatch(); + + $job->handle(); + + $this->assertTrue($batch->cancelled()); + $this->assertEmpty($batch->added); + ## Job Events diff --git a/upgrade.md b/upgrade.md index 2e2a393d44d..c074e5ac9ed 100644 --- a/upgrade.md +++ b/upgrade.md @@ -241,7 +241,7 @@ return Redirect::route('home'); The deprecated `MocksApplicationServices` trait has been removed from the framework. This trait provided testing methods such as `expectsEvents`, `expectsJobs`, and `expectsNotifications`. -If your application uses these methods, we recommend you transition to `Event::fake`, `Bus::fake`, and `Notification::fake`, respectively. You can learn more about mocking via the complete [mocking documentation](/docs/{{version}}/mocking). +If your application uses these methods, we recommend you transition to `Event::fake`, `Bus::fake`, and `Notification::fake`, respectively. You can learn more about mocking via fakes in the corresponding documentation for the component you are attempting to fake. ### Validation From 8a2cbd60a83f081749275b17100846643f98342e Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 14 Feb 2023 15:39:55 +0100 Subject: [PATCH 0762/2609] Prepare Laravel v11 (#8570) --- releases.md | 180 ++------------------------------------ upgrade.md | 246 ++-------------------------------------------------- 2 files changed, 14 insertions(+), 412 deletions(-) diff --git a/releases.md b/releases.md index 8507c36c5c4..cd5591bb6cb 100644 --- a/releases.md +++ b/releases.md @@ -2,14 +2,14 @@ - [Versioning Scheme](#versioning-scheme) - [Support Policy](#support-policy) -- [Laravel 10](#laravel-10) +- [Laravel 11](#laravel-11) ## Versioning Scheme Laravel and its other first-party packages follow [Semantic Versioning](https://semver.org). Major framework releases are released every year (~Q1), while minor and patch releases may be released as often as every week. Minor and patch releases should **never** contain breaking changes. -When referencing the Laravel framework or its components from your application or package, you should always use a version constraint such as `^10.0`, since major releases of Laravel do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less. +When referencing the Laravel framework or its components from your application or package, you should always use a version constraint such as `^11.0`, since major releases of Laravel do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less. #### Named Arguments @@ -26,7 +26,6 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | | --- | --- | --- | --- | --- | -| 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.2 | Q1 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 | Q1 2024 | August 5th, 2025 | February 3rd, 2026 | @@ -46,177 +45,12 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe (*) Supported PHP versions - -## Laravel 10 + +## Laravel 11 -As you may know, Laravel transitioned to yearly releases with the release of Laravel 8. Previously, major versions were released every 6 months. This transition is intended to ease the maintenance burden on the community and challenge our development team to ship amazing, powerful new features without introducing breaking changes. Therefore, we have shipped a variety of robust features to Laravel 9 without breaking backwards compatibility. - -Therefore, this commitment to ship great new features during the current release will likely lead to future "major" releases being primarily used for "maintenance" tasks such as upgrading upstream dependencies, which can be seen in these release notes. - -Laravel 10 continues the improvements made in Laravel 9.x by introducing argument and return types to all application skeleton methods, as well as all stub files used to generate classes throughout the framework. In addition, a new, developer-friendly abstraction layer has been introduced for starting and interacting with external processes. Further, Laravel Pennant has been introduced to provide a wonderful approach to managing your application's "feature flags". +To be determined... -### PHP 8.1 - -Laravel 10.x requires a minimum PHP version of 8.1. - - -### Types - -_Application skeleton and stub type-hints were contributed by [Nuno Maduro](https://github.com/nunomaduro)_. - -On its initial release, Laravel utilized all of the type-hinting features available in PHP at the time. However, many new features have been added to PHP in the subsequent years, including additional primitive type-hints, return types, and union types. - -Laravel 10.x thoroughly updates the application skeleton and all stubs utilized by the framework to introduce argument and return types to all method signatures. In addition, extraneous "doc block" type-hint information has been deleted: - -```php - -### Laravel Pennant - -_Laravel Pennant was developed by [Tim MacDonald](https://github.com/timacdonald)_. - -A new first-party package, Laravel Pennant, has been released. Laravel Pennant offers a light-weight, streamlined approach to managing your application's feature flags. Out of the box, Pennant includes an in-memory `array` driver and a `database` driver for persistent feature storage. - -Features can be easily defined via the `Feature::define` method: - -```php -use Laravel\Pennant\Feature; -use Illuminate\Support\Lottery; - -Feature::define('new-onboarding-flow', function () { - return Lottery::odds(1, 10); -}); -``` - -Once a feature has been defined, you may easily determine if the current user has access to the given feature: - -```php -if (Feature::active('new-onboarding-flow')) { - // ... -} -``` - -Of course, for convenience, Blade directives are also available: - -```blade -@feature('new-onboarding-flow') -
    - -
    -@endfeature -``` - -Pennant offers a variety of more advanced features and APIs. For more information, please consult the [comprehensive Pennant documentation](/docs/{{version}}/pennant). - - -### Process Interaction - -_The process abstraction layer was contributed by [Nuno Maduro](https://github.com/nunomaduro) and [Taylor Otwell](https://github.com/taylorotwell)_. - -Laravel 10.x introduces a beautiful abstraction layer for starting and interacting with external processes via a new `Process` facade: - -```php -use Illuminate\Support\Facades\Process; - -$result = Process::run('ls -la'); - -return $result->output(); -``` - -Processes may even be started in pools, allowing for the convenient execution and management of concurrent processes: - -```php -use Illuminate\Process\Pool; -use Illuminate\Support\Facades\Pool; - -[$first, $second, $third] = Process::concurrently(function (Pool $pool) { - $pool->command('cat first.txt'); - $pool->command('cat second.txt'); - $pool->command('cat third.txt'); -}); - -return $first->output(); -``` - -In addition, processes may be faked for convenient testing: - -```php -Process::fake(); - -// ... - -Process::assertRan('ls -la'); -``` - -For more information on interacting with processes, please consult the [comprehensive process documentation](/docs/{{version}}/processes). - - -### Test Profiling - -_Test profiling was contributed by [Nuno Maduro](https://github.com/nunomaduro)_. - -The Artisan `test` command has received a new `--profile` option that allows you to easily identify the slowest tests in your application: - -```shell -php artisan test --profile -``` - -For convenience, the slowest tests will be displayed directly within the CLI output: - -

    - -

    - - -### Pest Scaffolding - -New Laravel projects may now be created with Pest test scaffolding by default. To opt-in to this feature, provide the `--pest` flag when creating a new application via the Laravel installer: - -```shell -laravel new example-application --pest -``` - - -### Generator CLI Prompts - -_Generator CLI prompts were contributed by [Jess Archer](https://github.com/jessarcher)_. - -To improve the framework's developer experience, all of Laravel's built-in `make` commands no longer require any input. If the commands are invoked without input, you will be prompted for the required arguments: +### PHP 8.2 -```shell -php artisan make:controller -``` +Laravel 11.x requires a minimum PHP version of 8.2. diff --git a/upgrade.md b/upgrade.md index c074e5ac9ed..e01d5d38084 100644 --- a/upgrade.md +++ b/upgrade.md @@ -1,6 +1,6 @@ # Upgrade Guide -- [Upgrading To 10.0 From 9.x](#upgrade-10.0) +- [Upgrading To 11.0 From 10.x](#upgrade-11.0) ## High Impact Changes @@ -12,40 +12,11 @@
    - -## Medium Impact Changes - -
    - -- [Database Expressions](#database-expressions) -- [Model "Dates" Property](#model-dates-property) -- [Redis Cache Tags](#redis-cache-tags) -- [Service Mocking](#service-mocking) -- [The Language Directory](#language-directory) - -
    - - -## Low Impact Changes - -
    - -- [Closure Validation Rule Messages](#closure-validation-rule-messages) -- [Monolog 3](#monolog-3) -- [Query Exception Constructor](#query-exception-constructor) -- [Rate Limiter Return Values](#rate-limiter-return-values) -- [Relation `getBaseQuery` Method](#relation-getbasequery-method) -- [The `Redirect::home` Method](#redirect-home) -- [The `Bus::dispatchNow` Method](#dispatch-now) -- [ULID Columns](#ulid-columns) - -
    - - -## Upgrading To 10.0 From 9.x + +## Upgrading To 11.0 From 10.x -#### Estimated Upgrade Time: 10 Minutes +#### Estimated Upgrade Time: ?? Minutes > **Note** > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. @@ -55,13 +26,9 @@ **Likelihood Of Impact: High** -#### PHP 8.1.0 Required +#### PHP 8.2.0 Required -Laravel now requires PHP 8.1.0 or greater. - -#### Composer 2.2.0 Required - -Laravel now requires [Composer](https://getcomposer.org) 2.2.0 or greater. +Laravel now requires PHP 8.2.0 or greater. #### Composer Dependencies @@ -69,205 +36,6 @@ You should update the following dependencies in your application's `composer.jso
    -- `laravel/framework` to `^10.0` -- `spatie/laravel-ignition` to `^2.0` - -
    - -Optionally, if you wish to use [PHPUnit 10](https://phpunit.de/announcements/phpunit-10.html), you should delete the `processUncoveredFiles` attribute from the `` section of your application's `phpunit.xml` configuration file. Then, update the following dependencies in your application's `composer.json` file: - -
    - -- `nunomaduro/collision` to `^7.0` -- `phpunit/phpunit` to `^10.0` +- `laravel/framework` to `^11.0`
    - -Finally, examine any other third-party packages consumed by your application and verify you are using the proper version for Laravel 10 support. - - -#### Minimum Stability - -You should update the `minimum-stability` setting in your application's `composer.json` file to `stable`: - -```json -"minimum-stability": "stable", -``` - -### Cache - - -#### Redis Cache Tags - -**Likelihood Of Impact: Medium** - -Redis [cache tag](/docs/{{version}}/cache#cache-tags) support has been rewritten for better performance and storage efficiency. In previously releases of Laravel, stale cache tags would accumulate in the cache when using Redis as your application's cache driver. - -However, to properly prune stale cache tag entries, Laravel's new `cache:prune-stale-tags` Artisan command should be [scheduled](/docs/{{version}}/scheduling) in your application's `App\Console\Kernel` class: - - $schedule->command('cache:prune-stale-tags')->hourly(); - -### Database - - -#### Database Expressions - -**Likelihood Of Impact: Medium** - -Database "expressions" (typically generated via `DB::raw`) have been rewritten in Laravel 10.x to offer additional functionality in the future. Notably, the grammar's raw string value must now be retrieved via the expression's `getValue(Grammar $grammar)` method. Casting an expression to a string using `(string)` is no longer supported. - -**Typically, this does not affect end-user applications**; however, if your application is manually casting database expressions to strings using `(string)` or invoking the `__toString` method on the expression directly, you should update your code to invoke the `getValue` method instead: - -```php -use Illuminate\Support\Facades\DB; - -$expression = DB::raw('select 1'); - -$string = $expression->getValue(DB::connection()->getQueryGrammar()); -``` - - -#### Query Exception Constructor - -**Likelihood Of Impact: Very Low** - -The `Illuminate\Database\QueryException` constructor now accepts a string connection name as its first argument. If your application is mainly throwing this exception, you should adjust your code accordingly. - - -#### ULID Columns - -**Likelihood Of Impact: Low** - -When migrations invoke the `ulid` method without any arguments, the column will now be named `ulid`. In previous releases of Laravel, invoking this method without any arguments created a column erroneously named `uuid`: - - $table->ulid(); - -To explicitly specify a column name when invoking the `ulid` method, you may pass the column name to the method: - - $table->ulid('ulid'); - -### Eloquent - - -#### Model "Dates" Property - -**Likelihood Of Impact: Medium** - -The Eloquent model's deprecated `$dates` property has been removed. Your application should now use the `$casts` property: - -```php -protected $casts = [ - 'deployed_at' => 'datetime', -]; -``` - - -#### Relation `getBaseQuery` Method - -**Likelihood Of Impact: Very Low** - -The `getBaseQuery` method on the `Illuminate\Database\Eloquent\Relations\Relation` class has been renamed to `toBase`. - -### Localization - - -#### The Language Directory - -**Likelihood Of Impact: None** - -Though not relevant to existing applications, the Laravel application skeleton no longer contains the `lang` directory by default. Instead, when writing new Laravel applications, it may be published using the `lang:publish` Artisan command: - -```shell -php artisan lang:publish -``` - -### Logging - - -#### Monolog 3 - -**Likelihood Of Impact: Low** - -Laravel's Monolog dependency has been updated to Monolog 3.x. If you are directly interacting with Monolog within your application, you should review Monolog's [upgrade guide](https://github.com/Seldaek/monolog/blob/main/UPGRADE.md). - -### Queues - - -#### The `Bus::dispatchNow` Method - -**Likelihood Of Impact: Low** - -The deprecated `Bus::dispatchNow` and `dispatch_now` methods have been removed. Instead, your application should use the `Bus::dispatchSync` and `dispatch_sync` methods, respectively. - -### Routing - - -#### Middleware Aliases - -**Likelihood Of Impact: Optional** - -In new Laravel applications, the `$routeMiddleware` property of the `App\Http\Kernel` class has been renamed to `$middlewareAliases` to better reflect its purpose. You are welcome to rename this property in your existing applications; however, it is not required. - - -#### Rate Limiter Return Values - -**Likelihood Of Impact: Low** - -When invoking the `RateLimiter::attempt` method, the value returned by the provided closure will now be returned by the method. If nothing or `null` is returned, the `attempt` method will return `true`: - -```php -$value = RateLimiter::attempt('key', 10, fn () => ['example'], 1); - -$value; // ['example'] -``` - - -#### The `Redirect::home` Method - -**Likelihood Of Impact: Very Low** - -The deprecated `Redirect::home` method has been removed. Instead, your application should redirect to an explicitly named route: - -```php -return Redirect::route('home'); -``` - -### Testing - - -#### Service Mocking - -**Likelihood Of Impact: Medium** - -The deprecated `MocksApplicationServices` trait has been removed from the framework. This trait provided testing methods such as `expectsEvents`, `expectsJobs`, and `expectsNotifications`. - -If your application uses these methods, we recommend you transition to `Event::fake`, `Bus::fake`, and `Notification::fake`, respectively. You can learn more about mocking via fakes in the corresponding documentation for the component you are attempting to fake. - -### Validation - - -#### Closure Validation Rule Messages - -**Likelihood Of Impact: Very Low** - -When writing closure based custom validation rules, invoking the `$fail` callback more than once will now append the messages to an array instead of overwriting the previous message. Typically, this will not affect your application. - -In addition, the `$fail` callback now returns an object. If you were previously type-hinting the return type of your validation closure, this may require you to update your type-hint: - -```php -public function rules() -{ - 'name' => [ - function ($attribute, $value, $fail) { - $fail('validation.translation.key')->translate(); - }, - ], -} -``` - - -### Miscellaneous - -We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. - -You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/9.x...10.x) and choose which updates are important to you. However, many of the changes shown by the GitHub comparison tool are due to our organization's adoption of PHP native types. These changes are backwards compatible and the adoption of them during the migration to Laravel 10 is optional. From ab7a1dd8d643724e451ec2f2f0e283ced5e70c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Tue, 14 Feb 2023 21:40:29 +0700 Subject: [PATCH 0763/2609] [10.x] Update Laravel version in readme.md (#8569) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3db17ac3a66..3194312ba4e 100644 --- a/readme.md +++ b/readme.md @@ -4,4 +4,4 @@ You can find the online version of the Laravel documentation at [https://laravel ## Contribution Guidelines -If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 9 would be submitted to the `9.x` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. +If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 10 would be submitted to the `10.x` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. From ccfd711435d7717011eac54290b5309e2b53d5dc Mon Sep 17 00:00:00 2001 From: Faraz Samapoor Date: Tue, 14 Feb 2023 18:14:37 +0330 Subject: [PATCH 0764/2609] [10.x] Removes duplicated testing documentation. (#8568) --- pennant.md | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/pennant.md b/pennant.md index da82835a858..3acba755447 100644 --- a/pennant.md +++ b/pennant.md @@ -859,48 +859,3 @@ class EventServiceProvider extends ServiceProvider ### `Laravel\Pennant\Events\DynamicallyDefiningFeature` This event is dispatched when a class based feature is being dynamically checked for the first time during a request. - - -## Testing - -When testing code that interacts with feature flags, the easiest way to control the feature flag's returned value in your tests is to simply re-define the feature. For example, imagine you have the following feature defined in one of your application's service provider: - -```php -use Illuminate\Support\Arr; -use Laravel\Pennant\Feature; - -Feature::define('purchase-button', fn () => Arr::random([ - 'blue-sapphire', - 'seafoam-green', - 'tart-orange', -])); -``` - -To modify the feature's returned value in your tests, you may re-define the feature at the beginning of the test. The following test will always pass, even though the `Arr::random()` implementation is still present in the service provider: - -```php -use Laravel\Pennant\Feature; - -public function test_it_can_control_feature_values() -{ - Feature::define('purchase-button', 'seafoam-green'); - - $this->assertSame('seafoam-green', Feature::value('purchase-button')); -} -``` - -The same approach may be used for class based features: - -```php -use App\Features\NewApi; -use Laravel\Pennant\Feature; - -public function test_it_can_control_feature_values() -{ - Feature::define(NewApi::class, true); - - $this->assertTrue(Feature::value(NewApi::class)); -} -``` - -If your feature is returning a `Lottery` instance, there are a handful of useful [testing helpers available](/docs/{{version}}/helpers#testing-lotteries). From ea8e8b02cf388d9db962d56bdbfa5367a59feea9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 14 Feb 2023 09:48:41 -0600 Subject: [PATCH 0765/2609] wip --- releases.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/releases.md b/releases.md index 8507c36c5c4..a3b7afeb163 100644 --- a/releases.md +++ b/releases.md @@ -220,3 +220,10 @@ To improve the framework's developer experience, all of Laravel's built-in `make ```shell php artisan make:controller ``` + + +### Horizon / Telescope Facelift + +[Horizon](/docs/{{version}}/horizon) and [Telescope](/docs/{{version}}/telescope) have been updated with a fresh, modern look including improved typography, spacing, and design: + + From a03d765b81f02c61ef6819adf0ff929bdc5feba5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 14 Feb 2023 12:51:19 -0600 Subject: [PATCH 0766/2609] document stopignoring --- errors.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/errors.md b/errors.md index d9df978c035..37049748460 100644 --- a/errors.md +++ b/errors.md @@ -154,13 +154,22 @@ When building your application, there will be some types of exceptions you simpl InvalidOrderException::class, ]; -> **Note** -> Behind the scenes, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP "not found" errors or 419 HTTP responses generated by invalid CSRF tokens. +Internally, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP errors or 419 HTTP responses generated by invalid CSRF tokens. If you would like to instruct Laravel to stop ignoring a given type of exception, you may invoke the `stopIgnoring` method within your exception handler's `register` method: + + use Symfony\Component\HttpKernel\Exception\HttpException; + + /** + * Register the exception handling callbacks for the application. + */ + public function register(): void + { + $this->stopIgnoring(HttpException::class); + } ### Rendering Exceptions -By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this via the `renderable` method of your exception handler. +By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by invoking the `renderable` method within your exception handler. The closure passed to the `renderable` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will deduce what type of exception the closure renders by examining the type-hint of the closure: @@ -199,7 +208,7 @@ You may also use the `renderable` method to override the rendering behavior for ### Reportable & Renderable Exceptions -Instead of type-checking exceptions in the exception handler's `register` method, you may define `report` and `render` methods directly on your custom exceptions. When these methods exist, they will be automatically called by the framework: +Instead of defining custom reporting and rendering behavior in your exception handler's `register` method, you may define `report` and `render` methods directly on your custom exceptions. When these methods exist, they will be automatically called by the framework: Date: Tue, 14 Feb 2023 12:51:29 -0600 Subject: [PATCH 0767/2609] wip --- errors.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/errors.md b/errors.md index 37049748460..c6ca24026f4 100644 --- a/errors.md +++ b/errors.md @@ -164,6 +164,8 @@ Internally, Laravel already ignores some types of errors for you, such as except public function register(): void { $this->stopIgnoring(HttpException::class); + + // ... } From 5b9b843960346018f563b1901a44ec694fd5b940 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 14 Feb 2023 14:34:37 -0600 Subject: [PATCH 0768/2609] wip --- upgrade.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/upgrade.md b/upgrade.md index c074e5ac9ed..a32a65df8af 100644 --- a/upgrade.md +++ b/upgrade.md @@ -32,6 +32,7 @@ - [Closure Validation Rule Messages](#closure-validation-rule-messages) - [Monolog 3](#monolog-3) +- [Public Path Binding](#public-path-binding) - [Query Exception Constructor](#query-exception-constructor) - [Rate Limiter Return Values](#rate-limiter-return-values) - [Relation `getBaseQuery` Method](#relation-getbasequery-method) @@ -94,6 +95,19 @@ You should update the `minimum-stability` setting in your application's `compose "minimum-stability": "stable", ``` +### Application + + +#### Public Path Binding + +**Likelihood Of Impact: Low** + +If your application is customizing its "public path" by binding `path.public` into the container, you should instead update your code to invoke the `usePublicPath` method offered by the `Illuminate\Foundation\Application` object: + +```php +app()->usePublicPath(__DIR__.'/public'); +``` + ### Cache From 91c62eb85bf271792350b072a554b997e4fc29fa Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 15 Feb 2023 12:46:17 +1100 Subject: [PATCH 0769/2609] [10.x] Documents when method when used with a rich value (#8573) * documents rich-value when method * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pennant.md b/pennant.md index 3acba755447..3e1eb2befdf 100644 --- a/pennant.md +++ b/pennant.md @@ -570,6 +570,20 @@ Pennant's included Blade directive also makes it easy to conditionally render co > **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. +When calling the [conditional `when`](#conditional-execution) method, the feature's rich value will be provided to the first closure: + + Feature::when('purchase-button', + fn ($color) => /* ... */, + fn () => /* ... */, + ); + +Likewise, when calling the conditional `unless` method, the feature's rich value will be provided to the optional second closure: + + Feature::unless('purchase-button', + fn () => /* ... */, + fn ($color) => /* ... */, + ); + ## Eager Loading From 9ea93b5b9f2ea49f8e9219fed96c19bc2c855b2d Mon Sep 17 00:00:00 2001 From: lastsecondir <55757877+lastsecondir@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:22:49 +0330 Subject: [PATCH 0770/2609] Update deployment.md (#8580) fix nginx config to use php8.1-fpm socket instead of php8.0-fpm --- deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index 2102287555b..aaff2d2f780 100644 --- a/deployment.md +++ b/deployment.md @@ -75,7 +75,7 @@ server { error_page 404 /index.php; location ~ \.php$ { - fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; + fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } From bbb99e437f4ce43d637814070cd2312bc130f9e4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 15 Feb 2023 15:54:02 +0100 Subject: [PATCH 0771/2609] [10.x] Added exact release date (#8578) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index a3b7afeb163..bc2e27acb03 100644 --- a/releases.md +++ b/releases.md @@ -28,7 +28,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | --- | --- | --- | --- | --- | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | -| 10 | 8.1 - 8.2 | Q1 2023 | August 6th, 2024 | February 4th, 2025 | +| 10 | 8.1 - 8.2 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 | Q1 2024 | August 5th, 2025 | February 3rd, 2026 |
    From 67c2634ffc0641b6b66a90cee33f319813a48c44 Mon Sep 17 00:00:00 2001 From: Azhar At Zauhar Dripana <33951589+qctfw@users.noreply.github.com> Date: Wed, 15 Feb 2023 21:55:47 +0700 Subject: [PATCH 0772/2609] [10.x] Update current Laravel version in Contribution Guide (#8576) --- contributions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributions.md b/contributions.md index a46e6f6d0f0..4ce931f9d4d 100644 --- a/contributions.md +++ b/contributions.md @@ -75,9 +75,9 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest version that supports bug fixes (currently `9.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `10.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. -**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `9.x`). +**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `10.x`). **Major** new features or features with breaking changes should always be sent to the `master` branch, which contains the upcoming release. From dbd8686e6693b263b74c4849baec6800fb94aa26 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 16 Feb 2023 02:12:02 +1100 Subject: [PATCH 0773/2609] [10.x] Document `all` and `discover` (#8574) * Document `all` and `discover` * formatting * formatting * remove extra link --------- Co-authored-by: Taylor Otwell --- pennant.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pennant.md b/pennant.md index 3e1eb2befdf..9dbc3e9cc96 100644 --- a/pennant.md +++ b/pennant.md @@ -11,6 +11,7 @@ - [Blade Directive](#blade-directive) - [Middleware](#middleware) - [In-Memory Cache](#in-memory-cache) +- [Retrieving All Features](#retrieving-all-features) - [Scope](#scope) - [Specifying The Scope](#specifying-the-scope) - [Default Scope](#default-scope) @@ -395,6 +396,56 @@ If you need to manually flush the in-memory cache, you may use the `flushCache` Feature::flushCache(); + +## Retrieving All Features + +Via the `all` method, Pennant offers the ability to retrieve the values of all defined features for a scope: + +```php +Feature::all(); + +// [ +// 'site-redesign' => true, +// 'purchase-button' => 'blue-sapphire', +// ] +``` + +However, class based features are dynamically registered and are not known by Pennant until they are explicitly checked. This means your application's class based features may not appear in the results returned by the `all` method if they have not already been checked during the current request. + +If you would like to ensure that feature classes are always included when using the `all` method, you may use Pennant's feature discovery capabilities. To get started, invoke the `discover` method in one of your application's service providers: + + true, +// 'purchase-button' => 'blue-sapphire', +// 'App\Features\NewApi' => true, +// ] +``` + ## Scope From 4ebb7b7efcf282cab89b57c78d11b96cb779e5b8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 15 Feb 2023 09:17:10 -0600 Subject: [PATCH 0774/2609] document null scope behavior --- pennant.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pennant.md b/pennant.md index 9dbc3e9cc96..76cf7045611 100644 --- a/pennant.md +++ b/pennant.md @@ -532,9 +532,11 @@ Feature::for($user->team)->active('billing-v2'); ### Nullable Scope -If the scope you are passing to a feature is potentially `null`, you should account for that in your feature's definition. A `null` scope may occur if you check a feature within an Artisan command, queued job, or unauthenticated route. Since there is usually not an authenticated user in these contexts, the default scope will be `null`. +If the scope you provide when checking a feature is `null` and the feature's definition does not support `null` via a nullable type or by including `null` in a union type, Pennant will automatically return `false` as the feature's result value. -If you do not always [explictly specify your feature scope](#specifying-the-scope) then you should ensure the scope's type is "nullable" and handle the `null` scope value within your feature definition logic: +So, if the scope you are passing to a feature is potentially `null` and you want the feature's value resolver to be invoked, you should account for that in your feature's definition. A `null` scope may occur if you check a feature within an Artisan command, queued job, or unauthenticated route. Since there is usually not an authenticated user in these contexts, the default scope will be `null`. + +If you do not always [explicitly specify your feature scope](#specifying-the-scope) then you should ensure the scope's type is "nullable" and handle the `null` scope value within your feature definition logic: ```php use App\Models\User; From 85d62f3654d7319c1609a310bc044c684100a0dc Mon Sep 17 00:00:00 2001 From: Jonny Nott Date: Wed, 15 Feb 2023 17:07:19 +0000 Subject: [PATCH 0775/2609] =?UTF-8?q?Change=20Monolog=203=20bump=20impact?= =?UTF-8?q?=20to=20medium=20and=20explain=20about=203rd-party=20se?= =?UTF-8?q?=E2=80=A6=20(#8582)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change Monolog 3 bump impact to medium and explain about 3rd-party service packages implication * Update upgrade.md * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/upgrade.md b/upgrade.md index a32a65df8af..ea57ceb20dd 100644 --- a/upgrade.md +++ b/upgrade.md @@ -19,6 +19,7 @@ - [Database Expressions](#database-expressions) - [Model "Dates" Property](#model-dates-property) +- [Monolog 3](#monolog-3) - [Redis Cache Tags](#redis-cache-tags) - [Service Mocking](#service-mocking) - [The Language Directory](#language-directory) @@ -31,7 +32,6 @@
    - [Closure Validation Rule Messages](#closure-validation-rule-messages) -- [Monolog 3](#monolog-3) - [Public Path Binding](#public-path-binding) - [Query Exception Constructor](#query-exception-constructor) - [Rate Limiter Return Values](#rate-limiter-return-values) @@ -200,10 +200,12 @@ php artisan lang:publish #### Monolog 3 -**Likelihood Of Impact: Low** +**Likelihood Of Impact: Medium** Laravel's Monolog dependency has been updated to Monolog 3.x. If you are directly interacting with Monolog within your application, you should review Monolog's [upgrade guide](https://github.com/Seldaek/monolog/blob/main/UPGRADE.md). +If you are using third-party logging services such as BugSnag or Rollbar, you may need to upgrade those third-party packages to a version that supports Monolog 3.x and Laravel 10.x. + ### Queues From eac2da9313a1be23d7b77f9e1bfc1a127dc346e9 Mon Sep 17 00:00:00 2001 From: Andy P Date: Thu, 16 Feb 2023 15:35:27 +0000 Subject: [PATCH 0776/2609] Fix typo in 10.x upgrade guide (#8586) --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index ea57ceb20dd..b166a078f78 100644 --- a/upgrade.md +++ b/upgrade.md @@ -145,7 +145,7 @@ $string = $expression->getValue(DB::connection()->getQueryGrammar()); **Likelihood Of Impact: Very Low** -The `Illuminate\Database\QueryException` constructor now accepts a string connection name as its first argument. If your application is mainly throwing this exception, you should adjust your code accordingly. +The `Illuminate\Database\QueryException` constructor now accepts a string connection name as its first argument. If your application is manually throwing this exception, you should adjust your code accordingly. #### ULID Columns From 9f2f321ab2537201abf42b5b36da79c215f40b5f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 16 Feb 2023 11:20:37 -0600 Subject: [PATCH 0777/2609] wip --- documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation.md b/documentation.md index c35f8d35aa5..149e1955e4a 100644 --- a/documentation.md +++ b/documentation.md @@ -97,4 +97,4 @@ - [Socialite](/docs/{{version}}/socialite) - [Telescope](/docs/{{version}}/telescope) - [Valet](/docs/{{version}}/valet) -- [API Documentation](/api/9.x) +- [API Documentation](/api/10.x) From e83d2b850a1d42d6bcc0af01f076e0af514f98d5 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 16 Feb 2023 21:56:27 +0300 Subject: [PATCH 0778/2609] Add information about shouldRender method (#8588) * Blade, shouldRender method * formatting --------- Co-authored-by: Victor Isadov Co-authored-by: Taylor Otwell --- blade.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/blade.md b/blade.md index 882a31e1cd3..ad5f784ebf7 100644 --- a/blade.md +++ b/blade.md @@ -715,6 +715,18 @@ If the component class is nested deeper within the `app/View/Components` directo ``` +If you would like to conditionally render your component, you may define a `shouldRender` method on your component class. If the `shouldRender` method returns `false` the component will not be rendered: + + use Illuminate\Support\Str; + + /** + * Whether the component should be rendered + */ + public function shouldRender(): bool + { + return Str::length($this->message) > 0; + } + ### Passing Data To Components From 9f68d707fbf863ea3eb8f62e67159b3c12b3060a Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 17 Feb 2023 05:59:06 +1100 Subject: [PATCH 0779/2609] [10.x] Documents `Pennant::values` (#8587) * Re-order * Documents the `values` method * formatting --------- Co-authored-by: Taylor Otwell --- pennant.md | 115 +++++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 51 deletions(-) diff --git a/pennant.md b/pennant.md index 76cf7045611..3dffb0202a8 100644 --- a/pennant.md +++ b/pennant.md @@ -11,13 +11,13 @@ - [Blade Directive](#blade-directive) - [Middleware](#middleware) - [In-Memory Cache](#in-memory-cache) -- [Retrieving All Features](#retrieving-all-features) - [Scope](#scope) - [Specifying The Scope](#specifying-the-scope) - [Default Scope](#default-scope) - [Nullable Scope](#nullable-scope) - [Identifying Scope](#identifying-scope) - [Rich Feature Values](#rich-feature-values) +- [Retrieving Multiple Features](#retrieving-multiple-features) - [Eager Loading](#eager-loading) - [Updating Values](#updating-values) - [Bulk Updates](#bulk-updates) @@ -396,56 +396,6 @@ If you need to manually flush the in-memory cache, you may use the `flushCache` Feature::flushCache(); - -## Retrieving All Features - -Via the `all` method, Pennant offers the ability to retrieve the values of all defined features for a scope: - -```php -Feature::all(); - -// [ -// 'site-redesign' => true, -// 'purchase-button' => 'blue-sapphire', -// ] -``` - -However, class based features are dynamically registered and are not known by Pennant until they are explicitly checked. This means your application's class based features may not appear in the results returned by the `all` method if they have not already been checked during the current request. - -If you would like to ensure that feature classes are always included when using the `all` method, you may use Pennant's feature discovery capabilities. To get started, invoke the `discover` method in one of your application's service providers: - - true, -// 'purchase-button' => 'blue-sapphire', -// 'App\Features\NewApi' => true, -// ] -``` - ## Scope @@ -637,6 +587,69 @@ Likewise, when calling the conditional `unless` method, the feature's rich value fn ($color) => /* ... */, ); + +## Retrieving Multiple Features + +The `values` method allows the retrieval of multiple features for a given scope: + +```php +Feature::values(['billing-v2', 'purchase-button']); + +// [ +// 'billing-v2' => false, +// 'purchase-button' => 'blue-sapphire', +// ] +``` + +Or, you may use the `all` method to retrieve the values of all defined features for a given scope: + +```php +Feature::all(); + +// [ +// 'billing-v2' => false, +// 'purchase-button' => 'blue-sapphire', +// 'site-redesign' => true, +// ] +``` + +However, class based features are dynamically registered and are not known by Pennant until they are explicitly checked. This means your application's class based features may not appear in the results returned by the `all` method if they have not already been checked during the current request. + +If you would like to ensure that feature classes are always included when using the `all` method, you may use Pennant's feature discovery capabilities. To get started, invoke the `discover` method in one of your application's service providers: + + true, +// 'billing-v2' => false, +// 'purchase-button' => 'blue-sapphire', +// 'site-redesign' => true, +// ] +``` + ## Eager Loading From 9e8b81a722f3c8daa296688b3f9320f90b0609ad Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 16 Feb 2023 13:05:00 -0600 Subject: [PATCH 0780/2609] [10.x] update note about appending accessors (#8585) * update note about appending accessors the current wording makes it seem like you have to add the attribute to the `$appends` property. I'm hoping this new wording emphasizes that it is optional, and helps the reader understand when and why they would or would not want to use it. * Update eloquent-serialization.md --------- Co-authored-by: Taylor Otwell --- eloquent-serialization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-serialization.md b/eloquent-serialization.md index 0e388147323..ee338588e46 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -154,7 +154,7 @@ Occasionally, when converting models to arrays or JSON, you may wish to add attr } } -After creating the accessor, add the attribute name to the `appends` property of your model. Note that attribute names are typically referenced using their "snake case" serialized representation, even though the accessor's PHP method is defined using "camel case": +If you would like the accessor to always be appended to your model's array and JSON representations, you may add the attribute name to the `appends` property of your model. Note that attribute names are typically referenced using their "snake case" serialized representation, even though the accessor's PHP method is defined using "camel case": Date: Thu, 16 Feb 2023 22:37:48 +0300 Subject: [PATCH 0781/2609] [10.x] upgrade doc updated for removed registerPolicies method (#8584) * [10.x] upgrade doc updated for removed registerPolicies method * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/upgrade.md b/upgrade.md index b166a078f78..2d4b27f012f 100644 --- a/upgrade.md +++ b/upgrade.md @@ -38,6 +38,7 @@ - [Relation `getBaseQuery` Method](#relation-getbasequery-method) - [The `Redirect::home` Method](#redirect-home) - [The `Bus::dispatchNow` Method](#dispatch-now) +- [The `registerPolicy` Method](#register-policy) - [ULID Columns](#ulid-columns)
    @@ -108,6 +109,15 @@ If your application is customizing its "public path" by binding `path.public` in app()->usePublicPath(__DIR__.'/public'); ``` +### Authorization + + +### The `registerPolicy` Method + +**Likelihood Of Impact: Low** + +The `registerPolicies` method of the `AuthServiceProvider` is now invoked automatically by the framework. Therefore, you may remove the call to this method from the `boot` method of your application's `AuthServiceProvider`. + ### Cache From ad15dde7eb25bf5137c5a1a06ac6c1d057e1283a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 16 Feb 2023 13:42:03 -0600 Subject: [PATCH 0782/2609] wip --- authentication.md | 6 ------ authorization.md | 6 ------ passport.md | 12 ------------ passwords.md | 2 -- 4 files changed, 26 deletions(-) diff --git a/authentication.md b/authentication.md index d80dc44be0a..a15fa4b1b21 100644 --- a/authentication.md +++ b/authentication.md @@ -556,8 +556,6 @@ You may define your own authentication guards using the `extend` method on the ` */ public function boot(): void { - $this->registerPolicies(); - Auth::extend('jwt', function (Application $app, string $name, array $config) { // Return an instance of Illuminate\Contracts\Auth\Guard... @@ -591,8 +589,6 @@ To get started, call the `Auth::viaRequest` method within the `boot` method of y */ public function boot(): void { - $this->registerPolicies(); - Auth::viaRequest('custom-token', function (Request $request) { return User::where('token', $request->token)->first(); }); @@ -633,8 +629,6 @@ If you are not using a traditional relational database to store your users, you */ public function boot(): void { - $this->registerPolicies(); - Auth::provider('mongo', function (Application $app, array $config) { // Return an instance of Illuminate\Contracts\Auth\UserProvider... diff --git a/authorization.md b/authorization.md index 121cbe51f13..d51a985a676 100644 --- a/authorization.md +++ b/authorization.md @@ -54,8 +54,6 @@ In this example, we'll define a gate to determine if a user can update a given ` */ public function boot(): void { - $this->registerPolicies(); - Gate::define('update-post', function (User $user, Post $post) { return $user->id === $post->user_id; }); @@ -71,8 +69,6 @@ Like controllers, gates may also be defined using a class callback array: */ public function boot(): void { - $this->registerPolicies(); - Gate::define('update-post', [PostPolicy::class, 'update']); } @@ -314,8 +310,6 @@ The `App\Providers\AuthServiceProvider` included with fresh Laravel applications */ public function boot(): void { - $this->registerPolicies(); - // ... } } diff --git a/passport.md b/passport.md index 7946888803e..eff060e8288 100644 --- a/passport.md +++ b/passport.md @@ -138,8 +138,6 @@ If necessary, you may define the path where Passport's keys should be loaded fro */ public function boot(): void { - $this->registerPolicies(); - Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); } @@ -202,8 +200,6 @@ By default, Passport issues long-lived access tokens that expire after one year. */ public function boot(): void { - $this->registerPolicies(); - Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::personalAccessTokensExpireIn(now()->addMonths(6)); @@ -237,8 +233,6 @@ After defining your model, you may instruct Passport to use your custom model vi */ public function boot(): void { - $this->registerPolicies(); - Passport::useTokenModel(Token::class); Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); @@ -780,8 +774,6 @@ The implicit grant is similar to the authorization code grant; however, the toke */ public function boot(): void { - $this->registerPolicies(); - Passport::enableImplicitGrant(); } @@ -1021,8 +1013,6 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` */ public function boot(): void { - $this->registerPolicies(); - Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status', @@ -1167,8 +1157,6 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo */ public function boot(): void { - $this->registerPolicies(); - Passport::cookie('custom_name'); } diff --git a/passwords.md b/passwords.md index 14651a4f727..27a5b2b96ff 100644 --- a/passwords.md +++ b/passwords.md @@ -182,8 +182,6 @@ You may customize the password reset link URL using the `createUrlUsing` method */ public function boot(): void { - $this->registerPolicies(); - ResetPassword::createUrlUsing(function (User $user, string $token) { return 'https://example.com/reset-password?token='.$token; }); From fe650535cdd614f0d55ac4fbf2279663392b8133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEuris?= Date: Thu, 16 Feb 2023 23:03:15 +0200 Subject: [PATCH 0783/2609] Update link to API docs (#8589) * Update documentation.md * Update documentation.md --------- Co-authored-by: Taylor Otwell --- documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation.md b/documentation.md index c35f8d35aa5..f5c52627da0 100644 --- a/documentation.md +++ b/documentation.md @@ -97,4 +97,4 @@ - [Socialite](/docs/{{version}}/socialite) - [Telescope](/docs/{{version}}/telescope) - [Valet](/docs/{{version}}/valet) -- [API Documentation](/api/9.x) +- [API Documentation](/api/master) From 5a0a44e9fc20112ef661c79040c3a94a232e358b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 16 Feb 2023 15:50:44 -0600 Subject: [PATCH 0784/2609] temporary upload urls --- filesystem.md | 53 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/filesystem.md b/filesystem.md index 96a166b4c2d..95f4a12fb53 100644 --- a/filesystem.md +++ b/filesystem.md @@ -12,6 +12,7 @@ - [Retrieving Files](#retrieving-files) - [Downloading Files](#downloading-files) - [File URLs](#file-urls) + - [Temporary URLs](#temporary-urls) - [File Metadata](#file-metadata) - [Storing Files](#storing-files) - [Prepending & Appending To Files](#prepending-appending-to-files) @@ -271,8 +272,20 @@ When using the `local` driver, all files that should be publicly accessible shou > **Warning** > When using the `local` driver, the return value of `url` is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs. + +#### URL Host Customization + +If you would like to pre-define the host for URLs generated using the `Storage` facade, you may add a `url` option to the disk's configuration array: + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + ], + -#### Temporary URLs +### Temporary URLs Using the `temporaryUrl` method, you may create temporary URLs to files stored using the `s3` driver. This method accepts a path and a `DateTime` instance specifying when the URL should expire: @@ -311,27 +324,33 @@ If you need to customize how temporary URLs are created for a specific storage d */ public function boot(): void { - Storage::disk('local')->buildTemporaryUrlsUsing(function (string $path, DateTime $expiration, array $options) { - return URL::temporarySignedRoute( - 'files.download', - $expiration, - array_merge($options, ['path' => $path]) - ); - }); + Storage::disk('local')->buildTemporaryUrlsUsing( + function (string $path, DateTime $expiration, array $options) { + return URL::temporarySignedRoute( + 'files.download', + $expiration, + array_merge($options, ['path' => $path]) + ); + } + ); } } - -#### URL Host Customization + +#### Temporary Upload URLs -If you would like to pre-define the host for URLs generated using the `Storage` facade, you may add a `url` option to the disk's configuration array: +> **Warning** +> The ability to generate temporary upload URLs is only supported by the `s3` driver. - 'public' => [ - 'driver' => 'local', - 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', - 'visibility' => 'public', - ], +If you need to generate a temporary URL that can be used to upload a file directly from your client-side application, you may use the `temporaryUploadUrl` method. This method accepts a path and a `DateTime` instance specifying when the URL should expire. The `temporaryUploadUrl` method returns an associative array which may be destructured into the upload URL and the headers that should be included with the upload request: + + use Illuminate\Support\Facades\Storage; + + ['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl( + 'file.jpg', now()->addMinutes(5) + ); + +This method is primarily useful in serverless environments that require the client-side application to directly upload files to a cloud storage system such as Amazon S3. ### File Metadata From 9315b1f16c6455f06d3c56e8b0fd0b0be63d35b6 Mon Sep 17 00:00:00 2001 From: Victor S <32728904+ulcuber@users.noreply.github.com> Date: Fri, 17 Feb 2023 18:34:21 +0400 Subject: [PATCH 0785/2609] fix: releases import typo (#8595) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index bc2e27acb03..6951ae795d3 100644 --- a/releases.md +++ b/releases.md @@ -161,7 +161,7 @@ Processes may even be started in pools, allowing for the convenient execution an ```php use Illuminate\Process\Pool; -use Illuminate\Support\Facades\Pool; +use Illuminate\Support\Facades\Process; [$first, $second, $third] = Process::concurrently(function (Pool $pool) { $pool->command('cat first.txt'); From f671159f6cd563180ecc21cf28e5f2d92c0475c5 Mon Sep 17 00:00:00 2001 From: Fityan Date: Fri, 17 Feb 2023 21:39:10 +0700 Subject: [PATCH 0786/2609] Add `laravel/sanctum` to the list of dependencies to update (#8592) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 2d4b27f012f..e40c7dfb08c 100644 --- a/upgrade.md +++ b/upgrade.md @@ -72,6 +72,7 @@ You should update the following dependencies in your application's `composer.jso
    - `laravel/framework` to `^10.0` +- `laravel/sanctum` to `^3.2` - `spatie/laravel-ignition` to `^2.0`
    From a5889cffd02fd828d5efa727edb43d3959b957df Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 17 Feb 2023 09:12:18 -0600 Subject: [PATCH 0787/2609] correct `registerPolicies` method name (#8591) fix the minor mis-type of the method name --- upgrade.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/upgrade.md b/upgrade.md index e40c7dfb08c..fb34c36f346 100644 --- a/upgrade.md +++ b/upgrade.md @@ -38,7 +38,7 @@ - [Relation `getBaseQuery` Method](#relation-getbasequery-method) - [The `Redirect::home` Method](#redirect-home) - [The `Bus::dispatchNow` Method](#dispatch-now) -- [The `registerPolicy` Method](#register-policy) +- [The `registerPolicies` Method](#register-policies) - [ULID Columns](#ulid-columns)
    @@ -112,8 +112,8 @@ app()->usePublicPath(__DIR__.'/public'); ### Authorization - -### The `registerPolicy` Method + +### The `registerPolicies` Method **Likelihood Of Impact: Low** From 788bdefdf40c635e3e941d604bc2a444397ee2dc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 17 Feb 2023 16:23:07 -0600 Subject: [PATCH 0788/2609] wip --- broadcasting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 8e84dd40f87..ac9eb2abe3d 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -116,7 +116,7 @@ The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) and [ ### Ably > **Note** -> The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). +> The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). If you plan to broadcast your events using [Ably](https://ably.com), you should install the Ably PHP SDK using the Composer package manager: @@ -212,7 +212,7 @@ window.Echo = new Echo({ ### Ably > **Note** -> The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). +> The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). [Laravel Echo](https://github.com/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. From d6072b7526e3ceb93d66885329bf44fb1b30e101 Mon Sep 17 00:00:00 2001 From: Aaron Parecki Date: Sat, 18 Feb 2023 13:35:02 -0800 Subject: [PATCH 0789/2609] clarifications for configuration caching (#8597) * clarifications for configuration caching clarify what you should do instead of just telling you what doesn't work * Update configuration.md * Update configuration.md --------- Co-authored-by: Taylor Otwell --- configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configuration.md b/configuration.md index 7f9c7fde14d..222adcdada9 100644 --- a/configuration.md +++ b/configuration.md @@ -197,6 +197,8 @@ To give your application a speed boost, you should cache all of your configurati You should typically run the `php artisan config:cache` command as part of your production deployment process. The command should not be run during local development as configuration options will frequently need to be changed during the course of your application's development. +Once the configuration has been cached, your application's `.env` file will not be loaded by the framework during requests or Artisan commands; therefore, the `env` function will only return external, system level environment variables. So, you should ensure you are only calling the `env` function from within your application's configuration (`config`) files. You can see many examples of this by examining Laravel's default configuration files. Configuration values may be accessed from anywhere in your application using the `config` function [described above](#accessing-configuration-values). + > **Warning** > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. From 1f481e9b0cf12517ae32f88f91839ea0fc25e74a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 18 Feb 2023 15:36:22 -0600 Subject: [PATCH 0790/2609] wip --- configuration.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index 222adcdada9..758f3bb4358 100644 --- a/configuration.md +++ b/configuration.md @@ -197,7 +197,9 @@ To give your application a speed boost, you should cache all of your configurati You should typically run the `php artisan config:cache` command as part of your production deployment process. The command should not be run during local development as configuration options will frequently need to be changed during the course of your application's development. -Once the configuration has been cached, your application's `.env` file will not be loaded by the framework during requests or Artisan commands; therefore, the `env` function will only return external, system level environment variables. So, you should ensure you are only calling the `env` function from within your application's configuration (`config`) files. You can see many examples of this by examining Laravel's default configuration files. Configuration values may be accessed from anywhere in your application using the `config` function [described above](#accessing-configuration-values). +Once the configuration has been cached, your application's `.env` file will not be loaded by the framework during requests or Artisan commands; therefore, the `env` function will only return external, system level environment variables. + +For this reason, you should ensure you are only calling the `env` function from within your application's configuration (`config`) files. You can see many examples of this by examining Laravel's default configuration files. Configuration values may be accessed from anywhere in your application using the `config` function [described above](#accessing-configuration-values). > **Warning** > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. From 0fd6501002f35a6944669fbc7166829be28193be Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 18 Feb 2023 19:59:29 -0600 Subject: [PATCH 0791/2609] clean up response examples --- authentication.md | 10 ++++++---- authorization.md | 34 +++++++++++++++++----------------- controllers.md | 19 +++++++++---------- eloquent.md | 6 +++--- encryption.md | 6 +++--- events.md | 6 +++--- hashing.md | 6 +++--- mail.md | 6 +++--- queues.md | 30 +++++++++++++++--------------- releases.md | 35 +---------------------------------- requests.md | 16 ++++++++-------- validation.md | 14 +++++++------- 12 files changed, 78 insertions(+), 110 deletions(-) diff --git a/authentication.md b/authentication.md index a15fa4b1b21..daa052737ef 100644 --- a/authentication.md +++ b/authentication.md @@ -146,19 +146,21 @@ Alternatively, once a user is authenticated, you may access the authenticated us namespace App\Http\Controllers; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class FlightController extends Controller { /** * Update the flight information for an existing flight. */ - public function update(Request $request): Response + public function update(Request $request): RedirectResponse { - // $request->user() + $user = $request->user(); + + // ... - return response()->noContent(); + return redirect('/flights'); } } diff --git a/authorization.md b/authorization.md index d51a985a676..9a320fe92a4 100644 --- a/authorization.md +++ b/authorization.md @@ -83,8 +83,8 @@ To authorize an action using gates, you should use the `allows` or `denies` meth use App\Http\Controllers\Controller; use App\Models\Post; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; use Illuminate\Support\Facades\Gate; class PostController extends Controller @@ -92,7 +92,7 @@ To authorize an action using gates, you should use the `allows` or `denies` meth /** * Update the given post. */ - public function update(Request $request, Post $post): Response + public function update(Request $request, Post $post): RedirectResponse { if (! Gate::allows('update-post', $post)) { abort(403); @@ -100,7 +100,7 @@ To authorize an action using gates, you should use the `allows` or `denies` meth // Update the post... - return response()->noContent(); + return redirect('/posts'); } } @@ -511,15 +511,15 @@ The `App\Models\User` model that is included with your Laravel application inclu use App\Http\Controllers\Controller; use App\Models\Post; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PostController extends Controller { /** * Update the given post. */ - public function update(Request $request, Post $post): Response + public function update(Request $request, Post $post): RedirectResponse { if ($request->user()->cannot('update', $post)) { abort(403); @@ -527,7 +527,7 @@ The `App\Models\User` model that is included with your Laravel application inclu // Update the post... - return response()->noContent(); + return redirect('/posts'); } } @@ -544,15 +544,15 @@ Remember, some actions may correspond to policy methods like `create` that do no use App\Http\Controllers\Controller; use App\Models\Post; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PostController extends Controller { /** * Create a post. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { if ($request->user()->cannot('create', Post::class)) { abort(403); @@ -560,7 +560,7 @@ Remember, some actions may correspond to policy methods like `create` that do no // Create the post... - return response()->noContent(); + return redirect('/posts'); } } @@ -577,8 +577,8 @@ Like the `can` method, this method accepts the name of the action you wish to au use App\Http\Controllers\Controller; use App\Models\Post; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PostController extends Controller { @@ -587,13 +587,13 @@ Like the `can` method, this method accepts the name of the action you wish to au * * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function update(Request $request, Post $post): Response + public function update(Request $request, Post $post): RedirectResponse { $this->authorize('update', $post); // The current user can update the blog post... - return response()->noContent(); + return redirect('/posts'); } } @@ -603,21 +603,21 @@ Like the `can` method, this method accepts the name of the action you wish to au 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\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; /** * Create a new blog post. * * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function create(Request $request): Response + public function create(Request $request): RedirectResponse { $this->authorize('create', Post::class); // The current user can create blog posts... - return response()->noContent(); + return redirect('/posts'); } @@ -782,11 +782,11 @@ When attempting to determine if the authenticated user can update a given post, * * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function update(Request $request, Post $post): Response + public function update(Request $request, Post $post): RedirectResponse { $this->authorize('update', [$post, $request->category]); // The current user can update the blog post... - return response()->noContent(); + return redirect('/posts'); } diff --git a/controllers.md b/controllers.md index 8f32ddffdac..5da6ad6cf89 100644 --- a/controllers.md +++ b/controllers.md @@ -77,11 +77,9 @@ If a controller action is particularly complex, you might find it convenient to /** * Provision a new web server. */ - public function __invoke(): Response + public function __invoke() { // ... - - return response()->noContent(); } } @@ -482,21 +480,21 @@ In addition to constructor injection, you may also type-hint dependencies on you namespace App\Http\Controllers; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class UserController extends Controller { /** * Store a new user. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $name = $request->name; - // ... + // Store the user... - return response()->noContent(); + return redirect('/users'); } } @@ -512,6 +510,7 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` param namespace App\Http\Controllers; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class UserController extends Controller @@ -519,10 +518,10 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` param /** * Update the given user. */ - public function update(Request $request, string $id): Response + public function update(Request $request, string $id): RedirectResponse { - // ... + // Update the user... - return response()->noContent(); + return redirect('/users'); } } diff --git a/eloquent.md b/eloquent.md index abb7353d208..7de9bfe6236 100644 --- a/eloquent.md +++ b/eloquent.md @@ -682,15 +682,15 @@ Of course, when using Eloquent, we don't only need to retrieve models from the d use App\Http\Controllers\Controller; use App\Models\Flight; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class FlightController extends Controller { /** * Store a new flight in the database. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { // Validate the request... @@ -700,7 +700,7 @@ Of course, when using Eloquent, we don't only need to retrieve models from the d $flight->save(); - return response()->noContent(); + return redirect('/flights'); } } diff --git a/encryption.md b/encryption.md index b46f17d074c..cbc788e1471 100644 --- a/encryption.md +++ b/encryption.md @@ -28,8 +28,8 @@ You may encrypt a value using the `encryptString` method provided by the `Crypt` use App\Http\Controllers\Controller; use App\Models\User; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; use Illuminate\Support\Facades\Crypt; class DigitalOceanTokenController extends Controller @@ -37,13 +37,13 @@ You may encrypt a value using the `encryptString` method provided by the `Crypt` /** * Store a DigitalOcean API token for the user. */ - public function storeSecret(Request $request): Response + public function store(Request $request): RedirectResponse { $request->user()->fill([ 'token' => Crypt::encryptString($request->token), ])->save(); - return response()->noContent(); + return redirect('/secrets'); } } diff --git a/events.md b/events.md index 65b08c4fd67..271cd7cb691 100644 --- a/events.md +++ b/events.md @@ -495,15 +495,15 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th use App\Events\OrderShipped; use App\Http\Controllers\Controller; use App\Models\Order; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class OrderShipmentController extends Controller { /** * Ship the given order. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $order = Order::findOrFail($request->order_id); @@ -511,7 +511,7 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th OrderShipped::dispatch($order); - return response()->noContent(); + return redirect('/orders'); } } diff --git a/hashing.md b/hashing.md index 91c5109c1f5..d674335094f 100644 --- a/hashing.md +++ b/hashing.md @@ -32,8 +32,8 @@ You may hash a password by calling the `make` method on the `Hash` facade: namespace App\Http\Controllers; use App\Http\Controllers\Controller; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; use Illuminate\Support\Facades\Hash; class PasswordController extends Controller @@ -41,7 +41,7 @@ You may hash a password by calling the `make` method on the `Hash` facade: /** * Update the password for the user. */ - public function update(Request $request): Response + public function update(Request $request): RedirectResponse { // Validate the new password length... @@ -49,7 +49,7 @@ You may hash a password by calling the `make` method on the `Hash` facade: 'password' => Hash::make($request->newPassword) ])->save(); - return response()->noContent(); + return redirect('/profile'); } } diff --git a/mail.md b/mail.md index 77a736594b5..59816acdda3 100644 --- a/mail.md +++ b/mail.md @@ -736,8 +736,8 @@ To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/ use App\Http\Controllers\Controller; use App\Mail\OrderShipped; use App\Models\Order; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; use Illuminate\Support\Facades\Mail; class OrderShipmentController extends Controller @@ -745,7 +745,7 @@ To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/ /** * Ship the given order. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $order = Order::findOrFail($request->order_id); @@ -753,7 +753,7 @@ To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/ Mail::to($request->user())->send(new OrderShipped($order)); - return response()->noContent(); + return redirect('/orders'); } } diff --git a/queues.md b/queues.md index 17ba4a91987..73c5582ebde 100644 --- a/queues.md +++ b/queues.md @@ -635,15 +635,15 @@ 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\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $podcast = Podcast::create(/* ... */); @@ -651,7 +651,7 @@ Once you have written your job class, you may dispatch it using the `dispatch` m ProcessPodcast::dispatch($podcast); - return response()->noContent(); + return redirect('/podcasts'); } } @@ -675,15 +675,15 @@ If you would like to specify that a job should not be immediately available for use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $podcast = Podcast::create(/* ... */); @@ -692,7 +692,7 @@ If you would like to specify that a job should not be immediately available for ProcessPodcast::dispatch($podcast) ->delay(now()->addMinutes(10)); - return response()->noContent(); + return redirect('/podcasts'); } } @@ -729,15 +729,15 @@ If you would like to dispatch a job immediately (synchronously), you may use the use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $podcast = Podcast::create(/* ... */); @@ -745,7 +745,7 @@ If you would like to dispatch a job immediately (synchronously), you may use the ProcessPodcast::dispatchSync($podcast); - return response()->noContent(); + return redirect('/podcasts'); } } @@ -856,15 +856,15 @@ 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\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $podcast = Podcast::create(/* ... */); @@ -872,7 +872,7 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e ProcessPodcast::dispatch($podcast)->onQueue('processing'); - return response()->noContent(); + return redirect('/podcasts'); } } @@ -913,15 +913,15 @@ If your application interacts with multiple queue connections, you may specify w use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class PodcastController extends Controller { /** * Store a new podcast. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $podcast = Podcast::create(/* ... */); @@ -929,7 +929,7 @@ If your application interacts with multiple queue connections, you may specify w ProcessPodcast::dispatch($podcast)->onConnection('sqs'); - return response()->noContent(); + return redirect('/podcasts'); } } diff --git a/releases.md b/releases.md index 6951ae795d3..8b56e924f97 100644 --- a/releases.md +++ b/releases.md @@ -67,40 +67,7 @@ _Application skeleton and stub type-hints were contributed by [Nuno Maduro](http On its initial release, Laravel utilized all of the type-hinting features available in PHP at the time. However, many new features have been added to PHP in the subsequent years, including additional primitive type-hints, return types, and union types. -Laravel 10.x thoroughly updates the application skeleton and all stubs utilized by the framework to introduce argument and return types to all method signatures. In addition, extraneous "doc block" type-hint information has been deleted: - -```php -input('name'); - // ... + // Store the user... - return response()->noContent(); + return redirect('/users'); } } @@ -79,19 +79,19 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` route namespace App\Http\Controllers; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; class UserController extends Controller { /** * Update the specified user. */ - public function update(Request $request, string $id): Response + public function update(Request $request, string $id): RedirectResponse { - // ... + // Update the user... - return response()->noContent(); + return redirect('/users'); } } diff --git a/validation.md b/validation.md index 4ef9e7a0cac..14297bc5f5c 100644 --- a/validation.md +++ b/validation.md @@ -109,7 +109,7 @@ To get a better understanding of the `validate` method, let's jump back into the /** * Store a new blog post. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'title' => 'required|unique:posts|max:255', @@ -118,7 +118,7 @@ To get a better understanding of the `validate` method, let's jump back into the // The blog post is valid... - return response()->noContent(); + return redirect('/posts'); } 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. @@ -328,7 +328,7 @@ So, how are the validation rules evaluated? All you need to do is type-hint the /** * Store a new blog post. */ - public function store(StorePostRequest $request): Response + public function store(StorePostRequest $request): RedirectResponse { // The incoming request is valid... @@ -341,7 +341,7 @@ So, how are the validation rules evaluated? All you need to do is type-hint the // Store the blog post... - return response()->noContent(); + return redirect('/posts'); } 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](#validation-error-response-format). @@ -513,8 +513,8 @@ If you do not want to use the `validate` method on the request, you may create a namespace App\Http\Controllers; use App\Http\Controllers\Controller; + use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; - use Illuminate\Http\Response; use Illuminate\Support\Facades\Validator; class PostController extends Controller @@ -522,7 +522,7 @@ If you do not want to use the `validate` method on the request, you may create a /** * Store a new blog post. */ - public function store(Request $request): Response + public function store(Request $request): RedirectResponse { $validator = Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', @@ -544,7 +544,7 @@ If you do not want to use the `validate` method on the request, you may create a // Store the blog post... - return response()->noContent(); + return redirect('/posts'); } } From 333db95cf9555c8672df8f384425e43e4606f0d4 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 21 Feb 2023 22:51:28 +1100 Subject: [PATCH 0792/2609] [10.x] Document pennant store testing env (#8604) * documents pennant store testing env * Update pennant.md * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pennant.md b/pennant.md index 3dffb0202a8..6419541fec8 100644 --- a/pennant.md +++ b/pennant.md @@ -815,6 +815,22 @@ public function test_it_can_control_feature_values() If your feature is returning a `Lottery` instance, there are a handful of useful [testing helpers available](/docs/{{version}}/helpers#testing-lotteries). + +#### Store Configuration + +You may configure the store that Pennant will use during testing by defining the `PENNANT_STORE` environment variable in your application's `phpunit.xml` file: + +```xml + + + + + + + + +``` + ## Adding Custom Pennant Drivers From fe4f7e8ef765f54fde0c42aa36378f2f44096605 Mon Sep 17 00:00:00 2001 From: Hamid Afghan Date: Tue, 21 Feb 2023 12:51:47 +0100 Subject: [PATCH 0793/2609] Update valet php version to 8.1 (#8602) --- valet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/valet.md b/valet.md index 6cda947b746..8062e4de200 100644 --- a/valet.md +++ b/valet.md @@ -105,7 +105,7 @@ Valet will automatically start its required services each time your machine boot 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: ```shell -valet use php@7.2 +valet use php@8.1 valet use php ``` @@ -113,7 +113,7 @@ valet use php You may also create a `.valetphprc` file in the root of your project. The `.valetphprc` file should contain the PHP version the site should use: ```shell -php@7.2 +php@8.1 ``` Once this file has been created, you may simply execute the `valet use` command and the command will determine the site's preferred PHP version by reading the file. From 01ad980e7459c3c9297f204f67bd9feb0705c448 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 21 Feb 2023 12:54:21 +0100 Subject: [PATCH 0794/2609] [10.x] Add note about IDE warnings (#8600) * Add note about IDE warnings We still get issues about docblocks regularly so I figured I'd add a note about this to the contribution guide. * Update contributions.md * Update contributions.md --------- Co-authored-by: Taylor Otwell --- contributions.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contributions.md b/contributions.md index 4ce931f9d4d..ac9a71b4633 100644 --- a/contributions.md +++ b/contributions.md @@ -20,6 +20,8 @@ However, if you file a bug report, your issue should contain a title and a clear Remember, bug reports are created in the hope that others with the same problem will be able to collaborate with you on solving it. Do not expect that the bug report will automatically see any activity or that others will jump to fix it. Creating a bug report serves to help yourself and others start on the path of fixing the problem. If you want to chip in, you can help out by fixing [any bugs listed in our issue trackers](https://github.com/issues?q=is%3Aopen+is%3Aissue+label%3Abug+user%3Alaravel). You must be authenticated with GitHub to view all of Laravel's issues. +If you notice improper DocBlock, PHPStan, or IDE warnings while using Laravel, do not create a GitHub issue. Instead, please submit a pull request to fix the problem. + The Laravel source code is managed on GitHub, and there are repositories for each of the Laravel projects:
    @@ -38,6 +40,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Horizon](https://github.com/laravel/horizon) - [Laravel Jetstream](https://github.com/laravel/jetstream) - [Laravel Passport](https://github.com/laravel/passport) +- [Laravel Pennant](https://github.com/laravel/pennant) - [Laravel Pint](https://github.com/laravel/pint) - [Laravel Sail](https://github.com/laravel/sail) - [Laravel Sanctum](https://github.com/laravel/sanctum) From 01966e9766be78535767f32b594be63df5021409 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 21 Feb 2023 12:00:01 +0000 Subject: [PATCH 0795/2609] Removes `@return $this` when returning static (#8601) --- eloquent-factories.md | 2 -- validation.md | 1 - 2 files changed, 3 deletions(-) diff --git a/eloquent-factories.md b/eloquent-factories.md index 3adb4392c93..39d6bd59b40 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -146,8 +146,6 @@ Factory callbacks are registered using the `afterMaking` and `afterCreating` met { /** * Configure the model factory. - * - * @return $this */ public function configure(): static { diff --git a/validation.md b/validation.md index 14297bc5f5c..6e5952391f6 100644 --- a/validation.md +++ b/validation.md @@ -2166,7 +2166,6 @@ If your custom validation rule class needs to access all of the other data under * Set the data under validation. * * @param array $data - * @return $this */ public function setData(array $data): static { From 200d712f4e19f7acfb019f36ea3c6c2f9d67104e Mon Sep 17 00:00:00 2001 From: Volodya Kurshudyan <70023120+xurshudyan@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:09:31 +0400 Subject: [PATCH 0796/2609] Add docs for methods (#8599) Co-authored-by: Valodia --- eloquent-relationships.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 2988a1810b3..c844a76b657 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1915,6 +1915,19 @@ You may use the `createMany` method to create multiple related models: ['message' => 'Another new comment.'], ]); +The `createQuietly` and `createManyQuietly` methods may be used to create a model(s) without dispatching any events: + + $user = User::find(1); + + $user->posts()->createQuietly([ + 'title' => 'Post title.', + ]); + + $user->posts()->createManyQuietly([ + ['title' => 'First post.'], + ['title' => 'Second post.'], + ]); + You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods to [create and update models on relationships](/docs/{{version}}/eloquent#upserts). > **Note** From 1e462c635b3b524892476431b2f3430079af32b2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Feb 2023 19:32:27 +0300 Subject: [PATCH 0797/2609] wip --- upgrade.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index fb34c36f346..545d7632372 100644 --- a/upgrade.md +++ b/upgrade.md @@ -77,7 +77,9 @@ You should update the following dependencies in your application's `composer.jso
    -Optionally, if you wish to use [PHPUnit 10](https://phpunit.de/announcements/phpunit-10.html), you should delete the `processUncoveredFiles` attribute from the `` section of your application's `phpunit.xml` configuration file. Then, update the following dependencies in your application's `composer.json` file: +If you are upgrading to Sanctum 3.x from the 2.x release series, please consult the [Sanctum upgrade guide](https://github.com/laravel/sanctum/blob/3.x/UPGRADE.md). + +Furthermore, if you wish to use [PHPUnit 10](https://phpunit.de/announcements/phpunit-10.html), you should delete the `processUncoveredFiles` attribute from the `` section of your application's `phpunit.xml` configuration file. Then, update the following dependencies in your application's `composer.json` file:
    From 7cfd23b11755405764b8bba689a64269f20daeb1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Feb 2023 21:33:53 +0300 Subject: [PATCH 0798/2609] wip --- processes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/processes.md b/processes.md index 0a49278484a..8a974824f7c 100644 --- a/processes.md +++ b/processes.md @@ -191,12 +191,12 @@ $result = $process->wait(); ### Process IDs & Signals -The `pid` method may be used to retrieve the operating system assigned process ID of the running process: +The `id` method may be used to retrieve the operating system assigned process ID of the running process: ```php $process = Process::start('bash import.sh'); -return $process->pid(); +return $process->id(); ``` You may use the `signal` method to send a "signal" to the running process. A list of predefined signal constants can be found within the [PHP documentation](https://www.php.net/manual/en/pcntl.constants.php): @@ -302,7 +302,7 @@ return $results['first']->output(); Since the process pool's `running` method provides a collection of all invoked processes within the pool, you may easily access the underlying pool process IDs: ```php -$processIds = $pool->running()->each->pid(); +$processIds = $pool->running()->each->id(); ``` And, for convenience, you may invoke the `signal` method on a process pool to send a signal to every process within the pool: From 2e062ca334aacff39590afca7de003ba18e6537a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 22 Feb 2023 11:43:57 +0000 Subject: [PATCH 0799/2609] [4.x] Documents Telescope's log `level` (#8607) * Documents Telescope's log `level` * Update telescope.md --------- Co-authored-by: Taylor Otwell --- telescope.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/telescope.md b/telescope.md index 40decdde8f6..a6bda67018a 100644 --- a/telescope.md +++ b/telescope.md @@ -337,6 +337,17 @@ The job watcher records the data and status of any [jobs](/docs/{{version}}/queu The log watcher records the [log data](/docs/{{version}}/logging) for any logs written by your application. +By default, Telescope will only record logs at the `error` level and above. However, you can modify the `level` option in your application's `config/telescope.php` configuration file to modify this behavior: + + 'watchers' => [ + Watchers\LogWatcher::class => [ + 'enabled' => env('TELESCOPE_LOG_WATCHER', true), + 'level' => 'debug', + ], + + // ... + ], + ### Mail Watcher From 737bdeb7bcf8b12c6b7ce4a467019329f591f292 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 22 Feb 2023 15:09:13 +0300 Subject: [PATCH 0800/2609] wip --- processes.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/processes.md b/processes.md index 8a974824f7c..1456adde421 100644 --- a/processes.md +++ b/processes.md @@ -73,6 +73,15 @@ You may use the `path` method to specify the working directory of the process. I $result = Process::path(__DIR__)->run('ls -la'); ``` + +#### Input + +You may provide input via the "standard input" of the process using the `input` method: + +```php +$result = Process::input('Hello World')->run('cat'); +``` + #### Timeouts From 512e4029666a8c8204c25dc6344900e6fef8156b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20Vil=C3=A0?= Date: Wed, 22 Feb 2023 15:48:32 +0100 Subject: [PATCH 0801/2609] [10.x] Replace optional by Null-safe operator (#8609) * Update authorization.md * Update authorization.md * use nullsafe --------- Co-authored-by: Taylor Otwell --- authorization.md | 2 +- cache.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/authorization.md b/authorization.md index 9a320fe92a4..b5e0d8531eb 100644 --- a/authorization.md +++ b/authorization.md @@ -469,7 +469,7 @@ By default, all gates and policies automatically return `false` if the incoming */ public function update(?User $user, Post $post): bool { - return optional($user)->id === $post->user_id; + return $user?->id === $post->user_id; } } diff --git a/cache.md b/cache.md index 8d6fe89520c..1517900e484 100644 --- a/cache.md +++ b/cache.md @@ -362,7 +362,7 @@ If the lock is not available at the moment you request it, you may instruct Lara } catch (LockTimeoutException $e) { // Unable to acquire lock... } finally { - optional($lock)->release(); + $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: From 1bbe7401c5775d8e0d2ecd53bd7999ad22c47a68 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 23 Feb 2023 13:57:56 +0100 Subject: [PATCH 0802/2609] Update upgrade.md (#8611) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 545d7632372..7f4989d9a28 100644 --- a/upgrade.md +++ b/upgrade.md @@ -73,6 +73,7 @@ You should update the following dependencies in your application's `composer.jso - `laravel/framework` to `^10.0` - `laravel/sanctum` to `^3.2` +- `doctrine/dbal` to `^3.0` - `spatie/laravel-ignition` to `^2.0`
    From 9be6dee38ece87a6408a3ae5036404a0659d1263 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Fri, 24 Feb 2023 23:06:21 +0330 Subject: [PATCH 0803/2609] [10.x] Add make controller command into the basic controller (#8612) * add make controller command into the basic controller * formatting --------- Co-authored-by: Taylor Otwell --- controllers.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/controllers.md b/controllers.md index 5da6ad6cf89..ad62c11e3ad 100644 --- a/controllers.md +++ b/controllers.md @@ -27,7 +27,13 @@ Instead of defining all of your request handling logic as closures in your route ### Basic Controllers -Let's take a look at an example of a basic controller. Note that the controller extends the base controller class included with Laravel: `App\Http\Controllers\Controller`: +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: + +```shell +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: Date: Sat, 25 Feb 2023 07:08:50 +0100 Subject: [PATCH 0804/2609] Updated link to Jetstream 3.0 (#8614) --- starter-kits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starter-kits.md b/starter-kits.md index b4fab3552d0..5b9755739c2 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -126,4 +126,4 @@ While Laravel Breeze provides a simple and minimal starting point for building a 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](https://inertiajs.com) driven frontend scaffolding. -Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/2.x/introduction.html). +Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/3.x/introduction.html). From 0aac8427f58d645083772ff1c6a283b38336c71a Mon Sep 17 00:00:00 2001 From: Jean-Pierre Fourie Date: Mon, 27 Feb 2023 20:44:42 +0200 Subject: [PATCH 0805/2609] [10.x] Removed PHP 8.1 warnings (#8621) --- eloquent-mutators.md | 3 --- validation.md | 3 --- 2 files changed, 6 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 3ec715fb6a5..693ef77af42 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -412,9 +412,6 @@ If a custom format is applied to the `date` or `datetime` cast, such as `datetim ### Enum Casting -> **Warning** -> Enum casting is only available for PHP 8.1+. - Eloquent also allows you to cast your attribute values to PHP [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: use App\Enums\ServerStatus; diff --git a/validation.md b/validation.md index 6e5952391f6..d25553d3a79 100644 --- a/validation.md +++ b/validation.md @@ -1194,9 +1194,6 @@ The `Enum` rule is a class based rule that validates whether the field under val 'status' => [new Enum(ServerStatus::class)], ]); -> **Warning** -> Enums are only available on PHP 8.1+. - #### exclude From 2fd24d58d7f48a086b30ed5503e57bad8a283372 Mon Sep 17 00:00:00 2001 From: Volodya Kurshudyan <70023120+xurshudyan@users.noreply.github.com> Date: Wed, 1 Mar 2023 23:39:54 +0400 Subject: [PATCH 0806/2609] [10.x] Add `intersectAssoc` method to docs (#8623) * Add intersectAssoc method * Update collections.md * Update collections.md --------- Co-authored-by: Valodia Co-authored-by: Taylor Otwell --- collections.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/collections.md b/collections.md index 6031e5e5b7e..0919e826bef 100644 --- a/collections.md +++ b/collections.md @@ -136,6 +136,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [hasAny](#method-hasany) [implode](#method-implode) [intersect](#method-intersect) +[intersectAssoc](#method-intersectAssoc) [intersectByKeys](#method-intersectbykeys) [isEmpty](#method-isempty) [isNotEmpty](#method-isnotempty) @@ -1169,6 +1170,27 @@ The `intersect` method removes any values from the original collection that are > **Note** > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). + +#### `intersectAssoc()` {.collection-method} + +The `intersectAssoc` method compares the original collection against another collection or `array`, returning the key / value pairs that are present in all of the given collections: + + $collection = collect([ + 'color' => 'red', + 'size' => 'M', + 'material' => 'cotton' + ]); + + $intersect = $collection->intersectAssoc([ + 'color' => 'blue', + 'size' => 'M', + 'material' => 'polyester' + ]); + + $intersect->all(); + + // ['size' => 'M'] + #### `intersectByKeys()` {.collection-method} @@ -3412,6 +3434,7 @@ Almost all methods available on the `Collection` class are also available on the [has](#method-has) [implode](#method-implode) [intersect](#method-intersect) +[intersectAssoc](#method-intersectAssoc) [intersectByKeys](#method-intersectbykeys) [isEmpty](#method-isempty) [isNotEmpty](#method-isnotempty) From 6fd375ca2f19d10dc5ed434f762f786300e19d29 Mon Sep 17 00:00:00 2001 From: Italo Date: Wed, 1 Mar 2023 16:58:51 -0300 Subject: [PATCH 0807/2609] [10.x] Adds more examples to time manipulation (#8620) * [10.x] Some love to time manipulation * Minor fix * Fixes codeblock tags * Update mocking.md Co-authored-by: Anjorin Damilare * Update mocking.md Co-authored-by: Anjorin Damilare * Update mocking.md Co-authored-by: Anjorin Damilare * formatting --------- Co-authored-by: Anjorin Damilare Co-authored-by: Taylor Otwell --- mocking.md | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/mocking.md b/mocking.md index a50179d57ad..be8ee7a522d 100644 --- a/mocking.md +++ b/mocking.md @@ -149,11 +149,6 @@ When testing, you may occasionally need to modify the time returned by helpers s $this->travel(5)->weeks(); $this->travel(5)->years(); - // Freeze time and resume normal time after executing closure... - $this->freezeTime(function (Carbon $time) { - // ... - }); - // Travel into the past... $this->travel(-5)->hours(); @@ -163,3 +158,40 @@ When testing, you may occasionally need to modify the time returned by helpers s // Return back to the present time... $this->travelBack(); } + +You may also provide a closure to the various time travel methods. The closure will be invoked with time frozen at the specified time. Once the closure has executed, time will resume as normal: + + $this->travel(5)->days(function () { + // Test something five days into the future... + }); + + $this->travelTo(now()->subDays(10), function () { + // Test something during a given moment... + }); + +The `freezeTime` method may be used to freeze the current time. Similarly, the `freezeSecond` method will freeze the current time but at the start of the current second: + + use Illuminate\Support\Carbon; + + // Freeze time and resume normal time after executing closure... + $this->freezeTime(function (Carbon $time) { + // ... + }); + + // Freeze time at the current second and resume normal time after executing closure... + $this->freezeSecond(function (Carbon $time) { + // ... + }) + +As you would expect, all of the methods discussed above are primarily useful for testing time sensitive application behavior, such as locking inactive posts on a discussion forum: + + use App\Models\Thread; + + public function test_forum_threads_lock_after_one_week_of_inactivity() + { + $thread = Thread::factory()->create(); + + $this->travel(1)->week(); + + $this->assertTrue($thread->isLockedByInactivity()); + } From f5e191d35d281d571515befad8bb6dcb07bc54af Mon Sep 17 00:00:00 2001 From: christiaanschmid <34045064+christiaanschmid@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:08:14 -0700 Subject: [PATCH 0808/2609] fixed small typo in upgrade documentation (#8625) --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 7f4989d9a28..e3572024c1f 100644 --- a/upgrade.md +++ b/upgrade.md @@ -129,7 +129,7 @@ The `registerPolicies` method of the `AuthServiceProvider` is now invoked automa **Likelihood Of Impact: Medium** -Redis [cache tag](/docs/{{version}}/cache#cache-tags) support has been rewritten for better performance and storage efficiency. In previously releases of Laravel, stale cache tags would accumulate in the cache when using Redis as your application's cache driver. +Redis [cache tag](/docs/{{version}}/cache#cache-tags) support has been rewritten for better performance and storage efficiency. In previous releases of Laravel, stale cache tags would accumulate in the cache when using Redis as your application's cache driver. However, to properly prune stale cache tag entries, Laravel's new `cache:prune-stale-tags` Artisan command should be [scheduled](/docs/{{version}}/scheduling) in your application's `App\Console\Kernel` class: From b788ef5159f9242a176294fd5a7345c24ecb1cce Mon Sep 17 00:00:00 2001 From: Francesco Bedini Date: Thu, 2 Mar 2023 15:34:12 +0100 Subject: [PATCH 0809/2609] [10.x] Pass a value to an option with a shortcut without = (#8624) * Pass a value to an option with a shortcut without = Explain that `--queue=default` equals to `-Qdefault` and not to `-Q=default` (otherwise `$this->option('queue');` returns `"=default"` instead of `"default"`) * Update artisan.md --------- Co-authored-by: Taylor Otwell --- artisan.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artisan.md b/artisan.md index 843bb5898b2..02f43fb62c1 100644 --- a/artisan.md +++ b/artisan.md @@ -321,10 +321,10 @@ To assign a shortcut when defining an option, you may specify it before the opti 'mail:send {user} {--Q|queue}' -When invoking the command on your terminal, option shortcuts should be prefixed with a single hyphen: +When invoking the command on your terminal, option shortcuts should be prefixed with a single hyphen and no `=` character should be included when specifying a value for the option: ```shell -php artisan mail:send 1 -Q +php artisan mail:send 1 -Qdefault ``` From de05075c58337e39124a148d66d9c27b6efb2fa0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 2 Mar 2023 09:06:28 -0600 Subject: [PATCH 0810/2609] wip --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 3d33533066f..2bf28a59b47 100644 --- a/helpers.md +++ b/helpers.md @@ -212,6 +212,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [mask](#method-fluent-str-mask) [match](#method-fluent-str-match) [matchAll](#method-fluent-str-match-all) +[isMatch](#method-fluent-str-is-match) [newLine](#method-fluent-str-new-line) [padBoth](#method-fluent-str-padboth) [padLeft](#method-fluent-str-padleft) @@ -2723,6 +2724,21 @@ If you specify a matching group within the expression, Laravel will return a col If no matches are found, an empty collection will be returned. + +#### `isMatch` {.collection-method} + +The `isMatch` method will return `true` if the string matches a given regular expression: + + use Illuminate\Support\Str; + + $result = Str::of('foo bar')->isMatch('/foo (.*)/'); + + // true + + $result = Str::of('laravel')->match('/foo (.*)/'); + + // false + #### `newLine` {.collection-method} From df24b05bb985f45152b209cdd7670f2aac7eec00 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 2 Mar 2023 12:58:40 -0600 Subject: [PATCH 0811/2609] wip --- sail.md | 8 ++++---- scout.md | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/sail.md b/sail.md index 247bdd97422..f19291673df 100644 --- a/sail.md +++ b/sail.md @@ -13,7 +13,7 @@ - [Interacting With Databases](#interacting-with-sail-databases) - [MySQL](#mysql) - [Redis](#redis) - - [MeiliSearch](#meilisearch) + - [Meilisearch](#meilisearch) - [File Storage](#file-storage) - [Running Tests](#running-tests) - [Laravel Dusk](#laravel-dusk) @@ -229,11 +229,11 @@ Your application's `docker-compose.yml` file also contains an entry for a [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. -### MeiliSearch +### Meilisearch -If you chose to install the [MeiliSearch](https://www.meilisearch.com) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this powerful search-engine that is [compatible](https://github.com/meilisearch/meilisearch-laravel-scout) with [Laravel Scout](/docs/{{version}}/scout). Once you have started your containers, you may connect to the MeiliSearch instance within your application by setting your `MEILISEARCH_HOST` environment variable to `http://meilisearch:7700`. +If you chose to install the [Meilisearch](https://www.meilisearch.com) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this powerful search-engine that is [compatible](https://github.com/meilisearch/meilisearch-laravel-scout) with [Laravel Scout](/docs/{{version}}/scout). Once you have started your containers, you may connect to the Meilisearch instance within your application by setting your `MEILISEARCH_HOST` environment variable to `http://meilisearch:7700`. -From your local machine, you may access MeiliSearch's web based administration panel by navigating to `http://localhost:7700` in your web browser. +From your local machine, you may access Meilisearch's web based administration panel by navigating to `http://localhost:7700` in your web browser. ## File Storage diff --git a/scout.md b/scout.md index a0ee87e2199..4a8ce9163e8 100644 --- a/scout.md +++ b/scout.md @@ -33,7 +33,7 @@ [Laravel Scout](https://github.com/laravel/scout) provides a simple, driver based solution for adding full-text search to your [Eloquent models](/docs/{{version}}/eloquent). Using model observers, Scout will automatically keep your search indexes in sync with your Eloquent records. -Currently, Scout ships with [Algolia](https://www.algolia.com/), [MeiliSearch](https://www.meilisearch.com), and MySQL / PostgreSQL (`database`) drivers. In addition, Scout includes a "collection" driver that is designed for local development usage and does not require any external dependencies or third-party services. Furthermore, writing custom drivers is simple and you are free to extend Scout with your own search implementations. +Currently, Scout ships with [Algolia](https://www.algolia.com/), [Meilisearch](https://www.meilisearch.com), and MySQL / PostgreSQL (`database`) drivers. In addition, Scout includes a "collection" driver that is designed for local development usage and does not require any external dependencies or third-party services. Furthermore, writing custom drivers is simple and you are free to extend Scout with your own search implementations. ## Installation @@ -77,17 +77,17 @@ composer require algolia/algoliasearch-client-php ``` -#### MeiliSearch +#### Meilisearch -[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. +[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: ```shell 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: +Then, set the `SCOUT_DRIVER` environment variable as well as your Meilisearch `host` and `key` credentials within your application's `.env` file: ```ini SCOUT_DRIVER=meilisearch @@ -95,12 +95,12 @@ MEILISEARCH_HOST=http://127.0.0.1:7700 MEILISEARCH_KEY=masterKey ``` -For more information regarding MeiliSearch, please consult the [MeiliSearch documentation](https://docs.meilisearch.com/learn/getting_started/quick_start.html). +For more information regarding Meilisearch, please consult the [Meilisearch documentation](https://docs.meilisearch.com/learn/getting_started/quick_start.html). -In addition, you should ensure that you install a version of `meilisearch/meilisearch-php` that is compatible with your MeiliSearch binary version by reviewing [MeiliSearch's documentation regarding binary compatibility](https://github.com/meilisearch/meilisearch-php#-compatibility-with-meilisearch). +In addition, you should ensure that you install a version of `meilisearch/meilisearch-php` that is compatible with your Meilisearch binary version by reviewing [Meilisearch's documentation regarding binary compatibility](https://github.com/meilisearch/meilisearch-php#-compatibility-with-meilisearch). > **Warning** -> When upgrading Scout on an application that utilizes MeiliSearch, you should always [review any additional breaking changes](https://github.com/meilisearch/MeiliSearch/releases) to the MeiliSearch service itself. +> When upgrading Scout on an application that utilizes Meilisearch, you should always [review any additional breaking changes](https://github.com/meilisearch/Meilisearch/releases) to the Meilisearch service itself. ### Queueing @@ -179,7 +179,7 @@ By default, the entire `toArray` form of a given model will be persisted to its } } -Some search engines such as MeiliSearch will only perform filter operations (`>`, `<`, etc.) on data of the correct type. So, when using these search engines and customizing your searchable data, you should ensure that numeric values are cast to their correct type: +Some search engines such as Meilisearch will only perform filter operations (`>`, `<`, etc.) on data of the correct type. So, when using these search engines and customizing your searchable data, you should ensure that numeric values are cast to their correct type: public function toSearchableArray() { @@ -191,9 +191,9 @@ Some search engines such as MeiliSearch will only perform filter operations (`>` } -#### Configuring Filterable Data & Index Settings (MeiliSearch) +#### Configuring Filterable Data & Index Settings (Meilisearch) -Unlike Scout's other drivers, MeiliSearch requires you to pre-define index search settings such as filterable attributes, sortable attributes, and [other supported settings fields](https://docs.meilisearch.com/reference/api/settings.html). +Unlike Scout's other drivers, Meilisearch requires you to pre-define index search settings such as filterable attributes, sortable attributes, and [other supported settings fields](https://docs.meilisearch.com/reference/api/settings.html). Filterable attributes are any attributes you plan to filter on when invoking Scout's `where` method, while sortable attributes are any attributes you plan to sort by when invoking Scout's `orderBy` method. To define your index settings, adjust the `index-settings` portion of your `meilisearch` configuration entry in your application's `scout` configuration file: @@ -226,7 +226,7 @@ If the model underlying a given index is soft deletable and is included in the ` ], ``` -After configuring your application's index settings, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform MeiliSearch of your currently configured index settings. For convenience, you may wish to make this command part of your deployment process: +After configuring your application's index settings, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform Meilisearch of your currently configured index settings. For convenience, you may wish to make this command part of your deployment process: ```shell php artisan scout:sync-index-settings @@ -320,7 +320,7 @@ To use the database engine, you may simply set the value of the `SCOUT_DRIVER` e SCOUT_DRIVER=database ``` -Once you have specified the database engine as your preferred driver, you must [configure your searchable data](#configuring-searchable-data). Then, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia or MeiliSearch indexes, is unnecessary when using the database engine. +Once you have specified the database engine as your preferred driver, you must [configure your searchable data](#configuring-searchable-data). Then, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia or Meilisearch indexes, is unnecessary when using the database engine. #### Customizing Database Searching Strategies @@ -356,7 +356,7 @@ public function toSearchableArray(): array ### Collection Engine -While you are free to use the Algolia or MeiliSearch search engines during local development, you may find it more convenient to get started with the "collection" engine. The collection engine will use "where" clauses and collection filtering on results from your existing database to determine the applicable search results for your query. When using this engine, it is not necessary to "index" your searchable models, as they will simply be retrieved from your local database. +While you are free to use the Algolia or Meilisearch search engines during local development, you may find it more convenient to get started with the "collection" engine. The collection engine will use "where" clauses and collection filtering on results from your existing database to determine the applicable search results for your query. When using this engine, it is not necessary to "index" your searchable models, as they will simply be retrieved from your local database. To use the collection engine, you may simply set the value of the `SCOUT_DRIVER` environment variable to `collection`, or specify the `collection` driver directly in your application's `scout` configuration file: @@ -364,7 +364,7 @@ To use the collection engine, you may simply set the value of the `SCOUT_DRIVER` SCOUT_DRIVER=collection ``` -Once you have specified the collection driver as your preferred driver, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia or MeiliSearch indexes, is unnecessary when using the collection engine. +Once you have specified the collection driver as your preferred driver, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia or Meilisearch indexes, is unnecessary when using the collection engine. #### Differences From Database Engine @@ -564,7 +564,7 @@ You may use the `whereIn` method to constrain results against a given set of val Since a search index is not a relational database, more advanced "where" clauses are not currently supported. > **Warning** -> If your application is using MeiliSearch, you must configure your application's [filterable attributes](#configuring-filterable-data-for-meilisearch) before utilizing Scout's "where" clauses. +> If your application is using Meilisearch, you must configure your application's [filterable attributes](#configuring-filterable-data-for-meilisearch) before utilizing Scout's "where" clauses. ### Pagination From 325cb42da6adb6e68ccf16f13bf7342da639004d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 3 Mar 2023 10:08:35 -0600 Subject: [PATCH 0812/2609] wip --- notifications.md | 1 + 1 file changed, 1 insertion(+) diff --git a/notifications.md b/notifications.md index a11efbcc7aa..ec8103fed65 100644 --- a/notifications.md +++ b/notifications.md @@ -296,6 +296,7 @@ However, if you would like to make the final determination on whether the queued 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: use Illuminate\Broadcasting\Channel; + use Illuminate\Support\Facades\Notification; Notification::route('mail', 'taylor@example.com') ->route('vonage', '5555555555') From 56c949a6742bd2b6905801d0119014b69e1f82b9 Mon Sep 17 00:00:00 2001 From: dciprian-petrisor <35119124+dciprian-petrisor@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:07:03 +0200 Subject: [PATCH 0813/2609] Document primary keys as castables not being supported (#8631) * Document primary keys as castables not being supported * Update eloquent-mutators.md * Update eloquent-mutators.md --------- Co-authored-by: Taylor Otwell --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 693ef77af42..43f2cd70cc4 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -261,7 +261,7 @@ If you need to add a new, temporary cast at runtime, you may use the `mergeCasts ]); > **Warning** -> Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship. +> Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship or assign a cast to the model's primary key. #### Stringable Casting From 54c1bd342f5a5c530e341370ab2a0240b0e2bc46 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Mar 2023 09:10:22 -0600 Subject: [PATCH 0814/2609] wip --- broadcasting.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/broadcasting.md b/broadcasting.md index ac9eb2abe3d..4e5c90e3aba 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -597,6 +597,12 @@ The `channel` method accepts two arguments: the name of the channel and a callba All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the `{orderId}` placeholder to indicate that the "ID" portion of the channel name is a wildcard. +You may view a list of your application's broadcast authorization callbacks using the `channel:list` Artisan command: + +```shell +php artisan channel:list +``` + #### Authorization Callback Model Binding From d3b6ad58b0857d32f8594e8204d2c972914c3fff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Mar 2023 09:11:21 -0600 Subject: [PATCH 0815/2609] wip --- facades.md | 1 + 1 file changed, 1 insertion(+) diff --git a/facades.md b/facades.md index 37d0360ffb0..ef5510b34fa 100644 --- a/facades.md +++ b/facades.md @@ -274,6 +274,7 @@ Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/{{version}}/Illuminate Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/{{version}}/Illuminate/Notifications/ChannelManager.html) |   Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` +Pipeline (Instance) | [Illuminate\Pipeline\Pipeline](https://laravel.com/api/{{version}}/Illuminate/Pipeline/Pipeline.html) |   Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) |   From 85b902f08a1cf0cdc9d894ab67f567b8954c17d7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 7 Mar 2023 10:00:21 -0600 Subject: [PATCH 0816/2609] wip --- horizon.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/horizon.md b/horizon.md index 136bd3e205d..05b2a34327f 100644 --- a/horizon.md +++ b/horizon.md @@ -108,6 +108,7 @@ When using the `auto` strategy, you may define the `minProcesses` and `maxProces 'connection' => 'redis', 'queue' => ['default'], 'balance' => 'auto', + 'autoScalingStrategy' => 'time', 'minProcesses' => 1, 'maxProcesses' => 10, 'balanceMaxShift' => 1, @@ -117,6 +118,8 @@ When using the `auto` strategy, you may define the `minProcesses` and `maxProces ], ], +The `autoScalingStrategy` configuration value determines if Horizon will assign more worker processes to queues based on the total amount of time it will take to clear the queue (`time` strategy) or by the total number of jobs on the queue (`size` strategy). + The `balanceMaxShift` and `balanceCooldown` configuration values determine how quickly Horizon will scale to meet worker demand. In the example above, a maximum of one new process will be created or destroyed every three seconds. You are free to tweak these values as necessary based on your application's needs. When the `balance` option is set to `false`, the default Laravel behavior will be used, wherein queues are processed in the order they are listed in your configuration. From bf58626261ff1e13b844608b55a3b08b771c16fc Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 8 Mar 2023 15:13:51 +0000 Subject: [PATCH 0817/2609] [10.x] Added warning for valet use, pushing usage of valet isolate instead (#8636) * Added warning for valet use, pushing usage of valet use instead * Update valet.md --------- Co-authored-by: Taylor Otwell --- valet.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/valet.md b/valet.md index 8062e4de200..abedb60aa7b 100644 --- a/valet.md +++ b/valet.md @@ -102,6 +102,9 @@ Valet will automatically start its required services each time your machine boot #### PHP Versions +> **Note** +> Instead of modifying your global PHP version, you can instruct Valet to use per-site PHP versions via the `isolate` [command](#per-site-php-versions). + 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: ```shell From cb7be81c2f0a09dce9ea76e008da4ba592b018ed Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 8 Mar 2023 11:01:32 -0600 Subject: [PATCH 0818/2609] document processors --- logging.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/logging.md b/logging.md index 816962d8289..dd9f85d325a 100644 --- a/logging.md +++ b/logging.md @@ -384,6 +384,33 @@ If you are using a Monolog handler that is capable of providing its own formatte 'formatter' => 'default', ], + + + #### Monolog Processors + + Monolog can also process messages before logging them. You can create your own processors or use the [existing processors offered by Monolog](https://github.com/Seldaek/monolog/tree/main/src/Monolog/Processor). + + If you would like to customize the processors for a `monolog` driver, add a `processors` configuration value to your channel's configuration: + + 'memory' => [ + 'driver' => 'monolog', + 'handler' => Monolog\Handler\StreamHandler::class, + 'with' => [ + 'stream' => 'php://stderr', + ], + 'processors' => [ + // Simple syntax... + Monolog\Processor\MemoryUsageProcessor::class, + + // With options... + [ + 'processor' => Monolog\Processor\PsrLogMessageProcessor::class, + 'with' => ['removeUsedContextFields' => true], + ], + ], + ], + + ### Creating Custom Channels Via Factories From 22b07651208d338a86104b0c20dc7c2a24db48ba Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 8 Mar 2023 11:37:21 -0600 Subject: [PATCH 0819/2609] pipeline docs --- helpers.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/helpers.md b/helpers.md index 2bf28a59b47..d1fd22d4e07 100644 --- a/helpers.md +++ b/helpers.md @@ -4,6 +4,7 @@ - [Available Methods](#available-methods) - [Other Utilities](#other-utilities) - [Benchmarking](#benchmarking) + - [Pipeline](#pipeline) - [Lottery](#lottery) @@ -4164,6 +4165,48 @@ To invoke a callback more than once, you may specify the number of iterations th Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms + +### Pipeline + +Laravel's `Pipeline` facade provides a convenient way to "pipe" a given input through a series of invokable classes, closures, or callables, giving each class the opportunity to inspect or modify the input and invoke the next callable in the pipeline: + +```php +use Closure; +use App\Models\User; +use Illuminate\Support\Facades\Pipeline; + +$user = Pipeline::send($user) + ->through([ + function (User $user, Closure $next) { + // ... + + return $next($user); + }, + function (User $user, Closure $next) { + // ... + + return $next($user); + }, + ]) + ->then(fn (User $user) => $user); +``` + +As you can see, each invokable class or closure in the pipeline is provided the input and a `$next` closure. Invoking the `$next` closure will invoke the next callable in the pipeline. As you may have noticed, this is very similar to [middleware](/docs/{{version}}/middleware). + +When the last callable in the pipeline invokes the `$next` closure, the callable provided to the `then` method will be invoked. Typically, this callable will simply return the given input. + +Of course, as discussed previously, you are not limited to providing closures to your pipeline. You may also provide invokable classes. If a class name if provided, the class will be instantiated via Laravel's [service container](/docs/{{version}}/container), allowing dependencies to be injected into the invokable class: + +```php +$user = Pipeline::send($user) + ->through([ + GenerateProfilePhoto::class, + ActivateSubscription::class, + SendWelcomeEmail::class, + ]) + ->then(fn (User $user) => $user); +``` + ### Lottery From d2349f9e56de223697ff45316927a360eac221b9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 8 Mar 2023 11:52:56 -0600 Subject: [PATCH 0820/2609] wip --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index d1fd22d4e07..73ba080ad29 100644 --- a/helpers.md +++ b/helpers.md @@ -4195,7 +4195,7 @@ As you can see, each invokable class or closure in the pipeline is provided the When the last callable in the pipeline invokes the `$next` closure, the callable provided to the `then` method will be invoked. Typically, this callable will simply return the given input. -Of course, as discussed previously, you are not limited to providing closures to your pipeline. You may also provide invokable classes. If a class name if provided, the class will be instantiated via Laravel's [service container](/docs/{{version}}/container), allowing dependencies to be injected into the invokable class: +Of course, as discussed previously, you are not limited to providing closures to your pipeline. You may also provide invokable classes. If a class name is provided, the class will be instantiated via Laravel's [service container](/docs/{{version}}/container), allowing dependencies to be injected into the invokable class: ```php $user = Pipeline::send($user) From aa71a9c2a9f14c58e8088214ed0bffde4e0ff9dd Mon Sep 17 00:00:00 2001 From: Sevan Nerse Date: Thu, 9 Mar 2023 17:29:18 +0300 Subject: [PATCH 0821/2609] [10.x] Add `dot` method to collections (#8639) * [10.x] Add dot method to collections * formatting' --------- Co-authored-by: Taylor Otwell --- collections.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/collections.md b/collections.md index 0919e826bef..e577ffdb353 100644 --- a/collections.md +++ b/collections.md @@ -114,6 +114,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [diffAssoc](#method-diffassoc) [diffKeys](#method-diffkeys) [doesntContain](#method-doesntcontain) +[dot](#method-dot) [dump](#method-dump) [duplicates](#method-duplicates) [duplicatesStrict](#method-duplicatesstrict) @@ -661,6 +662,19 @@ You may also pass a key / value pair to the `doesntContain` method, which will d The `doesntContain` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. + +#### `dot()` {.collection-method} + +The `dot` method flattens a multi-dimensional collection into a single level collection that uses "dot" notation to indicate depth: + + $collection = collect(['products' => ['desk' => ['price' => 100]]]); + + $flattened = $collection->dot(); + + $flattened->all(); + + // ['products.desk.price' => 100] + #### `dump()` {.collection-method} From 09fad5f893a69759ee27ef09d8f22275d65798ce Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Mar 2023 16:41:26 -0600 Subject: [PATCH 0822/2609] wip --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index e3572024c1f..6c0377a7741 100644 --- a/upgrade.md +++ b/upgrade.md @@ -94,7 +94,7 @@ Finally, examine any other third-party packages consumed by your application and #### Minimum Stability -You should update the `minimum-stability` setting in your application's `composer.json` file to `stable`: +You should update the `minimum-stability` setting in your application's `composer.json` file to `stable`. Or, since the default value of `minimum-stability` is `stable`, you may delete this setting from your application's `composer.json` file: ```json "minimum-stability": "stable", From b440d7ae0ac81f9d3a48b8a88255cbc4103abf09 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 9 Mar 2023 20:28:32 -0600 Subject: [PATCH 0823/2609] [10.x] import correct Builder namespace (#8641) * import correct Builder namespace when constraining the eager loaded relationship, the class actually passed into the closure is an instance of the actual relationship class, which implements `Illuminate\Contracts\Database\Eloquent\Builder`, and **not** `Illuminate\Database\Eloquent\Builder`. it would be very easy for a user to be confused, since in most of these other examples an `Illuminate\Database\Eloquent\Builder` is actually passed to the closure. * Update eloquent-relationships.md --------- Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index c844a76b657..883da30f7a2 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1704,6 +1704,7 @@ If you would like to override all items within the `$with` property for a single Sometimes you may wish to eager load a relationship but also specify additional query conditions for the eager loading query. You can accomplish this by passing an array of relationships to the `with` method where the array key is a relationship name and the array value is a closure that adds additional constraints to the eager loading query: use App\Models\User; + use Illuminate\Contracts\Database\Eloquent\Builder; $users = User::with(['posts' => function (Builder $query) { $query->where('title', 'like', '%code%'); From 3037dcd68b6bb085c656f583b411c0e1b1f7b60c Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 10 Mar 2023 17:33:02 +0330 Subject: [PATCH 0824/2609] Update contracts.md (#8642) --- contracts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts.md b/contracts.md index 39052057333..4c4f27ff8f8 100644 --- a/contracts.md +++ b/contracts.md @@ -119,7 +119,7 @@ This table provides a quick reference to all of the Laravel contracts and their | [Illuminate\Contracts\Pagination\LengthAwarePaginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/LengthAwarePaginator.php) |   | | [Illuminate\Contracts\Pagination\Paginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/Paginator.php) |   | | [Illuminate\Contracts\Pipeline\Hub](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Hub.php) |   | -| [Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) |   | +| [Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) | `Pipeline`; | | [Illuminate\Contracts\Queue\EntityResolver](https://github.com/illuminate/contracts/blob/{{version}}/Queue/EntityResolver.php) |   | | [Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Factory.php) | `Queue` | | [Illuminate\Contracts\Queue\Job](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Job.php) |   | From de6f4f38a0ee1c3f40c9405e56fd54d60c7b29fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honor=C3=A9=20Hounwanou?= Date: Sun, 12 Mar 2023 10:16:34 -0400 Subject: [PATCH 0825/2609] Fix small typo (#8645) --- middleware.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware.md b/middleware.md index d8c9090f2b8..13e6cff8a36 100644 --- a/middleware.md +++ b/middleware.md @@ -147,7 +147,7 @@ For convenience, you may assign aliases to middleware in your application's `app 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ]; -Once the middleware alias has been defined in the HTTP kernel, you may use the alias when assigning middlware to routes: +Once the middleware alias has been defined in the HTTP kernel, you may use the alias when assigning middleware to routes: Route::get('/profile', function () { // ... From 48289039e17b95137f92ed25c05a8413eeaec07d Mon Sep 17 00:00:00 2001 From: Majid Date: Mon, 13 Mar 2023 18:38:00 +0300 Subject: [PATCH 0826/2609] Fix pagination links for resource collections (#8646) --- eloquent-resources.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index eb158fe4bc8..1893788d94a 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -428,8 +428,8 @@ When returning paginated collections via a resource response, Laravel will wrap } ], "links":{ - "first": "http://example.com/pagination?page=1", - "last": "http://example.com/pagination?page=1", + "first": "http://example.com/users?page=1", + "last": "http://example.com/users?page=1", "prev": null, "next": null }, @@ -437,7 +437,7 @@ When returning paginated collections via a resource response, Laravel will wrap "current_page": 1, "from": 1, "last_page": 1, - "path": "http://example.com/pagination", + "path": "http://example.com/users", "per_page": 15, "to": 10, "total": 10 @@ -474,8 +474,8 @@ Paginated responses always contain `meta` and `links` keys with information abou } ], "links":{ - "first": "http://example.com/pagination?page=1", - "last": "http://example.com/pagination?page=1", + "first": "http://example.com/users?page=1", + "last": "http://example.com/users?page=1", "prev": null, "next": null }, @@ -483,7 +483,7 @@ Paginated responses always contain `meta` and `links` keys with information abou "current_page": 1, "from": 1, "last_page": 1, - "path": "http://example.com/pagination", + "path": "http://example.com/users", "per_page": 15, "to": 10, "total": 10 From 48dccbacdb3366834c404d2a128a732b499f053c Mon Sep 17 00:00:00 2001 From: jonhassall Date: Mon, 13 Mar 2023 18:05:30 +0000 Subject: [PATCH 0827/2609] Add missing Class import --- validation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/validation.md b/validation.md index d25553d3a79..ee339cbc326 100644 --- a/validation.md +++ b/validation.md @@ -2210,6 +2210,7 @@ Or, if your validation rule requires access to the validator instance performing 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; + use Closure; $validator = Validator::make($request->all(), [ 'title' => [ From 240cb6b068277cc737ba2773b0af812fea164569 Mon Sep 17 00:00:00 2001 From: Jamie Whelpton Date: Tue, 14 Mar 2023 13:12:53 +0000 Subject: [PATCH 0828/2609] Declare `$carry` as nullable (#8650) Co-authored-by: JamieW94 --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index e577ffdb353..650aa313a0b 100644 --- a/collections.md +++ b/collections.md @@ -1928,7 +1928,7 @@ The `reduce` method reduces the collection to a single value, passing the result $collection = collect([1, 2, 3]); - $total = $collection->reduce(function (int $carry, int $item) { + $total = $collection->reduce(function (?int $carry, int $item) { return $carry + $item; }); From 4c10ce123ab7c1bc5f8df699899835793ddfecd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Frankovi=C4=8D?= <50501921+frankykubo@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:16:18 +0100 Subject: [PATCH 0829/2609] [10.x] - Add laravel/passport to upgrade guide (#8649) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add passport to upgrade.md * add description * fix wrong service provider name * Update upgrade.md --------- Co-authored-by: Jakub Frankovič Co-authored-by: Taylor Otwell --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 6c0377a7741..0d08361122b 100644 --- a/upgrade.md +++ b/upgrade.md @@ -75,6 +75,7 @@ You should update the following dependencies in your application's `composer.jso - `laravel/sanctum` to `^3.2` - `doctrine/dbal` to `^3.0` - `spatie/laravel-ignition` to `^2.0` +- `laravel/passport` to `^11.0` ([Upgrade Guide](https://github.com/laravel/passport/blob/11.x/UPGRADE.md))
    From 04b365dd8c4a652403544eded75db46f31606300 Mon Sep 17 00:00:00 2001 From: Silvio Ney Date: Wed, 15 Mar 2023 15:48:02 +0000 Subject: [PATCH 0830/2609] fixed typo (#8655) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 73c5582ebde..5859975e437 100644 --- a/queues.md +++ b/queues.md @@ -381,7 +381,7 @@ Instead of rate limiting in the handle method, we could define a job middleware { Redis::throttle('key') ->block(0)->allow(1)->every(5) - ->then(function () use (object $job, Closure $next) { + ->then(function () use ($job, $next) { // Lock obtained... $next($job); From f866514414f6ce42dd86ea9ecdf997cc887d1aff Mon Sep 17 00:00:00 2001 From: Matt Stauffer Date: Wed, 15 Mar 2023 13:31:15 -0400 Subject: [PATCH 0831/2609] Update Valet docs for v4 (#8652) * Update Valet docs for v4 * Update share tool wording * formatting --------- Co-authored-by: Taylor Otwell --- valet.md | 97 +++++++++++++++++++++++++++----------------------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/valet.md b/valet.md index abedb60aa7b..90229edd9cb 100644 --- a/valet.md +++ b/valet.md @@ -10,8 +10,6 @@ - [Serving a Default Site](#serving-a-default-site) - [Per-Site PHP Versions](#per-site-php-versions) - [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) @@ -113,10 +111,10 @@ valet use php@8.1 valet use php ``` -You may also create a `.valetphprc` file in the root of your project. The `.valetphprc` file should contain the PHP version the site should use: +You may also create a `.valetrc` file in the root of your project. The `.valetrc` file should contain the PHP version the site should use: ```shell -php@8.1 +php=php@8.1 ``` Once this file has been created, you may simply execute the `valet use` command and the command will determine the site's preferred PHP version by reading the file. @@ -139,6 +137,19 @@ If you are having trouble getting your Valet installation to run properly, execu You may update your Valet installation by executing the `composer global require laravel/valet` 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. + +#### Upgrading To Valet 4 + +If you're upgrading from Valet 3 to Valet 4, take the following steps to properly upgrade your Valet installation: + +
    + +- Replace all `.valetphprc` files with `.valetrc` files, and prepend `php=` to the existing content of your `.valetphprc` files when you rename them. +- Update any custom drivers to match the namespace, extension, type-hints, and return type-hints of the new driver system. You may consult Valet's [SampleValetDriver](https://github.com/laravel/valet/blob/d7787c025e60abc24a5195dc7d4c5c6f2d984339/cli/stubs/SampleValetDriver.php) as an example. +- If you use PHP 7.1 - 7.4 to serve your sites, make sure you still use Homebrew to install a version of PHP that's 8.0 or higher, as Valet will use this version, even if it's not your primary linked version, to run some of its scripts. + +
    + ## Serving Sites @@ -260,12 +271,17 @@ valet unisolate ## 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. +Valet 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 +Out of the box, Valet supports sharing your sites via ngrok or Expose. Before sharing a site, you should update your Valet configuration using the `share-tool` command, specifying either `ngrok` or `expose`: -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: +```shell +valet share-tool ngrok +``` + +If you choose a tool and don't have it installed via Homebrew (for ngrok) or Composer (for Expose), Valet will automatically prompt you to install it. Of course, both tools require you to authenticate your ngrok or Expose account before you can start sharing sites. + +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 placed into your clipboard and is ready to paste directly into your browser or to be shared with your team: ```shell cd ~/Sites/laravel @@ -273,23 +289,29 @@ cd ~/Sites/laravel valet share ``` -To stop sharing your site, you may press `Control + C`. Sharing your site using Ngrok requires you to [create an Ngrok account](https://dashboard.ngrok.com/signup) and [setup an authentication token](https://dashboard.ngrok.com/get-started/your-authtoken). +To stop sharing your site, you may press `Control + C`. -> **Note** -> 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). +> **Warning** +> If you're using a custom DNS server (like `1.1.1.1`), ngrok sharing may not work correctly. If this is the case on your machine, open your Mac's system settings, go to the Network settings, open the Advanced settings, then go the DNS tab and add `127.0.0.1` as your first DNS server. - -### Sharing Sites Via Expose + +#### Sharing Sites Via Ngrok -If you have [Expose](https://expose.dev) 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://expose.dev/docs) 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: +Sharing your site using ngrok requires you to [create an ngrok account](https://dashboard.ngrok.com/signup) and [set up an authentication token](https://dashboard.ngrok.com/get-started/your-authtoken). Once you have an authentication token, you can update your Valet configuration with that token: ```shell -cd ~/Sites/laravel - -expose +valet set-ngrok-token YOUR_TOKEN_HERE ``` -To stop sharing your site, you may press `Control + C`. +> **Note** +> 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 + +Sharing your site using Expose requires you to [create an Expose account](https://expose.dev/register) and [authenticate with Expose via your authentication token](https://expose.dev/docs/getting-started/getting-your-token). + +You may consult the [Expose documentation](https://expose.dev/docs) for information regarding the additional command-line parameters it supports. ### Sharing Sites On Your Local Network @@ -368,13 +390,8 @@ For example, let's imagine we are writing a `WordPressValetDriver`. Our `serves` /** * Determine if the driver serves the request. - * - * @param string $sitePath - * @param string $siteName - * @param string $uri - * @return bool */ - public function serves($sitePath, $siteName, $uri) + public function serves(string $sitePath, string $siteName, string $uri): bool { return is_dir($sitePath.'/wp-admin'); } @@ -387,12 +404,9 @@ The `isStaticFile` should determine if the incoming request is for a file that i /** * Determine if the incoming request is for a static file. * - * @param string $sitePath - * @param string $siteName - * @param string $uri * @return string|false */ - public function isStaticFile($sitePath, $siteName, $uri) + public function isStaticFile(string $sitePath, string $siteName, string $uri) { if (file_exists($staticFilePath = $sitePath.'/public/'.$uri)) { return $staticFilePath; @@ -411,13 +425,8 @@ The `frontControllerPath` method should return the fully qualified path to your /** * Get the fully resolved path to the application's front controller. - * - * @param string $sitePath - * @param string $siteName - * @param string $uri - * @return string */ - public function frontControllerPath($sitePath, $siteName, $uri) + public function frontControllerPath(string $sitePath, string $siteName, string $uri): string { return $sitePath.'/public/index.php'; } @@ -433,26 +442,16 @@ If you would like to define a custom Valet driver for a single application, crea { /** * Determine if the driver serves the request. - * - * @param string $sitePath - * @param string $siteName - * @param string $uri - * @return bool */ - public function serves($sitePath, $siteName, $uri) + public function serves(string $sitePath, string $siteName, string $uri): bool { return true; } /** * Get the fully resolved path to the application's front controller. - * - * @param string $sitePath - * @param string $siteName - * @param string $uri - * @return string */ - public function frontControllerPath($sitePath, $siteName, $uri) + public function frontControllerPath(string $sitePath, string $siteName, string $uri): string { return $sitePath.'/public_html/index.php'; } @@ -466,6 +465,8 @@ If you would like to define a custom Valet driver for a single application, crea Command | Description ------------- | ------------- `valet list` | Display a list of all Valet commands. +`valet diagnose` | Output diagnostics to aid in debugging Valet. +`valet directory-listing` | Determine directory-listing behavior. Default is "off", which renders a 404 page for directories. `valet forget` | Run this command from a "parked" directory to remove it from the parked directory list. `valet log` | View a list of logs which are written by Valet's services. `valet paths` | View all of your "parked" paths. @@ -494,10 +495,6 @@ This directory contains DNSMasq's configuration. 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` and `secure` commands. From 5a3e7fa843c624c3f3ba0e60dac85f45c4c97a93 Mon Sep 17 00:00:00 2001 From: Matt Stauffer Date: Wed, 15 Mar 2023 14:11:02 -0400 Subject: [PATCH 0832/2609] Clarify .valetphprc's purpose in the Valet 4 upgrade guide (#8656) --- valet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valet.md b/valet.md index 90229edd9cb..5bdae7b0d1d 100644 --- a/valet.md +++ b/valet.md @@ -144,7 +144,7 @@ If you're upgrading from Valet 3 to Valet 4, take the following steps to properl
    -- Replace all `.valetphprc` files with `.valetrc` files, and prepend `php=` to the existing content of your `.valetphprc` files when you rename them. +- If you've added `.valetphprc` files to customize your site's PHP version, replace each `.valetphprc` file with a `.valetrc` file, and prepend `php=` to the existing content of your `.valetphprc` file when you rename it. - Update any custom drivers to match the namespace, extension, type-hints, and return type-hints of the new driver system. You may consult Valet's [SampleValetDriver](https://github.com/laravel/valet/blob/d7787c025e60abc24a5195dc7d4c5c6f2d984339/cli/stubs/SampleValetDriver.php) as an example. - If you use PHP 7.1 - 7.4 to serve your sites, make sure you still use Homebrew to install a version of PHP that's 8.0 or higher, as Valet will use this version, even if it's not your primary linked version, to run some of its scripts. From 84a1c97a52f8ba4cf89df0e162d3a4f87075d4b4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 15 Mar 2023 13:12:11 -0500 Subject: [PATCH 0833/2609] wip --- valet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valet.md b/valet.md index 5bdae7b0d1d..dceb1e75481 100644 --- a/valet.md +++ b/valet.md @@ -144,7 +144,7 @@ If you're upgrading from Valet 3 to Valet 4, take the following steps to properl
    -- If you've added `.valetphprc` files to customize your site's PHP version, replace each `.valetphprc` file with a `.valetrc` file, and prepend `php=` to the existing content of your `.valetphprc` file when you rename it. +- If you've added `.valetphprc` files to customize your site's PHP version, rename each `.valetphprc` file to `.valetrc`. Then, prepend `php=` to the existing content of the `.valetrc` file. - Update any custom drivers to match the namespace, extension, type-hints, and return type-hints of the new driver system. You may consult Valet's [SampleValetDriver](https://github.com/laravel/valet/blob/d7787c025e60abc24a5195dc7d4c5c6f2d984339/cli/stubs/SampleValetDriver.php) as an example. - If you use PHP 7.1 - 7.4 to serve your sites, make sure you still use Homebrew to install a version of PHP that's 8.0 or higher, as Valet will use this version, even if it's not your primary linked version, to run some of its scripts. From eda7e39035eec57f938bb47102557a7e845d5922 Mon Sep 17 00:00:00 2001 From: Andrew Minion Date: Wed, 15 Mar 2023 16:44:50 -0500 Subject: [PATCH 0834/2609] fix input parameter type (#8657) --- helpers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index 3d33533066f..add6aebf236 100644 --- a/helpers.md +++ b/helpers.md @@ -2911,8 +2911,8 @@ The `replaceMatches` method also accepts a closure that will be invoked with eac use Illuminate\Support\Str; use Illuminate\Support\Stringable; - $replaced = Str::of('123')->replaceMatches('/\d/', function (Stringable $match) { - return '['.$match[0].']'; + $replaced = Str::of('123')->replaceMatches('/\d/', function (array $matches) { + return '['.$matches[0].']'; }); // '[1][2][3]' From 77807cb4c9c049626ccd3ca7d936fe57503198a8 Mon Sep 17 00:00:00 2001 From: Mubashir Abbas Date: Thu, 16 Mar 2023 07:59:27 +0500 Subject: [PATCH 0835/2609] a small apostophe typo fix (#8658) --- container.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container.md b/container.md index b519f92a635..e48dafd15cf 100644 --- a/container.md +++ b/container.md @@ -337,7 +337,7 @@ You may use the `make` method to resolve a class instance from the container. Th $transistor = $this->app->make(Transistor::class); -If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the `makeWith` method. For example, we may manually pass the `$id` constructor argument required by the `Transistor` service: +If some of your class's dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the `makeWith` method. For example, we may manually pass the `$id` constructor argument required by the `Transistor` service: use App\Services\Transistor; @@ -352,7 +352,7 @@ If you are outside of a service provider in a location of your code that does no $transistor = app(Transistor::class); -If you would like to have the Laravel container instance itself injected into a class that is being resolved by the container, you may type-hint the `Illuminate\Container\Container` class on your class' constructor: +If you would like to have the Laravel container instance itself injected into a class that is being resolved by the container, you may type-hint the `Illuminate\Container\Container` class on your class's constructor: use Illuminate\Container\Container; From 7bacf842b9372b343a2a3c18dbc2b6d1ea49f61c Mon Sep 17 00:00:00 2001 From: praxxys <65581743+praxxys@users.noreply.github.com> Date: Thu, 16 Mar 2023 11:40:27 +0800 Subject: [PATCH 0836/2609] [10.x] Fix Missing Config Params on SFTP Driver (#8659) * [10.x] Fix Missing Config Params on SFTP Driver This will save people a few hours of searching as setting file and directory permissions for the SFTP driver is not well documented. This is particularly useful for people upgrading from SFTP v2 as config params have changed in SFTP v3. Hope this helps. * Update filesystem.md * Update filesystem.md Clarify defaults * Update filesystem.md --------- Co-authored-by: Taylor Otwell --- filesystem.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filesystem.md b/filesystem.md index 95f4a12fb53..d7a71f38526 100644 --- a/filesystem.md +++ b/filesystem.md @@ -135,6 +135,10 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu 'privateKey' => env('SFTP_PRIVATE_KEY'), 'passphrase' => env('SFTP_PASSPHRASE'), + // Settings for file / directory permissions... + 'visibility' => 'private', // `private` = 0600, `public` = 0700 + 'directory_visibility' => 'private', // `private` = 0700, `public` = 0755 + // Optional SFTP Settings... // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'), // 'maxTries' => 4, From c1f6cba87033fa3167af0ccde7eda71280492648 Mon Sep 17 00:00:00 2001 From: Mubashir Abbas Date: Fri, 17 Mar 2023 07:07:44 +0500 Subject: [PATCH 0837/2609] it should be routes' not route's. (#8663) * it should be routes' not route's. * Update routing.md --------- Co-authored-by: Taylor Otwell --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 68a9e2c58e9..7f4d27c5430 100644 --- a/routing.md +++ b/routing.md @@ -415,7 +415,7 @@ The `prefix` method may be used to prefix each route in the group with a given U ### Route Name Prefixes -The `name` method may be used to prefix each route name in the group with a given string. For example, you may want to prefix all of the grouped route's names with `admin`. The given string is prefixed to the route name exactly as it is specified, so we will be sure to provide the trailing `.` character in the prefix: +The `name` method may be used to prefix each route name in the group with a given string. For example, you may want to prefix the names of all of the routes in the group with `admin`. The given string is prefixed to the route name exactly as it is specified, so we will be sure to provide the trailing `.` character in the prefix: Route::name('admin.')->group(function () { Route::get('/users', function () { From 3905582c0777e930eeae6309b92374d217d98863 Mon Sep 17 00:00:00 2001 From: Mubashir Abbas Date: Fri, 17 Mar 2023 07:09:41 +0500 Subject: [PATCH 0838/2609] another missing apostophe added. (#8664) --- requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests.md b/requests.md index 60a356c5c99..4f19393dbbf 100644 --- a/requests.md +++ b/requests.md @@ -242,7 +242,7 @@ Using the `collect` method, you may retrieve all of the incoming request's input $input = $request->collect(); -The `collect` method also allows you to retrieve a subset of the incoming request input as a collection: +The `collect` method also allows you to retrieve a subset of the incoming request's input as a collection: $request->collect('users')->each(function (string $user) { // ... From 210aaf8427c6a2c0fee60b40b5ddad1302f61430 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 17 Mar 2023 13:37:36 +0000 Subject: [PATCH 0839/2609] Fixes fortify custom response (#8669) --- fortify.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fortify.md b/fortify.md index 95dd50b5b83..0943493f0d1 100644 --- a/fortify.md +++ b/fortify.md @@ -222,8 +222,6 @@ If you need advanced customization of this behavior, you may bind implementation ```php use Laravel\Fortify\Contracts\LogoutResponse; -use Illuminate\Http\RedirectResponse; -use Illuminate\Http\Request; /** * Register any application services. @@ -231,7 +229,7 @@ use Illuminate\Http\Request; public function register(): void { $this->app->instance(LogoutResponse::class, new class implements LogoutResponse { - public function toResponse(Request $request): RedirectResponse + public function toResponse($request) { return redirect('/'); } From d67e68432f8e5ba395e154d85bd713d54d417130 Mon Sep 17 00:00:00 2001 From: Mubashir Abbas Date: Fri, 17 Mar 2023 18:37:54 +0500 Subject: [PATCH 0840/2609] changed options .. 'is' housed in to (#8668) options ... 'are' housed in --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index dd9f85d325a..bf544e3fe6b 100644 --- a/logging.md +++ b/logging.md @@ -26,7 +26,7 @@ Under the hood, Laravel utilizes the [Monolog](https://github.com/Seldaek/monolo ## Configuration -All of the configuration options for your application's logging behavior is housed in the `config/logging.php` configuration file. This file allows you to configure your application's log channels, so be sure to review each of the available channels and their options. We'll review a few common options below. +All of the configuration options for your application's logging behavior are housed in the `config/logging.php` configuration file. This file allows you to configure your application's log channels, so be sure to review each of the available channels and their options. We'll review a few common options below. By default, Laravel will use the `stack` channel when logging messages. The `stack` channel is used to aggregate multiple log channels into a single channel. For more information on building stacks, check out the [documentation below](#building-log-stacks). From 855cc8902af9a6dbc0845c167462b773886315bb Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 17 Mar 2023 08:38:54 -0500 Subject: [PATCH 0841/2609] minor grammar (#8666) --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 59816acdda3..971a9c76fe3 100644 --- a/mail.md +++ b/mail.md @@ -975,7 +975,7 @@ As you might expect, the "HTML" assertions assert that the HTML version of your ### Testing Mailable Sending -We suggest testing the content of your mailables separately from your tests that assert that a given mailable was "sent" to a specific user. Typically, the content of mailables it not relevant to the code you are testing, and it is sufficient to simply assert that Laravel was instructed to send a given mailable. +We suggest testing the content of your mailables separately from your tests that assert that a given mailable was "sent" to a specific user. Typically, the content of mailables is not relevant to the code you are testing, and it is sufficient to simply assert that Laravel was instructed to send a given mailable. You may use the `Mail` facade's `fake` method to prevent mail from being sent. After calling the `Mail` facade's `fake` method, you may then assert that mailables were instructed to be sent to users and even inspect the data the mailables received: From 45a7ce1fac60a1019202021a6c3a0a31f04043bc Mon Sep 17 00:00:00 2001 From: Morten Scheel Date: Fri, 17 Mar 2023 14:47:36 +0100 Subject: [PATCH 0842/2609] Document how to customize pagination information for resources (#8670) * Document how to customize pagination information for resources * formatting --------- Co-authored-by: Taylor Otwell --- eloquent-resources.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/eloquent-resources.md b/eloquent-resources.md index 1893788d94a..13881e39295 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -491,6 +491,28 @@ Paginated responses always contain `meta` and `links` keys with information abou } ``` + +#### Customizing The Pagination Information + +If you would like to customize the information included in the `links` or `meta` keys of the pagination response, you may define a `paginationInformation` method on the resource. This method will receive the `$paginated` data and the array of `$default` information, which is an array containing the `links` and `meta` keys: + + /** + * Customize the pagination information for the resource. + * + * @param \Illuminate\Http\Request $request + * @param array $paginated + * @param array $default + * @return array + */ + public function paginationInformation($request, $paginated, $default) + { + $default['links']['custom'] = 'https://example.com/'; + + return $default; + } + +In this example, the response will no longer contain the `links` array. + ### Conditional Attributes From 616edf4f7dcc58e069a304ba49c654551bdcf460 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 18 Mar 2023 14:57:25 -0400 Subject: [PATCH 0843/2609] wip --- eloquent.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/eloquent.md b/eloquent.md index 7de9bfe6236..eef45d4eb98 100644 --- a/eloquent.md +++ b/eloquent.md @@ -383,21 +383,6 @@ Also, you may instruct Laravel to throw an exception when attempting to fill an Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction()); ``` -Finally, you may instruct Eloquent to throw an exception if you attempt to access an attribute on a model when that attribute was not actually retrieved from the database or when the attribute does not exist. For example, this may occur when you forget to add an attribute to the `select` clause of an Eloquent query: - -```php -Model::preventAccessingMissingAttributes(! $this->app->isProduction()); -``` - - -#### Enabling Eloquent "Strict Mode" - -For convenience, you may enable all three of the methods discussed above by simply invoking the `shouldBeStrict` method: - -```php -Model::shouldBeStrict(! $this->app->isProduction()); -``` - ## Retrieving Models From f3bdce69c3c53e09cdfc1bf3b2a237175e204d34 Mon Sep 17 00:00:00 2001 From: Antoni Siek Date: Mon, 20 Mar 2023 13:35:50 +0100 Subject: [PATCH 0844/2609] Update passport.md (#8672) --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index eff060e8288..dd9697bb0c0 100644 --- a/passport.md +++ b/passport.md @@ -260,7 +260,7 @@ Then, you may copy the routes defined by Passport in [its routes file](https://g Route::group([ 'as' => 'passport.', 'prefix' => config('passport.path', 'oauth'), - 'namespace' => 'Laravel\Passport\Http\Controllers', + 'namespace' => '\Laravel\Passport\Http\Controllers', ], function () { // Passport routes... }); From e588bf079a123f1155b92812d33a233d4a283682 Mon Sep 17 00:00:00 2001 From: TARMIZI SANUSI Date: Mon, 20 Mar 2023 20:36:37 +0800 Subject: [PATCH 0845/2609] Add exception message for InvalidArgumentException (#8673) * Add exception message for InvalidArgumentException Add exception message on `throw_unless` for `InvalidArgumentException`. This will guide developer to custom the message and make logs capture more readable. * Update passport.md --------- Co-authored-by: Taylor Otwell --- passport.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/passport.md b/passport.md index dd9697bb0c0..1f2483145c0 100644 --- a/passport.md +++ b/passport.md @@ -445,7 +445,8 @@ If the user approves the authorization request, they will be redirected back to throw_unless( strlen($state) > 0 && $state === $request->state, - InvalidArgumentException::class + InvalidArgumentException::class, + 'Invalid state value.' ); $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ From 521051f089341fee2982ea001303cd723753a61d Mon Sep 17 00:00:00 2001 From: Amadeusz Annissimo Date: Mon, 20 Mar 2023 12:45:54 +0000 Subject: [PATCH 0846/2609] Running parallel tests require brianium/paratest (#8671) * Running parallel tests require brianium/paratest * Update testing.md --------- Co-authored-by: Taylor Otwell --- testing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing.md b/testing.md index 7545c27c3f1..fabea712994 100644 --- a/testing.md +++ b/testing.md @@ -107,9 +107,11 @@ php artisan test --testsuite=Feature --stop-on-failure ### Running Tests In Parallel -By default, Laravel and PHPUnit execute your tests sequentially within a single process. However, you may greatly reduce the amount of time it takes to run your tests by running tests simultaneously across multiple processes. To get started, ensure your application depends on version `^5.3` or greater of the `nunomaduro/collision` package. Then, include the `--parallel` option when executing the `test` Artisan command: +By default, Laravel and PHPUnit execute your tests sequentially within a single process. However, you may greatly reduce the amount of time it takes to run your tests by running tests simultaneously across multiple processes. To get started, you should install the `brianium/paratest` Composer package as a "dev" dependency. Then, include the `--parallel` option when executing the `test` Artisan command: ```shell +composer require brianium/paratest --dev + php artisan test --parallel ``` From 88153c5a789eb47049f54e074668ba093d2ca621 Mon Sep 17 00:00:00 2001 From: Daniel Hartmann Date: Tue, 21 Mar 2023 12:48:20 +0100 Subject: [PATCH 0847/2609] Specify default timeout value for the HTTP Client (#8675) * add default timeout * Update http-client.md --------- Co-authored-by: Taylor Otwell --- http-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index be726bc2ed8..d7974ac5ed4 100644 --- a/http-client.md +++ b/http-client.md @@ -195,7 +195,7 @@ If you would like to quickly add a bearer token to the request's `Authorization` ### Timeout -The `timeout` method may be used to specify the maximum number of seconds to wait for a response: +The `timeout` method may be used to specify the maximum number of seconds to wait for a response. By default, the HTTP client will timeout after 30 seconds: $response = Http::timeout(3)->get(/* ... */); From f24c72f9cb6db985feffc06d6033a9eeb17422a5 Mon Sep 17 00:00:00 2001 From: Morten Scheel Date: Tue, 21 Mar 2023 12:57:42 +0100 Subject: [PATCH 0848/2609] Remove obsolete explanation (#8674) --- eloquent-resources.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index 13881e39295..e9efe67e293 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -511,8 +511,6 @@ If you would like to customize the information included in the `links` or `meta` return $default; } -In this example, the response will no longer contain the `links` array. - ### Conditional Attributes From 3f9d0813a00125a6c36d9c959d7f518ec9220371 Mon Sep 17 00:00:00 2001 From: Chris Sternal-Johnson Date: Wed, 22 Mar 2023 17:02:48 -0400 Subject: [PATCH 0849/2609] indicate postgres-specific handling for ipAddress (#8677) * indicate postgres-specific handling for ipAddress The Postgres grammar creates an `INET` column instead of a `VARCHAR`. https://github.com/laravel/framework/blob/be2ddb5c31b0b9ebc2738d9f37a9d4c960aa3199/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php#L943 * Update migrations.md --------- Co-authored-by: Taylor Otwell --- migrations.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migrations.md b/migrations.md index da5b09b1958..a9838e43c14 100644 --- a/migrations.md +++ b/migrations.md @@ -606,6 +606,8 @@ The `integer` method creates an `INTEGER` equivalent column: The `ipAddress` method creates a `VARCHAR` equivalent column: $table->ipAddress('visitor'); + +When using Postgres, an `INET` column will be created. #### `json()` {.collection-method} From 114e7225d38b3b6088940f6b009ed0c3039f0ef0 Mon Sep 17 00:00:00 2001 From: Luca Andrea Rossi Date: Thu, 23 Mar 2023 15:36:14 +0100 Subject: [PATCH 0850/2609] Removed note on old versions of MariaDB & MySQL (#8678) --- migrations.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/migrations.md b/migrations.md index da5b09b1958..128b41c6095 100644 --- a/migrations.md +++ b/migrations.md @@ -1146,23 +1146,6 @@ Command | Description `$table->fullText('body')->language('english');` | Adds a full text index of the specified language (PostgreSQL). `$table->spatialIndex('location');` | Adds a spatial index (except SQLite). - -#### Index Lengths & MySQL / MariaDB - -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; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Schema::defaultStringLength(191); - } - -Alternatively, you may enable the `innodb_large_prefix` option for your database. Refer to your database's documentation for instructions on how to properly enable this option. - ### Renaming Indexes From 2ad673e9329581dbcc288723ef0957ef1645cadd Mon Sep 17 00:00:00 2001 From: Mubashir Abbas Date: Mon, 27 Mar 2023 18:42:07 +0500 Subject: [PATCH 0851/2609] Fixed a typo by changing (#8683) "...helpers makes it a cinch" to "...helpers make it a cinch" --- events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.md b/events.md index 271cd7cb691..b328bcfa5c7 100644 --- a/events.md +++ b/events.md @@ -522,7 +522,7 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th OrderShipped::dispatchUnless($condition, $order); > **Note** -> When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) makes it a cinch. +> When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) make it a cinch. ## Event Subscribers From 471d0a27f8180d224e6e034680da53211931f447 Mon Sep 17 00:00:00 2001 From: Scott Zirkel Date: Mon, 27 Mar 2023 19:12:04 -0500 Subject: [PATCH 0852/2609] Cited useCurrentOnUpdate modifier to be MySQL specific. (#8685) --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index a9838e43c14..e2f0cfdce11 100644 --- a/migrations.md +++ b/migrations.md @@ -966,7 +966,7 @@ Modifier | Description `->storedAs($expression)` | Create a stored generated column (MySQL / PostgreSQL). `->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. +`->useCurrentOnUpdate()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated (MySQL). `->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). From ae82315232428e5002db0ec6033854989666f29c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 28 Mar 2023 14:25:39 -0400 Subject: [PATCH 0853/2609] wip --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index e2f0cfdce11..5bec5aabd5b 100644 --- a/migrations.md +++ b/migrations.md @@ -59,7 +59,7 @@ php artisan schema:dump php artisan schema:dump --prune ``` -When you execute this command, Laravel will write a "schema" file to your application's `database/schema` directory. The schema file's name will correspond to the database connection. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will execute first the SQL statements of the schema file of the database connection you are using. After executing the schema file's statements, Laravel will execute any remaining migrations that were not part of the schema dump. +When you execute this command, Laravel will write a "schema" file to your application's `database/schema` directory. The schema file's name will correspond to the database connection. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will first execute the SQL statements in the schema file of the database connection you are using. After executing the schema file's SQL statements, Laravel will execute any remaining migrations that were not part of the schema dump. If your application's tests use a different database connection than the one you typically use during local development, you should ensure you have dumped a schema file using that database connection so that your tests are able to build your database. You may wish to do this after dumping the database connection you typically use during local development: From 543dea06e81ac85a12dc903b819a9a0ea4c1eb5c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 28 Mar 2023 18:24:01 -0400 Subject: [PATCH 0854/2609] document one method --- eloquent-relationships.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 883da30f7a2..f8d77cf6974 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -351,6 +351,29 @@ public function largestOrder(): HasOne > **Warning** > Because PostgreSQL does not support executing the `MAX` function against UUID columns, it is not currently possible to use one-of-many relationships in combination with PostgreSQL UUID columns. + +#### Converting "Many" Relationships To Has One Relationships + +Often, when retrieving a single model using the `latestOfMany`, `oldestOfMany`, or `ofMany` methods, you already have a "has many" relationship defined for the same model. For convenience, Laravel allows you to easily convert this relationship into a "has one" relationship by invoking the `one` method on the relationship: + +```php +/** + * Get the user's orders. + */ +public function largestOrder(): HasMany +{ + return $this->hasMany(Order::class); +} + +/** + * Get the user's largest order. + */ +public function largestOrder(): HasOne +{ + return $this->orders()->one()->ofMany('price', 'max'); +} +``` + #### Advanced Has One Of Many Relationships From f015934589eeb15dde112dd03049eab999593fbd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 28 Mar 2023 18:32:37 -0400 Subject: [PATCH 0855/2609] wip --- filesystem.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filesystem.md b/filesystem.md index d7a71f38526..de0fb6f9aea 100644 --- a/filesystem.md +++ b/filesystem.md @@ -241,6 +241,10 @@ The `get` method may be used to retrieve the contents of a file. The raw string $contents = Storage::get('file.jpg'); +If the file you are retrieving contains JSON, you may use the `json` method to retrieve the file and decode its contents: + + $orders = Storage::json('orders.json'); + The `exists` method may be used to determine if a file exists on the disk: if (Storage::disk('s3')->exists('file.jpg')) { From 1ba6613f7b4a76a320a8027d6200eedc01038ce7 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Wed, 29 Mar 2023 11:29:43 +0100 Subject: [PATCH 0856/2609] fix typo --- eloquent-relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index f8d77cf6974..295779d45e7 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -360,7 +360,7 @@ Often, when retrieving a single model using the `latestOfMany`, `oldestOfMany`, /** * Get the user's orders. */ -public function largestOrder(): HasMany +public function orders(): HasMany { return $this->hasMany(Order::class); } From c4a05c15805c7cf759c7af81586cace93183cde3 Mon Sep 17 00:00:00 2001 From: John Ngondi <34208943+johnngondi@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:20:41 +0300 Subject: [PATCH 0857/2609] Updated queues doc (#8689) I fixed a typo --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 5859975e437..24b58687424 100644 --- a/queues.md +++ b/queues.md @@ -978,7 +978,7 @@ One approach to specifying the maximum number of times a job may be attempted is php artisan queue:work --tries=3 ``` -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). If `--tries=0` is provided to the `queue:work` command, the job will retried indefinitely. +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). If `--tries=0` is provided to the `queue:work` command, the job will be retried indefinitely. 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: From 570ee0cb2dadab9e84bf6557af44b436bbb3945c Mon Sep 17 00:00:00 2001 From: Hein Hanekom Date: Fri, 31 Mar 2023 14:56:41 +0200 Subject: [PATCH 0858/2609] Minor Clarification on Eloquent Factory Creation (#8693) * Add clarification for $model property setting on Eloquent Factory * Reword Note * Update eloquent-factories.md --------- Co-authored-by: Taylor Otwell --- eloquent-factories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-factories.md b/eloquent-factories.md index 39d6bd59b40..76b4ceed77f 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -87,7 +87,7 @@ The `HasFactory` trait's `factory` method will use conventions to determine the return FlightFactory::new(); } -Next, define a `model` property on the corresponding factory: +Then, define a `model` property on the corresponding factory: use App\Administration\Flight; use Illuminate\Database\Eloquent\Factories\Factory; From cc629e6143354adeef364c1a681bf7b39a9d7b52 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Sun, 2 Apr 2023 19:45:53 +0330 Subject: [PATCH 0859/2609] [10.x] Add original from in `mail` (#8694) * add correct from in `mail` * fix position of from * Update mail.md --------- Co-authored-by: Taylor Otwell --- mail.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 971a9c76fe3..4689088a9df 100644 --- a/mail.md +++ b/mail.md @@ -214,7 +214,10 @@ If you would like, you may also specify a `replyTo` address: However, if your application uses the same "from" address for all of its emails, it can become cumbersome to call the `from` method in each mailable class you generate. Instead, you may specify a global "from" address in your `config/mail.php` configuration file. This address will be used if no other "from" address is specified within the mailable class: - 'from' => ['address' => 'example@example.com', 'name' => 'App Name'], + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], In addition, you may define a global "reply_to" address within your `config/mail.php` configuration file: From cbe0be290c4bb929811b7524849b485570f3738c Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:07:18 +0330 Subject: [PATCH 0860/2609] [10.x] Add `assertUnsupportedMediaType` in http-tests (#8695) * add assertUnsupportedMediaType in http-tests * add assertUnsupportedMediaType section * fix description of `assertUnsupportedMediaType` --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 735b4776317..30b4173ba28 100644 --- a/http-tests.md +++ b/http-tests.md @@ -659,6 +659,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertSuccessful](#assert-successful) [assertUnauthorized](#assert-unauthorized) [assertUnprocessable](#assert-unprocessable) +[assertUnsupportedMediaType](#assert-unsupported-media-type) [assertValid](#assert-valid) [assertInvalid](#assert-invalid) [assertViewHas](#assert-view-has) @@ -1168,6 +1169,13 @@ Assert that the response has an unprocessable entity (422) HTTP status code: $response->assertUnprocessable(); + +#### assertUnsupportedMediaType + +Assert that the response has an unsupported media type (415) HTTP status code: + + $response->assertUnsupportedMediaType(); + #### assertValid From 2e75160e5b773522ac744e1cd64d77a413c6fb3e Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 3 Apr 2023 21:45:01 +0330 Subject: [PATCH 0861/2609] [10.x] Add `assertConflict` in http-tests (#8696) * add `assertConflict` in http-tests * add `assertConflict` section * fix http code for `assertConflict` * fix --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 30b4173ba28..2376534f72b 100644 --- a/http-tests.md +++ b/http-tests.md @@ -607,6 +607,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a
    +[assertConflict](#assert-conflict) [assertCookie](#assert-cookie) [assertCookieExpired](#assert-cookie-expired) [assertCookieNotExpired](#assert-cookie-not-expired) @@ -669,6 +670,13 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a
    + +#### assertConflict + +Assert that the response has a conflict (409) HTTP status code: + + $response->assertConflict(); + #### assertCookie From cac38a40a3321e205af63cba90f5f735a342a8e0 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:26:40 +0330 Subject: [PATCH 0862/2609] [10.x] add `assertAccepted` in http-tests (#8697) * add `assertAccepted` in http-tests * add `assertAccepted` section * add description for `assertAccepted` --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 2376534f72b..702cdcab647 100644 --- a/http-tests.md +++ b/http-tests.md @@ -607,6 +607,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a
    +[assertAccepted](#assert-accepted) [assertConflict](#assert-conflict) [assertCookie](#assert-cookie) [assertCookieExpired](#assert-cookie-expired) @@ -670,6 +671,13 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a
    + +#### assertAccepted + +Assert that the response has an accepted (202) HTTP status code: + + $response->assertAccepted(); + #### assertConflict From b6ae8eaf7b3f28e37416cf92c59c2237ba9320b9 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:30:27 +0330 Subject: [PATCH 0863/2609] [10.x] Add all missing methods in http-tests (#8698) * add `assertMovedPermanently` * add `assertMovedPermanently` section * fix description of `assertMovedPermanently` * add `assertFound` in http-tests * add `assertFound` section * add `assertBadRequest` in http-tests * add `assertBadRequest` section * add `assertPaymentRequired` in http-tests * add `assertPaymentRequired` section * add `assertRequestTimeout` in http-tests * add `assertRequestTimeout` section * add `assertTooManyRequests` in http-tests * add `assertTooManyRequests` section --- http-tests.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/http-tests.md b/http-tests.md index 702cdcab647..ee129def29d 100644 --- a/http-tests.md +++ b/http-tests.md @@ -608,6 +608,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a
    [assertAccepted](#assert-accepted) +[assertBadRequest](#assert-bad-request) [assertConflict](#assert-conflict) [assertCookie](#assert-cookie) [assertCookieExpired](#assert-cookie-expired) @@ -619,6 +620,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertDownload](#assert-download) [assertExactJson](#assert-exact-json) [assertForbidden](#assert-forbidden) +[assertFound](#assert-found) [assertHeader](#assert-header) [assertHeaderMissing](#assert-header-missing) [assertJson](#assert-json) @@ -635,16 +637,19 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertJsonValidationErrors](#assert-json-validation-errors) [assertJsonValidationErrorFor](#assert-json-validation-error-for) [assertLocation](#assert-location) +[assertMovedPermanently](#assert-moved-permanently) [assertContent](#assert-content) [assertNoContent](#assert-no-content) [assertStreamedContent](#assert-streamed-content) [assertNotFound](#assert-not-found) [assertOk](#assert-ok) +[assertPaymentRequired](#assert-payment-required) [assertPlainCookie](#assert-plain-cookie) [assertRedirect](#assert-redirect) [assertRedirectContains](#assert-redirect-contains) [assertRedirectToRoute](#assert-redirect-to-route) [assertRedirectToSignedRoute](#assert-redirect-to-signed-route) +[assertRequestTimeout](#assert-request-timeout) [assertSee](#assert-see) [assertSeeInOrder](#assert-see-in-order) [assertSeeText](#assert-see-text) @@ -659,6 +664,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertSessionMissing](#assert-session-missing) [assertStatus](#assert-status) [assertSuccessful](#assert-successful) +[assertTooManyRequests](#assert-too-many-requests) [assertUnauthorized](#assert-unauthorized) [assertUnprocessable](#assert-unprocessable) [assertUnsupportedMediaType](#assert-unsupported-media-type) @@ -671,6 +677,13 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a
    + +#### assertBadRequest + +Assert that the response has a bad request (400) HTTP status code: + + $response->assertBadRequest(); + #### assertAccepted @@ -759,6 +772,13 @@ Assert that the response has a forbidden (403) HTTP status code: $response->assertForbidden(); + +#### assertFound + +Assert that the response has a found (302) HTTP status code: + + $response->assertFound(); + #### assertHeader @@ -959,6 +979,13 @@ Assert the response has any JSON validation errors for the given key: $response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors'); + +#### assertMovedPermanently + +Assert that the response has a moved permanently (301) HTTP status code: + + $response->assertMovedPermanently(); + #### assertLocation @@ -1001,6 +1028,13 @@ Assert that the response has a 200 HTTP status code: $response->assertOk(); + +#### assertPaymentRequired + +Assert that the response has a payment required (402) HTTP status code: + + $response->assertPaymentRequired(); + #### assertPlainCookie @@ -1036,6 +1070,13 @@ Assert that the response is a redirect to the given [signed route](/docs/{{versi $response->assertRedirectToSignedRoute($name = null, $parameters = []); + +#### assertRequestTimeout + +Assert that the response has a request timeout (408) HTTP status code: + + $response->assertRequestTimeout(); + #### assertSee @@ -1171,6 +1212,13 @@ Assert that the response has a successful (>= 200 and < 300) HTTP status code: $response->assertSuccessful(); + +#### assertTooManyRequests + +Assert that the response has a too many requests (429) HTTP status code: + + $response->assertTooManyRequests(); + #### assertUnauthorized From 5b236ca87207d1c261f219909817229ec6e303ed Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 4 Apr 2023 09:19:07 -0500 Subject: [PATCH 0864/2609] selectResultSets --- database.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/database.md b/database.md index 64123d35997..a8e26f1261b 100644 --- a/database.md +++ b/database.md @@ -164,6 +164,15 @@ Sometimes your database query may result in a single, scalar value. Instead of b "select count(case when food = 'burger' then 1 end) as burgers from menu" ); + +#### Selecting Multiple Result Sets + +If your application calls stored procedures that return multiple result sets, you may use the `selectResultSets` method to retrieve all of the result sets returned by the stored procedure: + + [$options, $notifications] = DB::selectResultSets( + "CALL get_user_options_and_notifications(?)", $request->user()->id + ); + #### Using Named Bindings From e8935ab363d24691481199eb52b1d0049e3c5ecc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 4 Apr 2023 09:21:57 -0500 Subject: [PATCH 0865/2609] wip --- eloquent-mutators.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 43f2cd70cc4..49b0283396c 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -365,6 +365,20 @@ Similarly, Laravel offers an `AsCollection` cast that casts your JSON attribute 'options' => AsCollection::class, ]; +If you would like the `AsCollection` cast to instantiate a custom collection class instead of Laravel's base collection class, you may provide the collection class name as a cast argument: + + use App\Collections\OptionCollection; + use Illuminate\Database\Eloquent\Casts\AsCollection; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'options' => AsCollection::class.':'.OptionCollection::class, + ]; + ### Date Casting From fec293797aae94d02779a8483b60a4451e1ab022 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 4 Apr 2023 09:25:55 -0500 Subject: [PATCH 0866/2609] wip --- helpers.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/helpers.md b/helpers.md index 73ba080ad29..121d203b25f 100644 --- a/helpers.md +++ b/helpers.md @@ -1848,6 +1848,10 @@ The `Str::replace` method replaces a given string within the string: // Laravel 9.x +The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: + + Str::replace('Framework', 'Laravel', caseSensitive: false); + #### `Str::replaceArray()` {.collection-method} @@ -2877,6 +2881,12 @@ The `replace` method replaces a given string within the string: // Laravel 7.x +The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: + + $replaced = Str::of('Laravel 6.x')->replace( + '6.x', '7.x', caseSensitive: false + ); + #### `replaceArray` {.collection-method} From 779c4bb76a76ad621090274c13c295d43f3dd09a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 4 Apr 2023 09:26:30 -0500 Subject: [PATCH 0867/2609] wip --- helpers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index 121d203b25f..0edf66995cf 100644 --- a/helpers.md +++ b/helpers.md @@ -2883,8 +2883,8 @@ The `replace` method replaces a given string within the string: The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: - $replaced = Str::of('Laravel 6.x')->replace( - '6.x', '7.x', caseSensitive: false + $replaced = Str::of('macOS 13.x')->replace( + 'macOS', 'iOS', caseSensitive: false ); From 79152535476655cbb3f14101da161c9d089fd613 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 4 Apr 2023 17:10:12 +0200 Subject: [PATCH 0868/2609] [10.x] Confirming payments (#8692) * Confirming payments * Update billing.md * Update billing.md --------- Co-authored-by: Taylor Otwell --- billing.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/billing.md b/billing.md index e2ec0a07c04..31be4f8389e 100644 --- a/billing.md +++ b/billing.md @@ -62,6 +62,7 @@ - [Previewing Subscription Invoices](#previewing-subscription-invoices) - [Generating Invoice PDFs](#generating-invoice-pdfs) - [Handling Failed Payments](#handling-failed-payments) + - [Confirming Payments](#confirming-payments) - [Strong Customer Authentication (SCA)](#strong-customer-authentication) - [Payments Requiring Additional Confirmation](#payments-requiring-additional-confirmation) - [Off-session Payment Notifications](#off-session-payment-notifications) @@ -2027,6 +2028,17 @@ You can derive the specific status of an incomplete payment by inspecting the `p } } + +### Confirming Payments + +Some payment methods require additional data in order to confirm payments. For example, SEPA payment methods require additional "mandate" data during the payment process. You may provide this data to Cashier using the `withPaymentConfirmationOptions` method: + + $subscription->withPaymentConfirmationOptions([ + 'mandate_data' => '...', + ])->swap('price_xxx'); + +You may consult the [Stripe API documentation](https://stripe.com/docs/api/payment_intents/confirm) to review all of the options accepted when confirming payments. + ## Strong Customer Authentication From 8a4282926e0c44010f2f3b43ce255e8147330e57 Mon Sep 17 00:00:00 2001 From: Chris Fidao Date: Wed, 5 Apr 2023 11:05:45 -0400 Subject: [PATCH 0869/2609] [10.x] Add Chipper CI to Dusk docs (#8701) * Add Chipper CI Dusk is very commonly used in Chipper CI. We have [docs](https://chipperci.com/docs/testing/laravel-dusk-new/) on using Dusk, but it would be very handy if the official docs mentioned Chipper as well! * Update dusk.md --------- Co-authored-by: Taylor Otwell --- dusk.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/dusk.md b/dusk.md index 8200ce9ba1d..7c3877599da 100644 --- a/dusk.md +++ b/dusk.md @@ -47,6 +47,7 @@ - [Heroku CI](#running-tests-on-heroku-ci) - [Travis CI](#running-tests-on-travis-ci) - [GitHub Actions](#running-tests-on-github-actions) + - [Chipper CI](#running-tests-on-chipper-ci) ## Introduction @@ -1988,3 +1989,51 @@ jobs: name: console path: tests/Browser/console ``` + + +### Chipper CI + +If you are using [Chipper CI](https://chipperci.com) to run your Dusk tests, you may use the following configuration file as a starting point. We will use PHP's built-in server to run Laravel so we can listen for requests: + +```yaml +# file .chipperci.yml +version: 1 + +environment: + php: 8.2 + node: 16 + +# Include Chrome in the build environment +services: + - dusk + +# Build all commits +on: + push: + branches: .* + +pipeline: + - name: Setup + cmd: | + cp -v .env.example .env + composer install --no-interaction --prefer-dist --optimize-autoloader + php artisan key:generate + + # Create a dusk env file, ensuring APP_URL uses BUILD_HOST + cp -v .env .env.dusk.ci + sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci + + - name: Compile Assets + cmd: | + npm ci --no-audit + npm run build + + - name: Browser Tests + cmd: | + php -S [::0]:8000 -t public 2>server.log & + sleep 2 + php artisan dusk:chrome-driver $CHROME_DRIVER + php artisan dusk --env=ci +``` + +To learn more about running Dusk tests on Chipper CI, including how to use databases, consult the [official Chipper CI documentation](https://chipperci.com/docs/testing/laravel-dusk-new/). From 9d18a6436009502d2f3fd0a21853db9c2d22bb1c Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Wed, 5 Apr 2023 23:34:33 +0800 Subject: [PATCH 0870/2609] Document using `WithConsoleEvents` trait (#8700) * Document using `WithConsoleEvents` trait * formatting --------- Co-authored-by: Taylor Otwell --- console-tests.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/console-tests.md b/console-tests.md index 785d2aa8600..587ef58fb98 100644 --- a/console-tests.md +++ b/console-tests.md @@ -3,6 +3,7 @@ - [Introduction](#introduction) - [Success / Failure Expectations](#success-failure-expectations) - [Input / Output Expectations](#input-output-expectations) +- [Console Events](#console-events) ## Introduction @@ -88,3 +89,22 @@ If your command displays a table of information using Artisan's `table` method, [1, 'taylor@example.com'], [2, 'abigail@example.com'], ]); + + +## Console Events + +By default, the `Illuminate\Console\Events\CommandStarting` and `Illuminate\Console\Events\CommandFinished` events are not dispatched while running your application's tests. However, you can enable these events for a given test class by adding the `Illuminate\Foundation\Testing\WithConsoleEvents` trait to the class: + + Date: Fri, 7 Apr 2023 08:55:14 -0500 Subject: [PATCH 0871/2609] wip --- scout.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/scout.md b/scout.md index 4a8ce9163e8..56075dfddeb 100644 --- a/scout.md +++ b/scout.md @@ -26,7 +26,6 @@ - [Soft Deleting](#soft-deleting) - [Customizing Engine Searches](#customizing-engine-searches) - [Custom Engines](#custom-engines) -- [Builder Macros](#builder-macros) ## Introduction @@ -701,30 +700,3 @@ 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 application's `config/scout.php` configuration file: 'driver' => 'mysql', - - -## Builder Macros - -If you would like to define a custom Scout search 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: - - use Illuminate\Support\Facades\Response; - use Illuminate\Support\ServiceProvider; - use Laravel\Scout\Builder; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Builder::macro('count', function () { - return $this->engine()->getTotalCount( - $this->engine()->search($this) - ); - }); - } - -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; - - Order::search('Star Trek')->count(); From 8491913e34f0fbb7ffbfc30e0e3fb0ae4579560e Mon Sep 17 00:00:00 2001 From: Zayed Adel Al-Meklahfi Date: Sat, 8 Apr 2023 17:27:39 +0300 Subject: [PATCH 0872/2609] Update ISO-8601 date example in Eloquent: Mutators & Casting (#8709) This commit replaces the actual date example in the "Eloquent: Mutators & Casting" section with a more generic representation of the UTC ISO-8601 format. The change aims to prevent search engines from misinterpreting the example date as the publication date of the documentation page and to provide a clearer representation of the format for readers. --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 49b0283396c..4ab1def965c 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -419,7 +419,7 @@ To specify the format that should be used when actually storing a model's dates #### Date Casting, Serialization, & Timezones -By default, the `date` and `datetime` casts will serialize dates to a UTC ISO-8601 date string (`1986-05-28T21:05:54.000000Z`), regardless of the timezone specified in your application's `timezone` configuration option. You are strongly encouraged to always use this serialization format, as well as to store your application's dates in the UTC timezone by not changing your application's `timezone` configuration option from its default `UTC` value. Consistently using the UTC timezone throughout your application will provide the maximum level of interoperability with other date manipulation libraries written in PHP and JavaScript. +By default, the `date` and `datetime` casts will serialize dates to a UTC ISO-8601 date string (`YYYY-MM-DDTHH:MM:SS.uuuuuuZ`), regardless of the timezone specified in your application's `timezone` configuration option. You are strongly encouraged to always use this serialization format, as well as to store your application's dates in the UTC timezone by not changing your application's `timezone` configuration option from its default `UTC` value. Consistently using the UTC timezone throughout your application will provide the maximum level of interoperability with other date manipulation libraries written in PHP and JavaScript. If a custom format is applied to the `date` or `datetime` cast, such as `datetime:Y-m-d H:i:s`, the inner timezone of the Carbon instance will be used during date serialization. Typically, this will be the timezone specified in your application's `timezone` configuration option. From bb9433d0a3f9635c068e4460190b62960299c4e7 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Sat, 8 Apr 2023 17:58:46 +0330 Subject: [PATCH 0873/2609] add correct `assertRedirectToRoute` (#8708) --- http-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index ee129def29d..5cb4bb6464f 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1061,7 +1061,7 @@ Assert whether the response is redirecting to a URI that contains the given stri Assert that the response is a redirect to the given [named route](/docs/{{version}}/routing#named-routes): - $response->assertRedirectToRoute($name = null, $parameters = []); + $response->assertRedirectToRoute($name, $parameters = []); #### assertRedirectToSignedRoute From be523f35bdfaa5801594e37b730f88e518e5b979 Mon Sep 17 00:00:00 2001 From: Mohammadreza Date: Mon, 10 Apr 2023 17:23:10 +0330 Subject: [PATCH 0874/2609] add `assertServerError` in http-tests (#8714) --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 5cb4bb6464f..7c21defe897 100644 --- a/http-tests.md +++ b/http-tests.md @@ -654,6 +654,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertSeeInOrder](#assert-see-in-order) [assertSeeText](#assert-see-text) [assertSeeTextInOrder](#assert-see-text-in-order) +[assertServerError](#assert-server-error) [assertSessionHas](#assert-session-has) [assertSessionHasInput](#assert-session-has-input) [assertSessionHasAll](#assert-session-has-all) @@ -1105,6 +1106,13 @@ Assert that the given strings are contained in order within the response text. T $response->assertSeeTextInOrder(array $values, $escaped = true); + +#### assertServerError + +Assert that the response has a server error (>= 500 , < 600) HTTP status code: + + $response->assertServerError(); + #### assertSessionHas From fb351fe6fa0da43579c6c937f413095ed2f56537 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 10 Apr 2023 09:00:27 -0500 Subject: [PATCH 0875/2609] wip --- rate-limiting.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rate-limiting.md b/rate-limiting.md index b659e3778f9..30cca6f54cf 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -44,6 +44,17 @@ The `attempt` method returns `false` when the callback has no remaining attempts return 'Too many messages sent!'; } +If necessary, you may provide a fourth argument to the `attempt` method, which is the "decay rate", or the number of seconds until the available attempts are reset. For example, we can modify the example above to allow five attempts every two minutes: + + $executed = RateLimiter::attempt( + 'send-message:'.$user->id, + $perTwoMinutes = 5, + function() { + // Send message... + } + $decayRate = 120, + ); + ### Manually Incrementing Attempts From 98c7b77dbf7c5bfd146f0297d7454690765edf70 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Apr 2023 09:17:44 -0500 Subject: [PATCH 0876/2609] document piplines --- processes.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/processes.md b/processes.md index 1456adde421..d7d1fada1c8 100644 --- a/processes.md +++ b/processes.md @@ -4,6 +4,7 @@ - [Invoking Processes](#invoking-processes) - [Process Options](#process-options) - [Process Output](#process-output) + - [Pipelines](#process-pipelines) - [Asynchronous Processes](#asynchronous-processes) - [Process IDs & Signals](#process-ids-and-signals) - [Asynchronous Process Output](#asynchronous-process-output) @@ -172,6 +173,47 @@ use Illuminate\Support\Facades\Process; $result = Process::quietly()->run('bash import.sh'); ``` + +### Pipelines + +Sometimes you may want to make the output of one process the input of another process. This is often referred to as "piping" the output of a process into another. The `pipe` method provided by the `Process` facades makes this easy to accomplish. The `pipe` method will execute the piped processes synchronously and return the process result for the last process in the pipeline: + +```php +use Illuminate\Process\Pipe; +use Illuminate\Support\Facades\Process; + +$result = Process::pipe(function (Pipe $pipe) { + $pipe->command('cat example.txt'), + $pipe->command('grep -i "laravel"'), +}); + +if ($result->successful()) { + // ... +} +``` + +The process output may be gathered in real-time by passing a closure as the second argument to the `pipe` method. The closure will receive two arguments: the "type" of output (`stdout` or `stderr`) and the output string itself: + +```php +$result = Process::pipe(function (Pipe $pipe) { + $pipe->command('cat example.txt'), + $pipe->command('grep -i "laravel"'), +}, function (string $type, string $output) { + echo $output; +}); +``` + +Laravel also allows you to assign string keys to each process within a pipeline via the `as` method. This key will also be passed to the output closure provided to the `pipe` method, allowing you to determine which process the output belongs to: + +```php +$result = Process::pipe(function (Pipe $pipe) { + $pipe->as('first')->command('cat example.txt'), + $pipe->as('second')->command('grep -i "laravel"'), +})->start(function (string $type, string $output, string $key) { + // ... +}); +``` + ## Asynchronous Processes From c317ab0f216bdd5a06a48634fbce3ed310aecbc0 Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 11 Apr 2023 14:07:33 -0300 Subject: [PATCH 0877/2609] Fix typo on process pipeline examples (#8715) --- processes.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/processes.md b/processes.md index d7d1fada1c8..f725dd1e8a1 100644 --- a/processes.md +++ b/processes.md @@ -183,8 +183,8 @@ use Illuminate\Process\Pipe; use Illuminate\Support\Facades\Process; $result = Process::pipe(function (Pipe $pipe) { - $pipe->command('cat example.txt'), - $pipe->command('grep -i "laravel"'), + $pipe->command('cat example.txt'); + $pipe->command('grep -i "laravel"'); }); if ($result->successful()) { @@ -196,8 +196,8 @@ The process output may be gathered in real-time by passing a closure as the seco ```php $result = Process::pipe(function (Pipe $pipe) { - $pipe->command('cat example.txt'), - $pipe->command('grep -i "laravel"'), + $pipe->command('cat example.txt'); + $pipe->command('grep -i "laravel"'); }, function (string $type, string $output) { echo $output; }); @@ -207,8 +207,8 @@ Laravel also allows you to assign string keys to each process within a pipeline ```php $result = Process::pipe(function (Pipe $pipe) { - $pipe->as('first')->command('cat example.txt'), - $pipe->as('second')->command('grep -i "laravel"'), + $pipe->as('first')->command('cat example.txt'); + $pipe->as('second')->command('grep -i "laravel"'); })->start(function (string $type, string $output, string $key) { // ... }); From 68edcfbb8df021c671c8171e012f145fe0556572 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Apr 2023 17:28:46 -0500 Subject: [PATCH 0878/2609] tweaking some wording --- deployment.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/deployment.md b/deployment.md index aaff2d2f780..880165fdd78 100644 --- a/deployment.md +++ b/deployment.md @@ -6,11 +6,11 @@ - [Nginx](#nginx) - [Optimization](#optimization) - [Autoloader Optimization](#autoloader-optimization) - - [Optimizing Configuration Loading](#optimizing-configuration-loading) - - [Optimizing Route Loading](#optimizing-route-loading) - - [Optimizing View Loading](#optimizing-view-loading) + - [Caching Configuration](#optimizing-configuration-loading) + - [Caching Routes](#optimizing-route-loading) + - [Caching Views](#optimizing-view-loading) - [Debug Mode](#debug-mode) -- [Deploying With Forge / Vapor](#deploying-with-forge-or-vapor) +- [Easy Deployment With Forge / Vapor](#deploying-with-forge-or-vapor) ## Introduction @@ -102,7 +102,7 @@ composer install --optimize-autoloader --no-dev > In addition to optimizing the autoloader, you should always be sure to include a `composer.lock` file in your project's source control repository. Your project's dependencies can be installed much faster when a `composer.lock` file is present. -### Optimizing Configuration Loading +### Caching Configuration When deploying your application to production, you should make sure that you run the `config:cache` Artisan command during your deployment process: @@ -116,7 +116,7 @@ This command will combine all of Laravel's configuration files into a single, ca > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function for `.env` variables will return `null`. -### Optimizing Route Loading +### Caching Routes If you are building a large application with many routes, you should make sure that you are running the `route:cache` Artisan command during your deployment process: @@ -127,7 +127,7 @@ php artisan route:cache This command reduces all of your route registrations into a single method call within a cached file, improving the performance of route registration when registering hundreds of routes. -### Optimizing View Loading +### Caching Views When deploying your application to production, you should make sure that you run the `view:cache` Artisan command during your deployment process: @@ -145,7 +145,7 @@ The debug option in your config/app.php configuration file determines how much i **In your production environment, this value should always be `false`. If the `APP_DEBUG` variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** -## Deploying With Forge / Vapor +## Easy Deployment With Forge / Vapor #### Laravel Forge From 8300119b4783e3001177eb22958efa9c3badc350 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 12 Apr 2023 16:49:52 +0330 Subject: [PATCH 0879/2609] [10.x] Add `dispatch_sync` into helpers (#8717) * add `dispatch_sync` into helpers * formatting --------- Co-authored-by: Taylor Otwell --- helpers.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/helpers.md b/helpers.md index 0edf66995cf..c6d05322e26 100644 --- a/helpers.md +++ b/helpers.md @@ -305,6 +305,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [decrypt](#method-decrypt) [dd](#method-dd) [dispatch](#method-dispatch) +[dispatch_sync](#method-dispatch-sync) [dump](#method-dump) [encrypt](#method-encrypt) [env](#method-env) @@ -3711,6 +3712,13 @@ The `dispatch` function pushes the given [job](/docs/{{version}}/queues#creating dispatch(new App\Jobs\SendEmails); + +#### `dispatch_sync()` {.collection-method} + +The `dispatch_sync` function pushes the given job to the [sync](/docs/{{version}}/queues#synchronous-dispatching) queue so that it is processed immediately: + + dispatch_sync(new App\Jobs\SendEmails); + #### `dump()` {.collection-method} From c03ab53f8a2af2fb13888060906cd91ee1e6a8be Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 12 Apr 2023 13:58:03 -0500 Subject: [PATCH 0880/2609] adjust docs per rate limiter being moved out of provider by default --- routing.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/routing.md b/routing.md index 7f4d27c5430..d4652f35472 100644 --- a/routing.md +++ b/routing.md @@ -665,7 +665,7 @@ Using the `Route::fallback` method, you may define a route that will be executed ### Defining Rate Limiters -Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this should be done within the `configureRateLimiting` method of your application's `App\Providers\RouteServiceProvider` class, which already includes a rate limiter definition that is applied to the routes in your application's `routes/api.php` file: +Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this should be done within the `boot` method of your application's `App\Providers\RouteServiceProvider` class: ```php use Illuminate\Cache\RateLimiting\Limit; @@ -673,13 +673,15 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; /** - * Configure the rate limiters for the application. + * Define your route model bindings, pattern filters, and other route configuration. */ -protected function configureRateLimiting(): void +protected function boot(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); + + // ... } ``` @@ -690,13 +692,15 @@ Rate limiters are defined using the `RateLimiter` facade's `for` method. The `fo use Illuminate\Support\Facades\RateLimiter; /** - * Configure the rate limiters for the application. + * Define your route model bindings, pattern filters, and other route configuration. */ - protected function configureRateLimiting(): void + protected function boot(): void { RateLimiter::for('global', function (Request $request) { return Limit::perMinute(1000); }); + + // ... } If the incoming request exceeds the specified rate limit, a response with a 429 HTTP status code will automatically be returned by Laravel. If you would like to define your own response that should be returned by a rate limit, you may use the `response` method: From e2a4a5dc00aa97c4f32b9ec75ab8ada8a700c809 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 12 Apr 2023 14:45:52 -0500 Subject: [PATCH 0881/2609] document event cache in deployment docs --- deployment.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/deployment.md b/deployment.md index 880165fdd78..133bc5782a3 100644 --- a/deployment.md +++ b/deployment.md @@ -7,6 +7,7 @@ - [Optimization](#optimization) - [Autoloader Optimization](#autoloader-optimization) - [Caching Configuration](#optimizing-configuration-loading) + - [Caching Events](#caching-events) - [Caching Routes](#optimizing-route-loading) - [Caching Views](#optimizing-view-loading) - [Debug Mode](#debug-mode) @@ -115,6 +116,15 @@ This command will combine all of Laravel's configuration files into a single, ca > **Warning** > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function for `.env` variables will return `null`. + +### Caching Events + +If your application is utilizing [event discovery](/docs/{{version}}/events#event-discovery), you should cache your application's event to listener mappings during your deployment process. This can be accomplished by invoking the `event:cache` Artisan command during deployment: + +```shell +php artisan event:cache +``` + ### Caching Routes From d544e4a50a9b7a022a187c13da7037e8ad1b3ef2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 14 Apr 2023 12:54:34 -0500 Subject: [PATCH 0882/2609] document bound --- container.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/container.md b/container.md index e48dafd15cf..68fe287162c 100644 --- a/container.md +++ b/container.md @@ -343,6 +343,12 @@ If some of your class's dependencies are not resolvable via the container, you m $transistor = $this->app->makeWith(Transistor::class, ['id' => 1]); +The `bound` method may be used to determine if a class or interface has been explicitly bound in the container: + + if ($this->app->bound(Transistor::class)) { + // ... + } + If you are outside of a service provider in a location of your code that does not have access to the `$app` variable, you may use the `App` [facade](/docs/{{version}}/facades) or the `app` [helper](/docs/{{version}}/helpers#method-app) to resolve a class instance from the container: use App\Services\Transistor; From 9e9676d793507867362261934ab815749b900753 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 14 Apr 2023 16:37:38 -0500 Subject: [PATCH 0883/2609] document service provider changes and exception handler changes --- errors.md | 4 ++-- providers.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/errors.md b/errors.md index c6ca24026f4..fee23227e02 100644 --- a/errors.md +++ b/errors.md @@ -124,7 +124,7 @@ When messages are written to your application's [logs](/docs/{{version}}/logging As noted above, even when you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application; however, since the log level can sometimes influence the channels on which a message is logged, you may wish to configure the log level that certain exceptions are logged at. -To accomplish this, you may define an array of exception types and their associated log levels within the `$levels` property of your application's exception handler: +To accomplish this, you may define a `$levels` property on your application's exception handler. This property should contain an array of exception types and their associated log levels: use PDOException; use Psr\Log\LogLevel; @@ -141,7 +141,7 @@ To accomplish this, you may define an array of exception types and their associa ### Ignoring Exceptions By Type -When building your application, there will be some types of exceptions you simply want to ignore and never report. Your application's exception handler contains a `$dontReport` property which is initialized to an empty array. Any classes that you add to this property will never be reported; however, they may still have custom rendering logic: +When building your application, there will be some types of exceptions you simply want to ignore and never report. To ignore these exceptions, define a `$dontReport` property on your application's exception handler. Any classes that you add to this property will never be reported; however, they may still have custom rendering logic: use App\Exceptions\InvalidOrderException; diff --git a/providers.md b/providers.md index 2d507902568..00217b03a78 100644 --- a/providers.md +++ b/providers.md @@ -145,15 +145,15 @@ You may type-hint dependencies for your service provider's `boot` method. The [s ## Registering Providers -All service providers are registered in the `config/app.php` configuration file. This file contains a `providers` array where you can list the class names of your service providers. 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. +All service providers are registered in the `config/app.php` configuration file. This file contains a `providers` array where you can list the class names of your service providers. By default, a set of Laravel core service providers are registered in this array. The default providers bootstrap the core Laravel components, such as the mailer, queue, cache, and others. To register your provider, add it to the array: - 'providers' => [ + 'providers' => ServiceProvider::defaultProviders()->merge([ // Other Service Providers App\Providers\ComposerServiceProvider::class, - ], + ])->toArray(), ## Deferred Providers From a74fa585df52ba56bcd69f481ef2f84c32f96217 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 14 Apr 2023 16:56:48 -0500 Subject: [PATCH 0884/2609] update validation docs --- validation.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/validation.md b/validation.md index ee339cbc326..5bb47d4b742 100644 --- a/validation.md +++ b/validation.md @@ -197,7 +197,9 @@ So, in our example, the user will be redirected to our controller's `create` met #### Customizing The Error Messages -Laravel's built-in validation rules each have an error message that is located in your application's `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. +Laravel's built-in validation rules each have an error message that is located in your application's `lang/en/validation.php` file. If your application does not have a `lang` directory, you may instruct Laravel to create it using the `lang:publish` Artisan command. + +Within the `lang/en/validation.php` 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 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). @@ -320,7 +322,7 @@ As you might have guessed, the `authorize` method is responsible for determining ]; } -> **Note** +> **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). 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: @@ -436,7 +438,7 @@ If you plan to handle authorization logic for the request in another part of you return true; } -> **Note** +> **Note** > You may type-hint any dependencies you need within the `authorize` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). @@ -735,7 +737,9 @@ The `has` method may be used to determine if any error messages exist for a give ### Specifying Custom Messages In Language Files -Laravel's built-in validation rules each have an error message that is located in your application's `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. +Laravel's built-in validation rules each have an error message that is located in your application's `lang/en/validation.php` file. If your application does not have a `lang` directory, you may instruct Laravel to create it using the `lang:publish` Artisan command. + +Within the `lang/en/validation.php` 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 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). From 1e35134f95b93afa3eefa3f3bd7d19f607775604 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Sat, 15 Apr 2023 01:32:11 +0330 Subject: [PATCH 0885/2609] [10.x] Add generate scope in eloquent (#8723) * add generate scope in eloquent * formatting --------- Co-authored-by: Taylor Otwell --- eloquent.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/eloquent.md b/eloquent.md index eef45d4eb98..8cd868cb681 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1170,12 +1170,19 @@ To exclude one or more attributes from being replicated to the new model, you ma Global scopes allow you to add constraints to all queries for a given model. Laravel's own [soft delete](#soft-deleting) functionality utilizes global scopes to only retrieve "non-deleted" models from the database. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints. + +#### Generating Scopes + +To generate a new global scope, you may invoke the `make:scope` Artisan command, which will place the generated scope in your application's `app/Models/Scopes` directory: + +```shell +php artisan make:scope AncientScope +``` + #### Writing Global Scopes -Writing a global scope is simple. First, define a class that implements the `Illuminate\Database\Eloquent\Scope` interface. Laravel does not have a conventional location where you should place scope classes, so you are free to place this class in any directory that you wish. - -The `Scope` interface requires you to implement one method: `apply`. The `apply` method may add `where` constraints or other types of clauses to the query as needed: +Writing a global scope is simple. First, use the `make:scope` command to generate a class that implements the `Illuminate\Database\Eloquent\Scope` interface. The `Scope` interface requires you to implement one method: `apply`. The `apply` method may add `where` constraints or other types of clauses to the query as needed: Date: Sat, 15 Apr 2023 18:12:12 -0300 Subject: [PATCH 0886/2609] Update Symfony Mailer link (#8727) This page points to a Symfony Mailer Documentation which is not maintained anymore. --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 4689088a9df..193f7c0df88 100644 --- a/mail.md +++ b/mail.md @@ -35,7 +35,7 @@ ## Introduction -Sending email doesn't have to be complicated. Laravel provides a clean, simple email API powered by the popular [Symfony Mailer](https://symfony.com/doc/6.0/mailer.html) component. Laravel and Symfony Mailer provide drivers for sending email via SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. +Sending email doesn't have to be complicated. Laravel provides a clean, simple email API powered by the popular [Symfony Mailer](https://symfony.com/doc/6.2/mailer.html) component. Laravel and Symfony Mailer provide drivers for sending email via SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. ### Configuration From f4b4a5f528dca838f89b006ed0dceff1ce713c10 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 15 Apr 2023 16:43:17 -0500 Subject: [PATCH 0887/2609] update rate limiting docs --- routing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routing.md b/routing.md index d4652f35472..1a050fbd615 100644 --- a/routing.md +++ b/routing.md @@ -665,7 +665,9 @@ Using the `Route::fallback` method, you may define a route that will be executed ### Defining Rate Limiters -Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this should be done within the `boot` method of your application's `App\Providers\RouteServiceProvider` class: +Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. + +Typically, rate limiters are defined within the `boot` method of your application's `App\Providers\RouteServiceProvider` class. In fact, this class already includes a rate limiter definition that is applied to the routes in your application's `routes/api.php` file: ```php use Illuminate\Cache\RateLimiting\Limit; From b9a5cd9aa7aa038e4138249b5aa6a9dbd6b80689 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 16 Apr 2023 14:28:04 +0100 Subject: [PATCH 0888/2609] Missing comma before argument in example code. (#8728) Was copying and pasting some example code from docs and discovered missing comma before 4th argument in the example. I'll admit I'm embarrassed to submit such a pathetic PR. --- rate-limiting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rate-limiting.md b/rate-limiting.md index 30cca6f54cf..5b36df01962 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -51,7 +51,7 @@ If necessary, you may provide a fourth argument to the `attempt` method, which i $perTwoMinutes = 5, function() { // Send message... - } + }, $decayRate = 120, ); From ba64839594ff741eabcffcf1226f51666156b7c8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 17 Apr 2023 15:29:11 -0500 Subject: [PATCH 0889/2609] wip --- cache.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cache.md b/cache.md index 1517900e484..4f6ceb302e0 100644 --- a/cache.md +++ b/cache.md @@ -328,6 +328,9 @@ When using the `database` cache driver, you will need to setup a table to contai $table->integer('expiration'); }); +> **Note** +> If you used the `cache:table` Artisan command to create the database driver's cache table, the migration created by that command already includes a definition for the `cache_locks` table. + ### Managing Locks From 9b29c445ffda01fc08d4928e538942b044da258c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Apr 2023 08:56:32 -0500 Subject: [PATCH 0890/2609] show custom index name --- migrations.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/migrations.md b/migrations.md index 5bec5aabd5b..e183b6dfd10 100644 --- a/migrations.md +++ b/migrations.md @@ -1214,10 +1214,12 @@ Since this syntax is rather verbose, Laravel provides additional, terser methods $table->foreignId('user_id')->constrained(); }); -The `foreignId` method creates an `UNSIGNED BIGINT` equivalent column, 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: +The `foreignId` method creates an `UNSIGNED BIGINT` equivalent column, 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 and index name by passing them as arguments to the `constrained` method: Schema::table('posts', function (Blueprint $table) { - $table->foreignId('user_id')->constrained('users'); + $table->foreignId('user_id')->constrained( + table: 'users', indexName: 'posts_user_id' + ); }); You may also specify the desired action for the "on delete" and "on update" properties of the constraint: From 7a52af26ab3cbc9ec7aff9a0693ec81cdf7d3382 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Apr 2023 08:59:35 -0500 Subject: [PATCH 0891/2609] wip --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index e183b6dfd10..aa734cc6a09 100644 --- a/migrations.md +++ b/migrations.md @@ -1214,7 +1214,7 @@ Since this syntax is rather verbose, Laravel provides additional, terser methods $table->foreignId('user_id')->constrained(); }); -The `foreignId` method creates an `UNSIGNED BIGINT` equivalent column, 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 and index name by passing them as arguments to the `constrained` method: +The `foreignId` method creates an `UNSIGNED BIGINT` equivalent column, while the `constrained` method will use conventions to determine the table and column being referenced. If your table name does not match Laravel's conventions, you may manually provide it to the `constrained` method. In addition, the name that should be assigned to the generated index may be specified as well: Schema::table('posts', function (Blueprint $table) { $table->foreignId('user_id')->constrained( From b1b0616e4e3cb09030f813f6acf320a2ae12eb01 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Apr 2023 09:07:47 -0500 Subject: [PATCH 0892/2609] wip --- processes.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/processes.md b/processes.md index f725dd1e8a1..53ba46f7ae5 100644 --- a/processes.md +++ b/processes.md @@ -192,6 +192,15 @@ if ($result->successful()) { } ``` +If you do not need to customize the individual processes that make up the pipeline, you may simply pass an array of command strings to the `pipe` method: + +```php +$result = Process::pipe([ + 'cat example.txt', + 'grep -i "laravel"', +]); +``` + The process output may be gathered in real-time by passing a closure as the second argument to the `pipe` method. The closure will receive two arguments: the "type" of output (`stdout` or `stderr`) and the output string itself: ```php From a759a33abea620a3a0179df91bd72d7a79c22e07 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Apr 2023 09:42:25 -0500 Subject: [PATCH 0893/2609] improve after hook docs --- validation.md | 78 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/validation.md b/validation.md index 5bb47d4b742..26c68acd353 100644 --- a/validation.md +++ b/validation.md @@ -18,7 +18,7 @@ - [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) + - [Performing Additional Validation](#performing-additional-validation) - [Working With Validated Input](#working-with-validated-input) - [Working With Error Messages](#working-with-error-messages) - [Specifying Custom Messages In Language Files](#specifying-custom-messages-in-language-files) @@ -348,28 +348,56 @@ So, how are the validation rules evaluated? All you need to do is type-hint the 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](#validation-error-response-format). - -#### Adding After Hooks To Form Requests + +#### Performing Additional Validation -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: +Sometimes you need to perform additional validation after your initial validation is complete. You can accomplish this using the form request's `after` method. + +The `after` method should return an array of callables or closures which will be invoked after validation is complete. The given callables will receive an `Illuminate\Validation\Validator` instance, allowing you to raise additional error messages if necessary: use Illuminate\Validation\Validator; /** - * Configure the validator instance. + * Get the "after" validation callables for the request. */ - public function withValidator(Validator $validator): void + public function after(): array { - $validator->after(function (Validator $validator) { - if ($this->somethingElseIsInvalid()) { - $validator->errors()->add('field', 'Something is wrong with this field!'); + return [ + function (Validator $validator) { + if ($this->somethingElseIsInvalid()) { + $validator->errors()->add( + 'field', + 'Something is wrong with this field!' + ); + } } - }); + ]; } +As noted, the array returned by the `after` method may also contain invokable classes. The `__invoke` method of these classes will receive an `Illuminate\Validation\Validator` instance: + +```php +use App\Validation\ValidateShippingTime; +use App\Validation\ValidateUserStatus; +use Illuminate\Validation\Validator; + +/** + * Get the "after" validation callables for the request. + */ +public function after(): array +{ + return [ + new ValidateUserStatus, + new ValidateShippingTime, + function (Validator $validator) { + // + } + ]; +} +``` -#### Stopping On First Validation Failure Attribute +#### Stopping On The First Validation Failure By adding a `stopOnFirstFailure` property to your request class, you may inform the validator that it should stop validating all attributes once a single validation failure has occurred: @@ -628,17 +656,16 @@ Many of Laravel's built-in error messages include an `:attribute` placeholder th 'email' => 'email address', ]); - -### After Validation Hook + +### Performing Additional Validation -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: +Sometimes you need to perform additional validation after your initial validation is complete. You can accomplish this using the validator's `after` method. The `after` method accepts a closure or an array of callables which will be invoked after validation is complete. The given callables will receive an `Illuminate\Validation\Validator` instance, allowing you to raise additional error messages if necessary: - use Illuminate\Support\Facades; - use Illuminate\Validation\Validator; + use Illuminate\Support\Facades\Validator; - $validator = Facades\Validator::make(/* ... */); + $validator = Validator::make(/* ... */); - $validator->after(function (Validator $validator) { + $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add( 'field', 'Something is wrong with this field!' @@ -650,6 +677,21 @@ You may also attach callbacks to be run after validation is completed. This allo // ... } +As noted, the `after` method also accepts an array of callables, which is particularly convenient if your "after validation" logic is encapsulated in invokable classes, which will receive an `Illuminate\Validation\Validator` instance via their `__invoke` method: + +```php +use App\Validation\ValidateShippingTime; +use App\Validation\ValidateUserStatus; + +$validator->after([ + new ValidateUserStatus, + new ValidateShippingTime, + function ($validator) { + // ... + }, +]); +``` + ## Working With Validated Input From fb1eee40c8a794e16ddff45b478422714219b0a9 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Fri, 21 Apr 2023 03:30:04 +1000 Subject: [PATCH 0894/2609] [10.x] Add note about UUID/ULID keys on notifications table (#8731) * Add note about UUID/ULID keys on notifications table * Update notifications.md * Update notifications.md --------- Co-authored-by: Taylor Otwell --- notifications.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/notifications.md b/notifications.md index ec8103fed65..7113e7292bb 100644 --- a/notifications.md +++ b/notifications.md @@ -782,6 +782,9 @@ php artisan notifications:table php artisan migrate ``` +> **Note** +> If your notifiable models are using [UUID or ULID primary keys](/docs/{{version}}/eloquent#uuid-and-ulid-keys), you should replace the `morphs()` method with [`uuidMorphs()`](docs/{{version}}/migrations#column-method-uuidMorphs) or [`ulidMorphs`](/docs/{{version}}/migrations#column-method-ulidMorphs) in the notification table migration. + ### Formatting Database Notifications From bee2d9ace54a301bb4b6fddbcbc660cc3c6a8d6c Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Fri, 21 Apr 2023 10:04:04 +1000 Subject: [PATCH 0895/2609] Remove inconsistent method parentheses --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 7113e7292bb..948efb2a0c7 100644 --- a/notifications.md +++ b/notifications.md @@ -783,7 +783,7 @@ php artisan migrate ``` > **Note** -> If your notifiable models are using [UUID or ULID primary keys](/docs/{{version}}/eloquent#uuid-and-ulid-keys), you should replace the `morphs()` method with [`uuidMorphs()`](docs/{{version}}/migrations#column-method-uuidMorphs) or [`ulidMorphs`](/docs/{{version}}/migrations#column-method-ulidMorphs) in the notification table migration. +> If your notifiable models are using [UUID or ULID primary keys](/docs/{{version}}/eloquent#uuid-and-ulid-keys), you should replace the `morphs` method with [`uuidMorphs`](docs/{{version}}/migrations#column-method-uuidMorphs) or [`ulidMorphs`](/docs/{{version}}/migrations#column-method-ulidMorphs) in the notification table migration. ### Formatting Database Notifications From bbd47a6fc3edecde6b7b8d5352e5b627b33b4e44 Mon Sep 17 00:00:00 2001 From: xc <45385480+localusercamp@users.noreply.github.com> Date: Fri, 21 Apr 2023 18:06:14 +0500 Subject: [PATCH 0896/2609] isMath typo fixed (#8732) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index c6d05322e26..d06be678d56 100644 --- a/helpers.md +++ b/helpers.md @@ -2741,7 +2741,7 @@ The `isMatch` method will return `true` if the string matches a given regular ex // true - $result = Str::of('laravel')->match('/foo (.*)/'); + $result = Str::of('laravel')->isMatch('/foo (.*)/'); // false From 2567c252453937976f9f76c9fa4925ae8c7ab619 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:41:45 +0330 Subject: [PATCH 0897/2609] [10.x] Add `bindIf` in Container (#8735) * add `bindIf` in container * Update container.md --------- Co-authored-by: Taylor Otwell --- container.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/container.md b/container.md index 68fe287162c..db92bfef9e6 100644 --- a/container.md +++ b/container.md @@ -126,6 +126,14 @@ As mentioned, you will typically be interacting with the container within servic // ... }); +You may use the `bindIf` method to register a container binding only if a binding has not already been registered for the given type: + +```php +$this->app->bindIf(Transistor::class, function (Application $app) { + return new Transistor($app->make(PodcastParser::class)); +}); +``` + > **Note** > There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection. From 39e1327553497df8f181d7892eec47ab65ef7a08 Mon Sep 17 00:00:00 2001 From: nazzal ramzan Date: Mon, 24 Apr 2023 21:32:48 +0700 Subject: [PATCH 0898/2609] fix: for consistent writing, call model class without its namespace (#8738) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 8cd868cb681..9cb988a082d 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1331,7 +1331,7 @@ Combining multiple Eloquent model scopes via an `or` query operator may require However, since this can be cumbersome, Laravel provides a "higher order" `orWhere` method that allows you to fluently chain scopes together without the use of closures: - $users = App\Models\User::popular()->orWhere->active()->get(); + $users = User::popular()->orWhere->active()->get(); #### Dynamic Scopes From f73459a5faf07a8816a95eca0927423f1128c176 Mon Sep 17 00:00:00 2001 From: Sven Luijten <11269635+svenluijten@users.noreply.github.com> Date: Mon, 24 Apr 2023 20:53:05 +0200 Subject: [PATCH 0899/2609] Use PSR-3 placeholders in logging docs (#8739) In response to [this blog post by @Crell](https://peakd.com/hive-168588/@crell/using-psr-3-placeholders-properly). --- logging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logging.md b/logging.md index bf544e3fe6b..30e8730a449 100644 --- a/logging.md +++ b/logging.md @@ -200,7 +200,7 @@ You may call any of these methods to log a message for the corresponding level. */ public function show(string $id): View { - Log::info('Showing the user profile for user: '.$id); + Log::info('Showing the user profile for user: {id}', ['id' => $id]); return view('user.profile', [ 'user' => User::findOrFail($id) @@ -215,7 +215,7 @@ An array of contextual data may be passed to the log methods. This contextual da use Illuminate\Support\Facades\Log; - Log::info('User failed to login.', ['id' => $user->id]); + Log::info('User {id} failed to login.', ['id' => $user->id]); Occasionally, you may wish to specify some contextual information that should be included with all subsequent log entries in a particular channel. For example, you may wish to log a request ID that is associated with each incoming request to your application. To accomplish this, you may call the `Log` facade's `withContext` method: From 6ae940092011753204c553f57f4ee88577a9d99f Mon Sep 17 00:00:00 2001 From: Mubashir Abbas Date: Wed, 26 Apr 2023 18:29:05 +0500 Subject: [PATCH 0900/2609] Added missing apostophe (#8743) * added a missing apostophe. * another missing apotophe fixed. * missing apotophe added. --- mail.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mail.md b/mail.md index 193f7c0df88..3f3398ac102 100644 --- a/mail.md +++ b/mail.md @@ -226,7 +226,7 @@ In addition, you may define a global "reply_to" address within your `config/mail ### Configuring The View -Within a mailable class' `content` method, you may define the `view`, or which template should be used when rendering the email's contents. Since each email typically uses a [Blade template](/docs/{{version}}/blade) to render its contents, you have the full power and convenience of the Blade templating engine when building your email's HTML: +Within a mailable class's `content` method, you may define the `view`, or which template should be used when rendering the email's contents. Since each email typically uses a [Blade template](/docs/{{version}}/blade) to render its contents, you have the full power and convenience of the Blade templating engine when building your email's HTML: /** * Get the message content definition. @@ -270,7 +270,7 @@ For clarity, the `html` parameter may be used as an alias of the `view` paramete #### Via Public Properties -Typically, you will want to pass some data to your view that you can utilize when rendering the email's HTML. There are two ways you may make data available to your view. First, any public property defined on your mailable class will automatically be made available to the view. So, for example, you may pass data into your mailable class' constructor and set that data to public properties defined on the class: +Typically, you will want to pass some data to your view that you can utilize when rendering the email's HTML. There are two ways you may make data available to your view. First, any public property defined on your mailable class will automatically be made available to the view. So, for example, you may pass data into your mailable class's constructor and set that data to public properties defined on the class: #### Via The `with` Parameter: -If you would like to customize the format of your email's data before it is sent to the template, you may manually pass your data to the view via the `Content` definition's `with` parameter. Typically, you will still pass data via the mailable class' constructor; however, you should set this data to `protected` or `private` properties so the data is not automatically made available to the template: +If you would like to customize the format of your email's data before it is sent to the template, you may manually pass your data to the view via the `Content` definition's `with` parameter. Typically, you will still pass data via the mailable class's constructor; however, you should set this data to `protected` or `private` properties so the data is not automatically made available to the template: Date: Wed, 26 Apr 2023 15:37:17 +0200 Subject: [PATCH 0901/2609] Update VirtualBox link on Homestead page (#8742) --- homestead.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homestead.md b/homestead.md index 81d3a93805e..a58cee36360 100644 --- a/homestead.md +++ b/homestead.md @@ -141,7 +141,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M Before launching your Homestead environment, you must install [Vagrant](https://developer.hashicorp.com/vagrant/downloads) as well as one of the following supported providers: -- [VirtualBox 6.1.x](https://www.virtualbox.org/wiki/Downloads) +- [VirtualBox 6.1.x](https://www.virtualbox.org/wiki/Download_Old_Builds_6_1) - [Parallels](https://www.parallels.com/products/desktop/) All of these software packages provide easy-to-use visual installers for all popular operating systems. From 302485ef548296ca0640cc660d4cbd915d00463d Mon Sep 17 00:00:00 2001 From: Anjorin Damilare Date: Wed, 26 Apr 2023 16:22:12 +0100 Subject: [PATCH 0902/2609] [10.x] Add example for date instance argument for `release` method (#8745) * [10.x] Improve `release` method example for date instance * Update queues.md * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 24b58687424..5ea19b01791 100644 --- a/queues.md +++ b/queues.md @@ -1125,10 +1125,12 @@ Sometimes you may wish to manually release a job back onto the queue so that it $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: +By default, the `release` method will release the job back onto the queue for immediate processing. However, you may instruct the queue to not make the job available for processing until a given number of seconds has elapsed by passing an integer or date instance to the `release` method: $this->release(10); + $this->release(now()->addSeconds(10)); + #### Manually Failing A Job From 8bdcec6266e469c404665f40cfa3aeafdd40b954 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 27 Apr 2023 16:15:31 +0200 Subject: [PATCH 0903/2609] Update installation.md (#8751) --- installation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/installation.md b/installation.md index dcdf41cb7fc..e5b856c5528 100644 --- a/installation.md +++ b/installation.md @@ -11,6 +11,7 @@ - [Initial Configuration](#initial-configuration) - [Environment Based Configuration](#environment-based-configuration) - [Databases & Migrations](#databases-and-migrations) + - [Directory Configuration](#directory-configuration) - [Next Steps](#next-steps) - [Laravel The Full Stack Framework](#laravel-the-fullstack-framework) - [Laravel The API Backend](#laravel-the-api-backend) @@ -244,6 +245,11 @@ Once you have configured your SQLite database, you may run your application's [d php artisan migrate ``` + +### Directory Configuration + +Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files present within your application. + ## Next Steps From 4cfda2cba02b1352f091dc53a08117478129e34f Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 27 Apr 2023 17:17:47 +0200 Subject: [PATCH 0904/2609] Remove extra section about subscribedToPlan (#8752) --- cashier-paddle.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index d669ed67477..8e6e6214904 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -590,12 +590,6 @@ The `subscribedToPlan` method may be used to determine if the user is subscribed // ... } -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: - - if ($user->subscribedToPlan([$monthly = 12345, $yearly = 54321], 'default')) { - // ... - } - The `recurring` method may be used to determine if the user is currently subscribed and is no longer within their trial period: if ($user->subscription('default')->recurring()) { From fd9e0a7d3d3c7fcfa83cb8156c5fa40c9b390aef Mon Sep 17 00:00:00 2001 From: Serhii Shevchenko Date: Mon, 1 May 2023 18:44:49 +0300 Subject: [PATCH 0905/2609] [10.x] add a small tip on how to clear the config cache (#8757) * add a small tip on how to clear the config cache * Update configuration.md --------- Co-authored-by: Taylor Otwell --- configuration.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configuration.md b/configuration.md index 758f3bb4358..d94fcb9e2b4 100644 --- a/configuration.md +++ b/configuration.md @@ -201,6 +201,12 @@ Once the configuration has been cached, your application's `.env` file will not For this reason, you should ensure you are only calling the `env` function from within your application's configuration (`config`) files. You can see many examples of this by examining Laravel's default configuration files. Configuration values may be accessed from anywhere in your application using the `config` function [described above](#accessing-configuration-values). +The `config:clear` command may be used to purge the cached configuration: + +```shell +php artisan config:clear +``` + > **Warning** > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. From 25b1963e058af55dde868a79091b99fda960ba9a Mon Sep 17 00:00:00 2001 From: Tom Westrick Date: Mon, 1 May 2023 11:50:55 -0400 Subject: [PATCH 0906/2609] Fix channel prefix for presence channel (#8753) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 4e5c90e3aba..cfd777c2441 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -878,7 +878,7 @@ Presence channels may receive events just like public or private channels. Using public function broadcastOn(): array { return [ - new PresenceChannel('room.'.$this->message->room_id), + new PresenceChannel('chat.'.$this->message->room_id), ]; } From 8d387d7ca07d53b72514892e43952d6047b42f0e Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Mon, 1 May 2023 22:55:54 +0330 Subject: [PATCH 0907/2609] Remove Importing 'Illuminate\Support\Str' (#8759) --- validation.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/validation.md b/validation.md index 26c68acd353..bbf47d5082c 100644 --- a/validation.md +++ b/validation.md @@ -523,8 +523,6 @@ If you need to prepare or sanitize any data from the request before you apply yo Likewise, if you need to normalize any request data after validation is complete, you may use the `passedValidation` method: - use Illuminate\Support\Str; - /** * Handle a passed validation attempt. */ From 71580575c62148d51d929442f21d9dadfd47b90f Mon Sep 17 00:00:00 2001 From: zobay Date: Tue, 2 May 2023 17:32:13 +0600 Subject: [PATCH 0908/2609] update doc block to make consistant with core Laravel application's implimentaion --- events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.md b/events.md index b328bcfa5c7..c98c8c0a438 100644 --- a/events.md +++ b/events.md @@ -37,7 +37,7 @@ The `App\Providers\EventServiceProvider` included with your Laravel application /** * The event listener mappings for the application. * - * @var array + * @var array> */ protected $listen = [ OrderShipped::class => [ From 950bd198ac67717a3aea0ddc8da57a9cbdb2783a Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 3 May 2023 00:19:42 +0330 Subject: [PATCH 0909/2609] add correct ExampleTest in http-tests (#8763) --- http-tests.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/http-tests.md b/http-tests.md index 7c21defe897..6fa81030eac 100644 --- a/http-tests.md +++ b/http-tests.md @@ -22,26 +22,28 @@ Laravel provides a very fluent API for making HTTP requests to your application and examining the responses. For example, take a look at the feature test defined below: - get('/'); + $response = $this->get('/'); - $response->assertStatus(200); - } + $response->assertStatus(200); } +} +``` The `get` method makes a `GET` request into the application, while the `assertStatus` method asserts that the returned response should have the given HTTP status code. In addition to this simple assertion, Laravel also contains a variety of assertions for inspecting the response headers, content, JSON structure, and more. From 47d1649b639f65a9c67c32d0cfd135d144d0615a Mon Sep 17 00:00:00 2001 From: Jeremy Schoonover Date: Tue, 2 May 2023 15:52:43 -0500 Subject: [PATCH 0910/2609] Update dynamic components example... (#8762) * Update dynamic components example... I was tripped up on the what format the dynamic component string needed to be, so I thought I'd provide a little more context in the example. * formatting --------- Co-authored-by: Taylor Otwell --- blade.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blade.md b/blade.md index ad5f784ebf7..73b5ecdc825 100644 --- a/blade.md +++ b/blade.md @@ -1206,6 +1206,8 @@ php artisan make:component Alert --inline Sometimes you may need to render a component but not know which component should be rendered until runtime. In this situation, you may use Laravel's built-in `dynamic-component` component to render the component based on a runtime value or variable: ```blade +// $componentName = "secondary-button"; + ``` From 15053e1c0e6b0fa495f3a68d9ab4c148bdb63cdf Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 3 May 2023 17:22:16 +0330 Subject: [PATCH 0911/2609] add missing `-p` option in generate model (#8765) --- eloquent.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent.md b/eloquent.md index 9cb988a082d..bf88f285d29 100644 --- a/eloquent.md +++ b/eloquent.md @@ -95,6 +95,7 @@ php artisan make:model Flight --all # Generate a pivot model... php artisan make:model Member --pivot +php artisan make:model Member -p ``` From 75b7c9579cffa658460307d7f8abbf3c3ac3c9a8 Mon Sep 17 00:00:00 2001 From: Aaron Parecki Date: Wed, 3 May 2023 06:52:44 -0700 Subject: [PATCH 0912/2609] Update eloquent-resources.md (#8764) remove useless sentence "In essence, resources are simple." --- eloquent-resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index e9efe67e293..7bd187198c4 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -199,7 +199,7 @@ For example, `UserCollection` will attempt to map the given user instances into > **Note** > If you have not read the [concept overview](#concept-overview), you are highly encouraged to do so before proceeding with this documentation. -In essence, resources are simple. They only need to transform a given model into an array. So, each resource contains a `toArray` method which translates your model's attributes into an API friendly array that can be returned from your application's routes or controllers: +Resources only need to transform a given model into an array. So, each resource contains a `toArray` method which translates your model's attributes into an API friendly array that can be returned from your application's routes or controllers: Date: Wed, 3 May 2023 21:51:17 +0330 Subject: [PATCH 0913/2609] [10.x] Add `assertGone` in http-tests (#8767) * add assertGone in http-tests * Update http-tests.md --------- Co-authored-by: Taylor Otwell --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 6fa81030eac..428e43f7ba4 100644 --- a/http-tests.md +++ b/http-tests.md @@ -623,6 +623,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertExactJson](#assert-exact-json) [assertForbidden](#assert-forbidden) [assertFound](#assert-found) +[assertGone](#assert-gone) [assertHeader](#assert-header) [assertHeaderMissing](#assert-header-missing) [assertJson](#assert-json) @@ -782,6 +783,13 @@ Assert that the response has a found (302) HTTP status code: $response->assertFound(); + +#### assertGone + +Assert that the response has a gone (410) HTTP status code: + + $response->assertGone(); + #### assertHeader From 0e26da4eed737d2c20b64df1ce9e2a39f7e174ff Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 3 May 2023 22:48:37 +0330 Subject: [PATCH 0914/2609] [10.x] Add missing asserts in http tests (#8769) * add `assertInternalServerError` into http-tests * add `assertServiceUnavailable` in http-tests * fix description for `assertServiceUnavailable` and `assertInternalServerError` * fix `assertServerError` --- http-tests.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/http-tests.md b/http-tests.md index 428e43f7ba4..b3415b37074 100644 --- a/http-tests.md +++ b/http-tests.md @@ -626,6 +626,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertGone](#assert-gone) [assertHeader](#assert-header) [assertHeaderMissing](#assert-header-missing) +[assertInternalServerError](#assert-internal-server-error) [assertJson](#assert-json) [assertJsonCount](#assert-json-count) [assertJsonFragment](#assert-json-fragment) @@ -658,6 +659,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertSeeText](#assert-see-text) [assertSeeTextInOrder](#assert-see-text-in-order) [assertServerError](#assert-server-error) +[assertServiceUnavailable](#assert-server-unavailable) [assertSessionHas](#assert-session-has) [assertSessionHasInput](#assert-session-has-input) [assertSessionHasAll](#assert-session-has-all) @@ -804,6 +806,13 @@ Assert that the given header is not present on the response: $response->assertHeaderMissing($headerName); + +#### assertInternalServerError + +Assert that the response has an "Internal Server Error" (500) HTTP status code: + + $response->assertInternalServerError(); + #### assertJson @@ -1123,6 +1132,13 @@ Assert that the response has a server error (>= 500 , < 600) HTTP status code: $response->assertServerError(); + +#### assertServiceUnavailable + +Assert that the response has a "Service Unavailable" (503) HTTP status code: + + $response->assertServiceUnavailable(); + #### assertSessionHas From 84bd3f4c5f721769024c93529e748d55d867d7e7 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Wed, 3 May 2023 15:45:14 -0400 Subject: [PATCH 0915/2609] Warning about Http::pool() (#8770) * Warning about Http::pool() * formatting --------- Co-authored-by: Taylor Otwell --- http-client.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/http-client.md b/http-client.md index d7974ac5ed4..cb9b7aa2631 100644 --- a/http-client.md +++ b/http-client.md @@ -385,6 +385,26 @@ As you can see, each response instance can be accessed based on the order it was return $responses['first']->ok(); + +#### Customizing Concurrent Requests + +The `pool` method cannot be chained with other HTTP client methods such as the `withHeaders` or `middleware` methods. If you want to apply custom headers or middleware to pooled requests, you should configure those options on each request in the pool: + +```php +use Illuminate\Http\Client\Pool; +use Illuminate\Support\Facades\Http; + +$headers = [ + 'X-Example' => 'example', +]; + +$responses = Http::pool(fn (Pool $pool) => [ + $pool->withHeaders($headers)->get('http://laravel.test/test'), + $pool->withHeaders($headers)->get('http://laravel.test/test'), + $pool->withHeaders($headers)->get('http://laravel.test/test'), +]); +``` + ## Macros From 1d8b5532c10da2e26ebece33f9dfe1b9619ee39c Mon Sep 17 00:00:00 2001 From: Julius Kiekbusch Date: Thu, 4 May 2023 17:00:52 +0200 Subject: [PATCH 0916/2609] Remove sail PHP 7.4 support (#8774) --- sail.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sail.md b/sail.md index f19291673df..118e20bf3c1 100644 --- a/sail.md +++ b/sail.md @@ -362,7 +362,7 @@ sail tinker ## PHP Versions -Sail currently supports serving your application via PHP 8.2, 8.1, PHP 8.0, or PHP 7.4. The default PHP version used by Sail is currently PHP 8.2. 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: +Sail currently supports serving your application via PHP 8.2, 8.1, or PHP 8.0. The default PHP version used by Sail is currently PHP 8.2. 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.2 @@ -373,9 +373,6 @@ context: ./vendor/laravel/sail/runtimes/8.1 # 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: From 5f39f7aee14122aaacd060264a774c905f01d934 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Thu, 4 May 2023 18:31:32 +0330 Subject: [PATCH 0917/2609] [10.x] Add default value for `assertRedirect` & `assertSessionHasErrors` (#8772) * add default value for `assertRedirect` * add default value for `assertSessionHasErrors` --- http-tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-tests.md b/http-tests.md index b3415b37074..7472e067d2f 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1067,7 +1067,7 @@ Assert that the response contains the given unencrypted cookie: Assert that the response is a redirect to the given URI: - $response->assertRedirect($uri); + $response->assertRedirect($uri = null); #### assertRedirectContains @@ -1185,7 +1185,7 @@ For example, if your application's session contains `name` and `status` keys, yo Assert that the session contains an error for the given `$keys`. If `$keys` is an associative array, assert that the session contains a specific error message (value) for each field (key). This method should be used when testing routes that flash validation errors to the session instead of returning them as a JSON structure: $response->assertSessionHasErrors( - array $keys, $format = null, $errorBag = 'default' + array $keys = [], $format = null, $errorBag = 'default' ); For example, to assert that the `name` and `email` fields have validation error messages that were flashed to the session, you may invoke the `assertSessionHasErrors` method like so: From 0a76114d7303e4a01ff86a1695975e7c7fba6d09 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 5 May 2023 15:09:23 -0500 Subject: [PATCH 0918/2609] wip --- errors.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/errors.md b/errors.md index fee23227e02..6c356ff8ae4 100644 --- a/errors.md +++ b/errors.md @@ -29,9 +29,9 @@ During local development, you should set the `APP_DEBUG` environment variable to ### Reporting Exceptions -All exceptions are handled by the `App\Exceptions\Handler` class. This class contains a `register` method where you may register custom exception reporting and rendering callbacks. We'll examine each of these concepts in detail. Exception reporting is used to log exceptions or send them to an external service like [Flare](https://flareapp.io), [Bugsnag](https://bugsnag.com) or [Sentry](https://github.com/getsentry/sentry-laravel). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. +All exceptions are handled by the `App\Exceptions\Handler` class. This class contains a `register` method where you may register custom exception reporting and rendering callbacks. We'll examine each of these concepts in detail. Exception reporting is used to log exceptions or send them to an external service like [Flare](https://flareapp.io), [Bugsnag](https://bugsnag.com), or [Sentry](https://github.com/getsentry/sentry-laravel). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. -For example, if you need to report different types of exceptions in different ways, you may use the `reportable` method to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will deduce what type of exception the closure reports by examining the type-hint of the closure: +If you need to report different types of exceptions in different ways, you may use the `reportable` method to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will determine what type of exception the closure reports by examining the type-hint of the closure: use App\Exceptions\InvalidOrderException; @@ -61,7 +61,7 @@ When you register a custom exception reporting callback using the `reportable` m #### Global Log Context -If available, Laravel automatically adds the current user's ID to every exception's log message as contextual data. You may define your own global contextual data by overriding the `context` method of your application's `App\Exceptions\Handler` class. This information will be included in every exception's log message written by your application: +If available, Laravel automatically adds the current user's ID to every exception's log message as contextual data. You may define your own global contextual data by defining a `context` method on your application's `App\Exceptions\Handler` class. This information will be included in every exception's log message written by your application: /** * Get the default context variables for logging. @@ -78,7 +78,7 @@ If available, Laravel automatically adds the current user's ID to every exceptio #### Exception Log Context -While adding context to every log message can be useful, sometimes a particular exception may have unique context that you would like to include in your logs. By defining a `context` method on one of your application's custom exceptions, you may specify any data relevant to that exception that should be added to the exception's log entry: +While adding context to every log message can be useful, sometimes a particular exception may have unique context that you would like to include in your logs. By defining a `context` method on one of your application's exceptions, you may specify any data relevant to that exception that should be added to the exception's log entry: ### Ignoring Exceptions By Type -When building your application, there will be some types of exceptions you simply want to ignore and never report. To ignore these exceptions, define a `$dontReport` property on your application's exception handler. Any classes that you add to this property will never be reported; however, they may still have custom rendering logic: +When building your application, there will be some types of exceptions you never want to report. To ignore these exceptions, define a `$dontReport` property on your application's exception handler. Any classes that you add to this property will never be reported; however, they may still have custom rendering logic: use App\Exceptions\InvalidOrderException; @@ -173,7 +173,7 @@ Internally, Laravel already ignores some types of errors for you, such as except By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by invoking the `renderable` method within your exception handler. -The closure passed to the `renderable` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will deduce what type of exception the closure renders by examining the type-hint of the closure: +The closure passed to the `renderable` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will determine what type of exception the closure renders by examining the type-hint of the closure: use App\Exceptions\InvalidOrderException; use Illuminate\Http\Request; @@ -210,7 +210,7 @@ You may also use the `renderable` method to override the rendering behavior for ### Reportable & Renderable Exceptions -Instead of defining custom reporting and rendering behavior in your exception handler's `register` method, you may define `report` and `render` methods directly on your custom exceptions. When these methods exist, they will be automatically called by the framework: +Instead of defining custom reporting and rendering behavior in your exception handler's `register` method, you may define `report` and `render` methods directly on your application's exceptions. When these methods exist, they will automatically be called by the framework: ## HTTP Exceptions -Some exceptions describe HTTP error codes from the server. For example, this may be a "page not found" error (404), an "unauthorized error" (401) or even a developer generated 500 error. In order to generate such a response from anywhere in your application, you may use the `abort` helper: +Some exceptions describe HTTP error codes from the server. For example, this may be a "page not found" error (404), an "unauthorized error" (401), or even a developer generated 500 error. In order to generate such a response from anywhere in your application, you may use the `abort` helper: abort(404); ### Custom HTTP Error Pages -Laravel makes it easy to display custom error pages for various HTTP status codes. For example, if you wish to customize the error page for 404 HTTP status codes, create a `resources/views/errors/404.blade.php` view template. This view will be rendered on all 404 errors generated by your application. The views within this directory should be named to match the HTTP status code they correspond to. The `Symfony\Component\HttpKernel\Exception\HttpException` instance raised by the `abort` function will be passed to the view as an `$exception` variable: +Laravel makes it easy to display custom error pages for various HTTP status codes. For example, to customize the error page for 404 HTTP status codes, create a `resources/views/errors/404.blade.php` view template. This view will be rendered for all 404 errors generated by your application. The views within this directory should be named to match the HTTP status code they correspond to. The `Symfony\Component\HttpKernel\Exception\HttpException` instance raised by the `abort` function will be passed to the view as an `$exception` variable:

    {{ $exception->getMessage() }}

    From bbd235c4b0551fc5936fd374f89a63aba13bd536 Mon Sep 17 00:00:00 2001 From: Matt Stauffer Date: Sat, 6 May 2023 12:03:27 -0400 Subject: [PATCH 0919/2609] Replace 'append' with 'prepend' in Vite example. (#8777) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 6a2a0ee7d8f..2950b430a67 100644 --- a/vite.md +++ b/vite.md @@ -808,7 +808,7 @@ For example, the `vite-imagetools` plugin outputs URLs like the following while The `vite-imagetools` plugin is expecting that the output URL will be intercepted by Vite and the plugin may then handle all URLs that start with `/@imagetools`. If you are using plugins that are expecting this behaviour, you will need to manually correct the URLs. You can do this in your `vite.config.js` file by using the `transformOnServe` option. -In this particular example, we will append the dev server URL to all occurrences of `/@imagetools` within the generated code: +In this particular example, we will prepend the dev server URL to all occurrences of `/@imagetools` within the generated code: ```js import { defineConfig } from 'vite'; From da4c6f5c00bdc8c4ba109a239a33592f5b1f82d9 Mon Sep 17 00:00:00 2001 From: Ekrow Date: Mon, 8 May 2023 15:14:47 +0200 Subject: [PATCH 0920/2609] Update notifications.md (#8780) replace unused 'format shortcode' anchor link --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 948efb2a0c7..f8a4931969a 100644 --- a/notifications.md +++ b/notifications.md @@ -36,7 +36,7 @@ - [SMS Notifications](#sms-notifications) - [Prerequisites](#sms-prerequisites) - [Formatting SMS Notifications](#formatting-sms-notifications) - - [Formatting Shortcode Notifications](#formatting-shortcode-notifications) + - [Unicode Content](#unicode-content) - [Customizing The "From" Number](#customizing-the-from-number) - [Adding A Client Reference](#adding-a-client-reference) - [Routing SMS Notifications](#routing-sms-notifications) From d212c590ba202fa69b7dadf26295664f71a79791 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 9 May 2023 08:29:16 -0500 Subject: [PATCH 0921/2609] document hashed cast --- eloquent-mutators.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 4ab1def965c..ba0ba1a33d2 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -217,6 +217,7 @@ The `$casts` property should be an array where the key is the name of the attrib - `encrypted:collection` - `encrypted:object` - `float` +- `hashed` - `integer` - `object` - `real` From 02670c9556c52c976aa908e22778f8d76c35b277 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 9 May 2023 08:31:09 -0500 Subject: [PATCH 0922/2609] sortRecursiveDesc documentation --- helpers.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/helpers.md b/helpers.md index d06be678d56..619ff3d5d17 100644 --- a/helpers.md +++ b/helpers.md @@ -65,6 +65,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::sort](#method-array-sort) [Arr::sortDesc](#method-array-sort-desc) [Arr::sortRecursive](#method-array-sort-recursive) +[Arr::sortRecursiveDesc](#method-array-sort-recursive-desc) [Arr::toCssClasses](#method-array-to-css-classes) [Arr::undot](#method-array-undot) [Arr::where](#method-array-where) @@ -967,6 +968,10 @@ The `Arr::sortRecursive` method recursively sorts an array using the `sort` func ] */ +If you would like the results sorted in descending order, you may use the `Arr::sortRecursiveDesc` method. + + $sorted = Arr::sortRecursiveDesc($array); + #### `Arr::toCssClasses()` {.collection-method} From ca00f334107e6b5699f985bcd832d6d07b3bb747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Debrauwer?= Date: Tue, 9 May 2023 15:49:55 +0200 Subject: [PATCH 0923/2609] [10.x] Scout - Document 'makeSearchableUsing' method (#8749) * Document 'makeSearchableUsing' * formatting --------- Co-authored-by: Taylor Otwell --- scout.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scout.md b/scout.md index 56075dfddeb..db840899928 100644 --- a/scout.md +++ b/scout.md @@ -462,6 +462,21 @@ Or, if you already have a collection of Eloquent models in memory, you may call $orders->searchable(); + +#### Modifying Records Before Importing + +Sometimes you may need to prepare the collection of models before they are made searchable. For instance, you may want to eager load a relationship so that the relationship data can be efficiently added to your search index. To accomplish this, define a `makeSearchableUsing` method on the corresponding model: + + use Illuminate\Database\Eloquent\Collection; + + /** + * Modify the collection of models being made searchable. + */ + protected function makeSearchableUsing(Collection $models): Collection + { + return $query->load('author'); + } + ### Removing Records From b4d7a5cfcf1bc3a8a11977257f14514f36d45247 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 10 May 2023 00:08:05 +1000 Subject: [PATCH 0924/2609] [10.x] Sleep (#8775) * wip * wip * wip * wip * formatting --------- Co-authored-by: Taylor Otwell --- helpers.md | 161 +++++++++++++++++++++++++++++++++++++++++++---------- pennant.md | 2 +- 2 files changed, 134 insertions(+), 29 deletions(-) diff --git a/helpers.md b/helpers.md index 619ff3d5d17..815342e674d 100644 --- a/helpers.md +++ b/helpers.md @@ -4,8 +4,9 @@ - [Available Methods](#available-methods) - [Other Utilities](#other-utilities) - [Benchmarking](#benchmarking) - - [Pipeline](#pipeline) - [Lottery](#lottery) + - [Pipeline](#pipeline) + - [Sleep](#sleep) ## Introduction @@ -4188,6 +4189,46 @@ To invoke a callback more than once, you may specify the number of iterations th Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms + +### Lottery + +Laravel's lottery class may be used to execute callbacks based on a set of given odds. This can be particularly useful when you only want to execute code for a percentage of your incoming requests: + + use Illuminate\Support\Lottery; + + Lottery::odds(1, 20) + ->winner(fn () => $user->won()) + ->loser(fn () => $user->lost()) + ->choose(); + +You may combine Laravel's lottery class with other Laravel features. For example, you may wish to only report a small percentage of slow queries to your exception handler. And, since the lottery class is callable, we may pass an instance of the class into any method that accepts callables: + + use Carbon\CarbonInterval; + use Illuminate\Support\Facades\DB; + use Illuminate\Support\Lottery; + + DB::whenQueryingForLongerThan( + CarbonInterval::seconds(2), + Lottery::odds(1, 100)->winner(fn () => report('Querying > 2 seconds.')), + ); + + +#### Testing Lotteries + +Laravel provides some simple methods to allow you to easily test your application's lottery invocations: + + // Lottery will always win... + Lottery::alwaysWin(); + + // Lottery will always lose... + Lottery::alwaysLose(); + + // Lottery will win then lose, and finally return to normal behavior... + Lottery::fix([true, false]); + + // Lottery will return to normal behavior... + Lottery::determineResultsNormally(); + ### Pipeline @@ -4230,42 +4271,106 @@ $user = Pipeline::send($user) ->then(fn (User $user) => $user); ``` - -### Lottery + +### Sleep -Laravel's lottery class may be used to execute callbacks based on a set of given odds. This can be particularly useful when you only want to execute code for a percentage of your incoming requests: +Laravel's `Sleep` class is a light-weight wrapper around PHP's native `sleep` and `usleep` functions, offering greater testability while also exposing a developer friendly API for working with time: - use Illuminate\Support\Lottery; + use Illuminate\Support\Sleep; - Lottery::odds(1, 20) - ->winner(fn () => $user->won()) - ->loser(fn () => $user->lost()) - ->choose(); + $waiting = true; -You may combine Laravel's lottery class with other Laravel features. For example, you may wish to only report a small percentage of slow queries to your exception handler. And, since the lottery class is callable, we may pass an instance of the class into any method that accepts callables: + while ($waiting) { + Sleep::for(1)->second(); - use Carbon\CarbonInterval; - use Illuminate\Support\Facades\DB; - use Illuminate\Support\Lottery; + $waiting = /* ... */; + } - DB::whenQueryingForLongerThan( - CarbonInterval::seconds(2), - Lottery::odds(1, 100)->winner(fn () => report('Querying > 2 seconds.')), - ); +The `Sleep` class offers a variety of methods that allow you to work with different units of time: - -#### Testing Lotteries + // Pause execution for 90 seconds... + Sleep::for(1.5)->minutes(); -Laravel provides some simple methods to allow you to easily test your application's lottery invocations: + // Pause execution for 2 seconds... + Sleep::for(2)->seconds(); - // Lottery will always win... - Lottery::alwaysWin(); + // Pause execution for 500 milliseconds... + Sleep::for(500)->milliseconds(); - // Lottery will always lose... - Lottery::alwaysLose(); + // Pause execution for 5,000 microseconds... + Sleep::for(5000)->microseconds(); - // Lottery will win then lose, and finally return to normal behavior... - Lottery::fix([true, false]); + // Pause execution until a given time... + Sleep::until(now()->addMinute()); - // Lottery will return to normal behavior... - Lottery::determineResultsNormally(); + // Alias of PHP's native "sleep" function... + Sleep::sleep(2); + + // Alias of PHP's native "usleep" function... + Sleep::usleep(5000); + +To easily combine units of time, you may use the `and` method: + + Sleep::for(1)->second()->and(10)->milliseconds(); + + +#### Testing Sleep + +When testing code that utilizes the `Sleep` class or PHP's native sleep functions, your test will pause execution. As you might expect, this makes your test suite significantly slower. For example, imagine you are testing the following code: + + $waiting = /* ... */; + + $seconds = 1; + + while ($waiting) { + Sleep::for($seconds++)->seconds(); + + $waiting = /* ... */; + } + +Typically, testing this code would take _at least_ one second. Luckily, the `Sleep` class allows us to "fake" sleeping so that our test suite stays fast: + + public function test_it_waits_until_ready() + { + Sleep::fake(); + + // ... + } + +When faking the `Sleep` class, the actual execution pause is by-passed, leading to a substantially faster test. + +Once the `Sleep` class has been faked, it is possible to make assertions against the expected "sleeps" that should have occurred. To illustrate this, let's imagine we are testing code that pauses execution three times, with each pause increasing by a single second. Using the `assertSequence` method, we can assert that our code "slept" for the proper amount of time while keeping our test fast: + + public function test_it_checks_if_ready_four_times() + { + Sleep::fake(); + + // ... + + Sleep::assertSequence([ + Sleep::for(1)->second(), + Sleep::for(2)->seconds(), + Sleep::for(3)->seconds(), + ]); + } + +Of course, the `Sleep` class offers a variety of other assertions you may use when testing: + + use Carbon\CarbonInterval as Duration; + use Illuminate\Support\Sleep; + + // Assert that sleep was called 3 times... + Sleep::assertSleptTimes(3); + + // Assert against the duration of sleep... + Sleep::assertSlept(function (Duration $duration): bool { + return /* ... */; + }, times: 1); + + // Assert that the Sleep class was never invoked... + Sleep::assertNeverSlept(); + + // Assert that, even if Sleep was called, no execution paused occurred... + Sleep::assertInsomniac(); + +Laravel uses the `Sleep` class under the hood whenever it is pausing execution. For example, the [`retry`](#method-retry) helper uses the `Sleep` class when sleeping, allowing for improved testability when using that helper. diff --git a/pennant.md b/pennant.md index 6419541fec8..31b8b91f159 100644 --- a/pennant.md +++ b/pennant.md @@ -31,7 +31,7 @@ ## Introduction -[Laravel Pennant](https://github.com/laravel/pennant) is a simple and lightweight feature flag package - without the cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. +[Laravel Pennant](https://github.com/laravel/pennant) is a simple and light-weight feature flag package - without the cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. ## Installation From 4f4fdd0ca6794a2f844087ce936cb98b9325ec40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=CC=88nther=20Debrauwer?= Date: Wed, 10 May 2023 11:39:14 +0200 Subject: [PATCH 0925/2609] Public instead of protected --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index db840899928..5e608799d27 100644 --- a/scout.md +++ b/scout.md @@ -472,7 +472,7 @@ Sometimes you may need to prepare the collection of models before they are made /** * Modify the collection of models being made searchable. */ - protected function makeSearchableUsing(Collection $models): Collection + public function makeSearchableUsing(Collection $models): Collection { return $query->load('author'); } From 45fb04d011a04b107f75abe1f3c63d6ece7e7e0b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 10 May 2023 13:29:57 -0500 Subject: [PATCH 0926/2609] wip --- vite.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vite.md b/vite.md index 2950b430a67..2b75c3bbffa 100644 --- a/vite.md +++ b/vite.md @@ -261,7 +261,7 @@ export default defineConfig({ ### Vue -If you would like to build your front-end using the [Vue](https://vuejs.org/) framework, then you will also need to install the `@vitejs/plugin-vue` plugin: +If you would like to build your frontend using the [Vue](https://vuejs.org/) framework, then you will also need to install the `@vitejs/plugin-vue` plugin: ```sh npm install --save-dev @vitejs/plugin-vue @@ -305,7 +305,7 @@ export default defineConfig({ ### React -If you would like to build your front-end using the [React](https://reactjs.org/) framework, then you will also need to install the `@vitejs/plugin-react` plugin: +If you would like to build your frontend using the [React](https://reactjs.org/) framework, then you will also need to install the `@vitejs/plugin-react` plugin: ```sh npm install --save-dev @vitejs/plugin-react From 5f4d91ba6425574a7db118710bc72b6f0a60e40f Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 11 May 2023 04:11:26 +0100 Subject: [PATCH 0927/2609] Typo fix (#8784) --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index 5e608799d27..5e5f65bba59 100644 --- a/scout.md +++ b/scout.md @@ -300,7 +300,7 @@ Scout also allows you to auto identify users when using [Algolia](https://algoli SCOUT_IDENTIFY=true ``` -Enabling this feature this will also pass the request's IP address and your authenticated user's primary identifier to Algolia so this data is associated with any search request that is made by the user. +Enabling this feature will also pass the request's IP address and your authenticated user's primary identifier to Algolia so this data is associated with any search request that is made by the user. ## Database / Collection Engines From 5ffb4245affee31849441549b9300ce72620ab82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20M?= Date: Fri, 12 May 2023 17:49:21 +0200 Subject: [PATCH 0928/2609] Fix makeSearchableUsing using $query instead of $models (#8785) * It should use Builder + remove unneeded Meilisearch dep * http-interop/http-factory-guzzle is required * Change $query to $models * Fix return type --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index 5e5f65bba59..33b9099199e 100644 --- a/scout.md +++ b/scout.md @@ -474,7 +474,7 @@ Sometimes you may need to prepare the collection of models before they are made */ public function makeSearchableUsing(Collection $models): Collection { - return $query->load('author'); + return $models->load('author'); } From 505747d5733ec9f62c2b247cfe720e06be43b29f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 12 May 2023 13:37:46 -0500 Subject: [PATCH 0929/2609] update docs for increment and decrement cache initialization --- cache.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cache.md b/cache.md index 4f6ceb302e0..fce32328098 100644 --- a/cache.md +++ b/cache.md @@ -160,6 +160,10 @@ The `has` method may be used to determine if an item exists in the cache. This m The `increment` and `decrement` methods may be used to adjust the value of integer items in the cache. Both of these methods accept an optional second argument indicating the amount by which to increment or decrement the item's value: + // Initialize the value if it does not exist... + Cache::add('key', 0, now()->addHours(4)); + + // Increment or decrement the value... Cache::increment('key'); Cache::increment('key', $amount); Cache::decrement('key'); From eda5c2b069d587a896d345ebfea75927e256cba7 Mon Sep 17 00:00:00 2001 From: Ransom Pate Date: Mon, 15 May 2023 11:47:43 -0400 Subject: [PATCH 0930/2609] [10.x] Add warnings to scout and queue pages to clarify functionality (#8787) * Add warnings to scout and queue pages that indicate an unsupported use case * wording * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 2 ++ scout.md | 3 +++ 2 files changed, 5 insertions(+) diff --git a/queues.md b/queues.md index 5ea19b01791..887f968c7de 100644 --- a/queues.md +++ b/queues.md @@ -238,6 +238,8 @@ Because loaded relationships also get serialized, the serialized job string can Furthermore, when a job is deserialized and model relationships are re-retrieved from the database, they will be retrieved in their entirety. Any previous relationship constraints that were applied before the model was serialized during the job queueing process will not be applied when the job is deserialized. Therefore, if you wish to work with a subset of a given relationship, you should re-constrain that relationship within your queued job. +If a job receives a collection or array of Eloquent models instead of a single model, the models within that collection will not have their relationships restored when the job is deserialized and executed. This is to prevent excessive resource usage on jobs that deal with large numbers of models. + ### Unique Jobs diff --git a/scout.md b/scout.md index 33b9099199e..a72136ac408 100644 --- a/scout.md +++ b/scout.md @@ -404,6 +404,9 @@ If you would like to modify the query that is used to retrieve all of your model return $query->with('author'); } +> **Warning** +> The `makeAllSearchableUsing` method may not be applicable when using a queue to batch import models. Relationships are [not restored](/docs/{{version}}/queues#handling-relationships) when model collections are processed by jobs. + ### Adding Records From df55689667ac21e48bf92ab1b9368845ca52c2d7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 15 May 2023 14:30:59 -0500 Subject: [PATCH 0931/2609] wip --- helpers.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/helpers.md b/helpers.md index 815342e674d..b3bd954846d 100644 --- a/helpers.md +++ b/helpers.md @@ -4,6 +4,7 @@ - [Available Methods](#available-methods) - [Other Utilities](#other-utilities) - [Benchmarking](#benchmarking) + - [Dates](#dates) - [Lottery](#lottery) - [Pipeline](#pipeline) - [Sleep](#sleep) @@ -4189,6 +4190,25 @@ To invoke a callback more than once, you may specify the number of iterations th Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms + +### Dates + +Laravel includes [Carbon](https://carbon.nesbot.com/docs/), a powerful date and time manipulation library. To create a new `Carbon` instance, you may invoke the `now` function. This function is globally available within your Laravel application: + +```php +$now = now(); +``` + +Or, you may create a new `Carbon` instance using the `Illuminate\Support\Carbon` class: + +```php +use Illuminate\Support\Carbon; + +$now = Carbon::now(); +``` + +For a thorough discussion of Carbon and its features, please consult the [official Carbon documentation](https://carbon.nesbot.com/docs/). + ### Lottery From 2ca5e5fe9281cbe1359655effab49fbe4b47065d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 May 2023 09:23:17 -0500 Subject: [PATCH 0932/2609] document arr map with keys --- helpers.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/helpers.md b/helpers.md index b3bd954846d..a8e50294d6b 100644 --- a/helpers.md +++ b/helpers.md @@ -55,6 +55,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::keyBy](#method-array-keyby) [Arr::last](#method-array-last) [Arr::map](#method-array-map) +[Arr::mapWithKeys](#method-array-map-with-keys) [Arr::only](#method-array-only) [Arr::pluck](#method-array-pluck) [Arr::prepend](#method-array-prepend) @@ -711,6 +712,37 @@ The `Arr::map` method iterates through the array and passes each value and key t // ['first' => 'James', 'last' => 'Kirk'] + +#### `Arr::mapWithKeys()` {.collection-method} + +The `Arr::mapWithKeys` method iterates through the array and passes each value to the given callback. The callback should return an associative array containing a single key / value pair: + + use Illuminate\Support\Arr; + + $array = [ + [ + 'name' => 'John', + 'department' => 'Sales', + 'email' => 'john@example.com', + ], + [ + 'name' => 'Jane', + 'department' => 'Marketing', + 'email' => 'jane@example.com', + ] + ]; + + $mapped = Arr::mapWithKeys(function (array $item, int $key) { + return [$item['email'] => $item['name']]; + }); + + /* + [ + 'john@example.com' => 'John', + 'jane@example.com' => 'Jane', + ] + */ + #### `Arr::only()` {.collection-method} From 3b21410ec23f43ea3e3ece26435370dba1a875dd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 May 2023 09:26:28 -0500 Subject: [PATCH 0933/2609] document createFromId carbon method --- helpers.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index a8e50294d6b..3205f27faf8 100644 --- a/helpers.md +++ b/helpers.md @@ -2139,7 +2139,7 @@ The `Str::upper` method converts the given string to uppercase: #### `Str::ulid()` {.collection-method} -The `Str::ulid` method generates a ULID: +The `Str::ulid` method generates a ULID, which is a compact, time-ordered unique identifier: use Illuminate\Support\Str; @@ -2147,6 +2147,15 @@ The `Str::ulid` method generates a ULID: // 01gd6r360bp37zj17nxb55yv40 +If you would like to retrieve a `Illuminate\Support\Carbon` date instance representing the date and time that a given ULID was created, you may use the `createFromId` method provided by Laravel's Carbon integration: + +```php +use Illuminate\Support\Carbon; +use Illuminate\Support\Str; + +$date = Carbon::createFromId((string) Str::ulid()); +``` + #### `Str::uuid()` {.collection-method} From aa4f1cfddb8a39fdca7a4c1090698768c01d4985 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 19 May 2023 08:34:20 -0500 Subject: [PATCH 0934/2609] Fixes roadrunner installation on sail (#8796) --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index b9f03224c7c..81f8c0ab322 100644 --- a/octane.md +++ b/octane.md @@ -62,7 +62,7 @@ If you plan to develop your application using [Laravel Sail](/docs/{{version}}/s ```shell ./vendor/bin/sail up -./vendor/bin/sail composer require laravel/octane spiral/roadrunner +./vendor/bin/sail composer require laravel/octane spiral/roadrunner-cli spiral/roadrunner-http ``` Next, you should start a Sail shell and use the `rr` executable to retrieve the latest Linux based build of the RoadRunner binary: From 518b8cde22cb92e455718580dcd6a4426d9e65cc Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Fri, 19 May 2023 17:14:14 +0330 Subject: [PATCH 0935/2609] [10.x] Add `assertThrows` in http tests (#8795) * add `assertThrows` into http-tests * remove $response * Update http-tests.md --------- Co-authored-by: Taylor Otwell --- http-tests.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/http-tests.md b/http-tests.md index 7472e067d2f..d2c8a97784c 100644 --- a/http-tests.md +++ b/http-tests.md @@ -238,6 +238,15 @@ In addition, if you would like to ensure that your application is not utilizing $response = $this->withoutDeprecationHandling()->get('/'); +The `assertThrows` method may be used to assert that code within a given closure throws an exception of the specified type: + +```php +$this->assertThrows( + fn () => (new ProcessOrder)->execute(), + OrderInvalid::class +); +``` + ## Testing JSON APIs From ea4fb1897f0f59272f7a4d593d4afcb9afc04d7f Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 19 May 2023 08:51:36 -0500 Subject: [PATCH 0936/2609] [11.x] Adds `Dumpable` changes to upgrade guide (#8797) * Adds `Dumpable` changes to upgrade guide * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/upgrade.md b/upgrade.md index e01d5d38084..74b783cbe07 100644 --- a/upgrade.md +++ b/upgrade.md @@ -12,6 +12,15 @@
    + +## Low Impact Changes + +
    + +- [The `Enumerable` Contract](#the-enumerable-contract) + +
    + ## Upgrading To 11.0 From 10.x @@ -39,3 +48,17 @@ You should update the following dependencies in your application's `composer.jso - `laravel/framework` to `^11.0`
    + + +### Collections + + +#### The `Enumerable` Contract + +**Likelihood Of Impact: Low** + +The `dump` method of the `Illuminate\Support\Enumerable` contract has been updated to accept a variadic `...$args` argument. If you are implementing this interface you should update your implementation accordingly: + +```php +public function dump(...$args); +``` From dd9392c664379acdf281571f0a8c1a78566c59ca Mon Sep 17 00:00:00 2001 From: shatterproof <4100078+shatterproof@users.noreply.github.com> Date: Sun, 21 May 2023 09:50:24 -0700 Subject: [PATCH 0937/2609] Add payment_method.automatically_updated webhook (#8799) --- billing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/billing.md b/billing.md index 31be4f8389e..cebf3790f96 100644 --- a/billing.md +++ b/billing.md @@ -1456,8 +1456,9 @@ To ensure your application can handle Stripe webhooks, be sure to configure the - `customer.subscription.deleted` - `customer.updated` - `customer.deleted` -- `invoice.payment_succeeded` +- `payment_method.automatically_updated` - `invoice.payment_action_required` +- `invoice.payment_succeeded` For convenience, Cashier includes a `cashier:webhook` Artisan command. This command will create a webhook in Stripe that listens to all of the events required by Cashier: From f9089ad45f8907eb39c70412e99f19f1484bfb03 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 23 May 2023 12:20:17 +1000 Subject: [PATCH 0938/2609] [10.x] Precognition (#8261) * add front-end precog docs * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wwip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wipg * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wipg * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * formatting and tweaking * Document react * formatting * wip * wip * wip * wip * formatting * formatting --------- Co-authored-by: Taylor Otwell --- documentation.md | 1 + precognition.md | 400 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 401 insertions(+) create mode 100644 precognition.md diff --git a/documentation.md b/documentation.md index 149e1955e4a..4570cae1183 100644 --- a/documentation.md +++ b/documentation.md @@ -91,6 +91,7 @@ - [Passport](/docs/{{version}}/passport) - [Pennant](/docs/{{version}}/pennant) - [Pint](/docs/{{version}}/pint) + - [Precognition](/docs/{{version}}/precognition) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) - [Scout](/docs/{{version}}/scout) diff --git a/precognition.md b/precognition.md new file mode 100644 index 00000000000..2271880024c --- /dev/null +++ b/precognition.md @@ -0,0 +1,400 @@ +# Precognition + +- [Introduction](#introduction) +- [Live Validation](#live-validation) + - [Using Vue](#using-vue) + - [Using Vue & Inertia](#using-vue-and-inertia) + - [Using React](#using-react) + - [Using React & Inertia](#using-react-and-inertia) +- [Customizing Validation Rules](#customizing-validation-rules) +- [Managing Side-Effects](#managing-side-effects) + + +## Introduction + +Laravel Precognition allows you to anticipate the outcome of a future HTTP request. One of the primary use cases of Precognition is the ability to provide "live" validation for your frontend JavaScript application without having to duplicate your application's backend validation rules. Precognition pairs especially well with Laravel's Inertia-based [starter kits](/docs/{{version}}/starter-kits). + +When Laravel receives a "precognitive request", it will execute all of the route's middleware and resolve the route's controller dependencies, including validating [form requests](/docs/{{version}}/validation#form-request-validation) - but it will not actually execute the route's controller method. + + +## Live Validation + + +### Using Vue + +Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend Vue application. To illustrate how it works, let's build a form for creating new users within our application. + +First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{version}/validation#form-request-validation) to house the route's validation rules: + +```php +use App\Http\Requests\CreateUserRequest; +use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; + +Route::post('/users', function (CreateUserRequest $request) { + // ... +})->middleware([HandlePrecognitiveRequests::class]); +``` + +Next, you should install the Laravel Precognition frontend helpers for Vue via NPM: + +```shell +npm install laravel-precognition-vue +``` + +With the Laravel Precognition package installed, you can now create a form object using Precognition's `useForm` function, providing the HTTP method (`post`), the target URL (`/users`), and the initial form data. + +Then, to enable live validation, invoke the form's `validate` method on each input's `change` event, providing the input's name: + +```vue + + + +``` + +Now, as the form is filled by the user, Precognition will provide live validation output powered by the validation rules in the route's form request. When the form's inputs are changed, a debounced "precognitive" validation request will be sent to your Laravel application. You may configure the debounce timeout by calling the form's `setValidationTimeout` function: + +```js +form.setValidationTimeout(3000); +``` + +When a validation request is in-flight, the form's `validating` property will be `true`: + +```html +
    + Validating... +
    +``` + +Any validation errors returned during a validation request or a form submission will automatically populate the form's `errors` object: + +```html +
    + {{ form.errors.email }} +
    +``` + +You can determine if the form has any errors using the form's `hasErrors` property: + +```html +
    + +
    +``` + +You may also determine if an input has passed or failed validation by passing the input's name to the form's `valid` and `invalid` functions, respectively: + +```html + + ✅ + + + + ❌ + +``` + +> **Warning** +> A form input will only appear as valid or invalid once it has changed and a validation response has been received. + +Of course, you may also execute code in reaction to the response to the form submission. The form's `submit` function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form inputs on successful submission, or handle a failed request: + +```js +const submit = () => form.submit() + .then(response => { + form.reset(); + + alert('User created.'); + }) + .catch(error => { + alert('An error occurred.'); + }); +``` + + +### Using Vue & Inertia + +> **Note** +> If you would like a head start when developing your Laravel application with Vue 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 Vue and Inertia, be sure to review our general documentation on [using Precognition with Vue](#using-vue). When using Vue with Inertia, you will need to install the Inertia compatible Precognition library via NPM: + +```shell +npm install laravel-precognition-vue-inertia +``` + +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 Vue 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: + +```vue + +``` + + +### Using React + +Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend React application. To illustrate how it works, let's build a form for creating new users within our application. + +First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{version}/validation#form-request-validation) to house the route's validation rules: + +```php +use App\Http\Requests\CreateUserRequest; +use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; + +Route::post('/users', function (CreateUserRequest $request) { + // ... +})->middleware([HandlePrecognitiveRequests::class]); +``` + +Next, you should install the Laravel Precognition frontend helpers for React via NPM: + +```shell +npm install laravel-precognition-react +``` + +With the Laravel Precognition package installed, you can now create a form object using Precognition's `useForm` function, providing the HTTP method (`post`), the target URL (`/users`), and the initial form data. + +To enable live validation, you should listen to each input's `change` and `blur` event. In the `change` event handler, you should set the form's data with the `setData` function, passing the input's name and new value. Then, in the `blur` event handler invoke the form's `validate` method, providing the input's name: + +```jsx +import { useForm } from 'laravel-precognition-react'; + +export default function Form() { + const form = useForm('post', '/users', { + name: '', + email: '', + }); + + const submit = (e) => { + e.preventDefault(); + + form.submit(); + }; + + return ( +
    + + form.setData('name', e.target.value)} + onBlur={() => form.validate('name')} + /> + {form.invalid('name') ? (
    {form.errors.name}
    ) : null} + + + form.setData('name', e.target.value)} + onBlur={() => form.validate('name')} + /> + {form.invalid('email') ? (
    {form.errors.email}
    ) : null} + + +
    + ); +}; +``` + +Now, as the form is filled by the user, Precognition will provide live validation output powered by the validation rules in the route's form request. When the form's inputs are changed, a debounced "precognitive" validation request will be sent to your Laravel application. You may configure the debounce timeout by calling the form's `setValidationTimeout` function: + +```js +form.setValidationTimeout(3000); +``` + +When a validation request is in-flight, the form's `validating` property will be `true`: + +```jsx +{form.validating ? (
    Validating...
    ) : null} +``` + +Any validation errors returned during a validation request or a form submission will automatically populate the form's `errors` object: + +```jsx +{form.invalid('email') ? (
    {form.errors.email}
    ) : null} +``` + +You can determine if the form has any errors using the form's `hasErrors` property: + +```jsx +{form.hasErrors ? (
    ) : null} +``` + +You may also determine if an input has passed or failed validation by passing the input's name to the form's `valid` and `invalid` functions, respectively: + +```jsx +{form.valid('email') ? () : null} + +{form.invalid('email') ? () : null} +``` + +> **Warning** +> A form input will only appear as valid or invalid once it has changed and a validation response has been received. + +Of course, you may also execute code in reaction to the response to the form submission. The form's `submit` function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form's inputs on a successful form submission, or handle a failed request: + +```js +const submit = (e) => { + e.preventDefault(); + + form.submit() + .then(response => { + form.reset(); + + alert('User created.'); + }) + .catch(error => { + alert('An error occurred.'); + }); +}; +``` + + +### Using React & Inertia + +> **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 +``` + +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 +import { useForm } from 'laravel-precognition-react-inertia'; + +const form = useForm('post', '/users', { + name: '', + email: '', +}); + +const submit = (e) => { + e.preventDefault(); + + form.submit({ + preserveScroll: true, + onSuccess: () => form.reset(), + }); +}; +``` + + +## Customizing Validation Rules + +It is possible to customize the validation rules executed during a precognitive request by using the request's `isPrecognitive` method. + +For example, on a user creation form, we may want to validate that a password is "uncompromised" only on the final form submission. For precognitive validation requests, we will simply validate that the password is required and has a minimum of 8 characters. Using the `isPrecognitive` method, we can customize the rules defined by our form request: + +```php + [ + 'required', + $this->isPrecognitive() + ? Password::min(8) + : Password::min(8)->uncompromised(), + ], + // ... + ]; + } +} +``` + + +## Managing Side-Effects + +When adding the `HandlePrecognitiveRequests` middleware to a route, you should consider if there are any side-effects in _other_ middleware that should be skipped during a precognitive request. + +For example, you may have a middleware that increments the total number of "interactions" each user has with your application, but you may not want precognitive requests to be counted as an interaction. To accomplish this, we may check the request's `isPrecognitive` method before incrementing the interaction count: + +```php +isPrecognitive()) { + Interaction::incrementFor($request->user()); + } + + return $next($request); + } +} +``` From cbaac70dd54acd6d50c1182588ca864cf187f873 Mon Sep 17 00:00:00 2001 From: Stefano Borghi Date: Tue, 23 May 2023 10:32:54 -0700 Subject: [PATCH 0939/2609] updates rate-limiting.m (#8804) --- rate-limiting.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rate-limiting.md b/rate-limiting.md index 5b36df01962..1bf27203f75 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -66,6 +66,10 @@ If you would like to manually interact with the rate limiter, a variety of other return 'Too many attempts!'; } + RateLimiter::hit('send-message:'.$user->id); + + // Send message... + Alternatively, you may use the `remaining` method to retrieve the number of attempts remaining for a given key. If a given key has retries remaining, you may invoke the `hit` method to increment the number of total attempts: use Illuminate\Support\Facades\RateLimiter; @@ -89,6 +93,10 @@ When a key has no more attempts left, the `availableIn` method returns the numbe return 'You may try again in '.$seconds.' seconds.'; } + RateLimiter::hit('send-message:'.$user->id); + + // Send message... + ### Clearing Attempts From 2b944f27dc8fa9d59cd176e6b6e0f03261779914 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 May 2023 13:58:25 -0500 Subject: [PATCH 0940/2609] add hint about precog --- validation.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/validation.md b/validation.md index bbf47d5082c..c0a24dc90db 100644 --- a/validation.md +++ b/validation.md @@ -348,6 +348,9 @@ So, how are the validation rules evaluated? All you need to do is type-hint the 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](#validation-error-response-format). +> **Note** +> Need to add real-time form request validation to your Inertia powered Laravel frontend? Check out [Laravel Precognition](/docs/{{version}}/precognition). + #### Performing Additional Validation From b41de2a4388e0a038019e0779871e369ba25a13b Mon Sep 17 00:00:00 2001 From: Chris Teyerl <44904418+christeyerl@users.noreply.github.com> Date: Tue, 23 May 2023 23:22:05 +0200 Subject: [PATCH 0941/2609] Fix package name (#8807) --- precognition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precognition.md b/precognition.md index 2271880024c..1f120bad3b3 100644 --- a/precognition.md +++ b/precognition.md @@ -305,7 +305,7 @@ const submit = (e) => { 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 +npm install laravel-precognition-react-inertia ``` 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. From f1af491b17ec5485531775486f904fe3a2448034 Mon Sep 17 00:00:00 2001 From: Mark van Eijk Date: Tue, 23 May 2023 23:22:51 +0200 Subject: [PATCH 0942/2609] correct input setData inside react example (#8806) 'name' is where 'email' should be used. --- precognition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/precognition.md b/precognition.md index 1f120bad3b3..8ddbfa59feb 100644 --- a/precognition.md +++ b/precognition.md @@ -232,8 +232,8 @@ export default function Form() { form.setData('name', e.target.value)} - onBlur={() => form.validate('name')} + onChange={(e) => form.setData('email', e.target.value)} + onBlur={() => form.validate('email')} /> {form.invalid('email') ? (
    {form.errors.email}
    ) : null} From c9943f8fdbf1418f4363cb201e6b258f79b84f72 Mon Sep 17 00:00:00 2001 From: Daryl Ferrer Legion Date: Wed, 24 May 2023 10:37:49 +0800 Subject: [PATCH 0943/2609] Fix broken link (#8809) --- precognition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precognition.md b/precognition.md index 8ddbfa59feb..99ba95aec87 100644 --- a/precognition.md +++ b/precognition.md @@ -24,7 +24,7 @@ When Laravel receives a "precognitive request", it will execute all of the route Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend Vue application. To illustrate how it works, let's build a form for creating new users within our application. -First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{version}/validation#form-request-validation) to house the route's validation rules: +First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{{version}}/validation#form-request-validation) to house the route's validation rules: ```php use App\Http\Requests\CreateUserRequest; From f8cf558e6282f5944b028248a4624fd1ba946b68 Mon Sep 17 00:00:00 2001 From: Sn0wCrack <442287+Sn0wCrack@users.noreply.github.com> Date: Wed, 24 May 2023 12:38:12 +1000 Subject: [PATCH 0944/2609] Update homestead documentation (#8808) Adds in additional features that have been added. Remove section on symbolic links on Windows as the latest Homestead versions now automatically add this option if running on Windows --- homestead.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/homestead.md b/homestead.md index a58cee36360..b8304a9c114 100644 --- a/homestead.md +++ b/homestead.md @@ -114,6 +114,8 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Go - Grafana - InfluxDB +- Logstash +- Mailpit (Replaces Mailhog) - MariaDB - Meilisearch - MinIO @@ -831,14 +833,3 @@ By default, Homestead configures the `natdnshostresolver` setting to `on`. This provider: virtualbox natdnshostresolver: 'off' ``` - - -#### Symbolic Links On Windows - -If symbolic links are not working properly on your Windows machine, you may need to add the following block to your `Vagrantfile`: - -```ruby -config.vm.provider "virtualbox" do |v| - v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root", "1"] -end -``` From aa7197b03e1896498615b6f5d2257a46b54c5ad1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 25 May 2023 12:52:31 +1000 Subject: [PATCH 0945/2609] Stick with conventions (#8811) --- precognition.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/precognition.md b/precognition.md index 99ba95aec87..ddb48b4e131 100644 --- a/precognition.md +++ b/precognition.md @@ -27,10 +27,10 @@ Using Laravel Precognition, you can offer live validation experiences to your us First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{{version}}/validation#form-request-validation) to house the route's validation rules: ```php -use App\Http\Requests\CreateUserRequest; +use App\Http\Requests\StoreUserRequest; use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; -Route::post('/users', function (CreateUserRequest $request) { +Route::post('/users', function (StoreUserRequest $request) { // ... })->middleware([HandlePrecognitiveRequests::class]); ``` @@ -184,10 +184,10 @@ Using Laravel Precognition, you can offer live validation experiences to your us First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{version}/validation#form-request-validation) to house the route's validation rules: ```php -use App\Http\Requests\CreateUserRequest; +use App\Http\Requests\StoreUserRequest; use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; -Route::post('/users', function (CreateUserRequest $request) { +Route::post('/users', function (StoreUserRequest $request) { // ... })->middleware([HandlePrecognitiveRequests::class]); ``` From 7c80ecc88c8bb229ef22ad21cf54e7936a9ca95a Mon Sep 17 00:00:00 2001 From: Ashley Shenton Date: Thu, 25 May 2023 16:24:34 +0200 Subject: [PATCH 0946/2609] docs: update the default strategy wording to match the default config (#8812) --- horizon.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/horizon.md b/horizon.md index 05b2a34327f..adc4a3c6a00 100644 --- a/horizon.md +++ b/horizon.md @@ -94,11 +94,11 @@ Within Horizon's default configuration file, you will notice a `defaults` config ### Balancing Strategies -Unlike Laravel's default queue system, Horizon allows you to choose from three worker balancing strategies: `simple`, `auto`, and `false`. The `simple` strategy, which is the configuration file's default, splits incoming jobs evenly between worker processes: +Unlike Laravel's default queue system, Horizon allows you to choose from three worker balancing strategies: `simple`, `auto`, and `false`. The `simple` strategy splits incoming jobs evenly between worker processes: 'balance' => 'simple', -The `auto` strategy adjusts the number of worker processes per queue based on the current workload of the queue. For example, if your `notifications` queue has 1,000 pending jobs while your `render` queue is empty, Horizon will allocate more workers to your `notifications` queue until the queue is empty. +The `auto` strategy, which is the configuration file's default, adjusts the number of worker processes per queue based on the current workload of the queue. For example, if your `notifications` queue has 1,000 pending jobs while your `render` queue is empty, Horizon will allocate more workers to your `notifications` queue until the queue is empty. When using the `auto` strategy, you may define the `minProcesses` and `maxProcesses` configuration options to control the minimum and the maximum number of worker processes Horizon should scale up and down to: From e63d6c490b22c46f751f2c19ab6c719efe6befc2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 25 May 2023 14:16:12 -0500 Subject: [PATCH 0947/2609] wip --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 887f968c7de..9c14e34eb94 100644 --- a/queues.md +++ b/queues.md @@ -440,7 +440,7 @@ In the example above, we defined an hourly rate limit; however, you may easily d return Limit::perMinute(50)->by($job->user->id); -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. +Once you have defined your rate limit, you may attach the rate limiter to your 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; From 08c3cb7cf35fe120188243f21753e982bd867a9c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 26 May 2023 15:21:37 +0100 Subject: [PATCH 0948/2609] Temporarily adds `--with-all-dependencies` (#8821) --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index de0fb6f9aea..4d3ef07caff 100644 --- a/filesystem.md +++ b/filesystem.md @@ -82,7 +82,7 @@ You may configure additional symbolic links in your `filesystems` configuration Before using the S3 driver, you will need to install the Flysystem S3 package via the Composer package manager: ```shell -composer require league/flysystem-aws-s3-v3 "^3.0" +composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies ``` The S3 driver configuration information is located in your `config/filesystems.php` configuration file. This file contains an example configuration array for an S3 driver. You are free to modify this array with your own S3 configuration and credentials. For convenience, these environment variables match the naming convention used by the AWS CLI. From 437b3b3583b78cb4e912c97994eb9cbab22d755f Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Fri, 26 May 2023 15:29:38 +0100 Subject: [PATCH 0949/2609] Added a note about the new form request after method to the upgrading guide (#8820) * Added a note about the new form request after method to the upgrading guide * Update upgrade.md * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/upgrade.md b/upgrade.md index 0d08361122b..6db4bfc83e7 100644 --- a/upgrade.md +++ b/upgrade.md @@ -32,6 +32,7 @@
    - [Closure Validation Rule Messages](#closure-validation-rule-messages) +- [Form Request `after` Method](#form-request-after-method) - [Public Path Binding](#public-path-binding) - [Query Exception Constructor](#query-exception-constructor) - [Rate Limiter Return Values](#rate-limiter-return-values) @@ -296,6 +297,13 @@ public function rules() } ``` + +#### Form Request After Method + +**Likelihood Of Impact: Very Low** + +Within form requests, the `after` method is now [reserved by Laravel](https://github.com/laravel/framework/pull/46757). If your form requests define an `after` method, the method should be renamed or modified to utilize the new "after validation" feature of Laravel's form requests. + ### Miscellaneous From 2c54cf161c639b73969e7e0ab75f07a207ec8a28 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 27 May 2023 00:39:28 +1000 Subject: [PATCH 0950/2609] wip (#8816) --- precognition.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/precognition.md b/precognition.md index ddb48b4e131..e7466395296 100644 --- a/precognition.md +++ b/precognition.md @@ -226,7 +226,7 @@ export default function Form() { onChange={(e) => form.setData('name', e.target.value)} onBlur={() => form.validate('name')} /> - {form.invalid('name') ? (
    {form.errors.name}
    ) : null} + {form.invalid('name') &&
    {form.errors.name}
    } form.setData('email', e.target.value)} onBlur={() => form.validate('email')} /> - {form.invalid('email') ? (
    {form.errors.email}
    ) : null} + {form.invalid('email') &&
    {form.errors.email}
    } @@ -252,27 +252,27 @@ form.setValidationTimeout(3000); When a validation request is in-flight, the form's `validating` property will be `true`: ```jsx -{form.validating ? (
    Validating...
    ) : null} +{form.validating &&
    Validating...
    } ``` Any validation errors returned during a validation request or a form submission will automatically populate the form's `errors` object: ```jsx -{form.invalid('email') ? (
    {form.errors.email}
    ) : null} +{form.invalid('email') &&
    {form.errors.email}
    } ``` You can determine if the form has any errors using the form's `hasErrors` property: ```jsx -{form.hasErrors ? (
    ) : null} +{form.hasErrors &&
    } ``` You may also determine if an input has passed or failed validation by passing the input's name to the form's `valid` and `invalid` functions, respectively: ```jsx -{form.valid('email') ? () : null} +{form.valid('email') && } -{form.invalid('email') ? () : null} +{form.invalid('email') && } ``` > **Warning** From a71970b9396571e5a8703a3b34d41d2dbc133c61 Mon Sep 17 00:00:00 2001 From: Peter Elmered Date: Tue, 30 May 2023 16:17:43 +0200 Subject: [PATCH 0951/2609] Fix error in Arr::mapWithKeys example code (#8827) The first $array parameter is missing --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 3205f27faf8..11f6e392238 100644 --- a/helpers.md +++ b/helpers.md @@ -732,7 +732,7 @@ The `Arr::mapWithKeys` method iterates through the array and passes each value t ] ]; - $mapped = Arr::mapWithKeys(function (array $item, int $key) { + $mapped = Arr::mapWithKeys($array, function (array $item, int $key) { return [$item['email'] => $item['name']]; }); From 912c32df833f80960263eec8b5e315a3d6aef951 Mon Sep 17 00:00:00 2001 From: Muntaser Muttaqi Date: Tue, 30 May 2023 21:30:24 +0600 Subject: [PATCH 0952/2609] Add TypeScript support to `Breeze & React / Vue` section in Starter Kits (#8828) * Add TypeScript support to `Breeze & React / Vue` section in `Starter Kits` * Update starter-kits.md * Update starter-kits.md * Update starter-kits.md --------- Co-authored-by: Taylor Otwell --- starter-kits.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/starter-kits.md b/starter-kits.md index 5b9755739c2..31cdc465abd 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -101,6 +101,16 @@ php artisan breeze:install vue --ssr php artisan breeze:install react --ssr ``` + +#### TypeScript + +When using the Vue or React stacks, you may provide the `--typescript` option to generate scaffolding that includes TypeScript support: + +```shell +php artisan breeze:install vue --typescript +php artisan breeze:install react --typescript +``` + ### Breeze & Next.js / API From 8262b2e6321268a1d0dc4dff37ec3d44e555926d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 30 May 2023 10:47:24 -0500 Subject: [PATCH 0953/2609] document timezone arguments --- validation.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/validation.md b/validation.md index c0a24dc90db..e612a85e7da 100644 --- a/validation.md +++ b/validation.md @@ -1731,7 +1731,15 @@ The field under validation must be a string. If you would like to allow the fiel #### timezone -The field under validation must be a valid timezone identifier according to the `timezone_identifiers_list` PHP function. +The field under validation must be a valid timezone identifier according to the `DateTimeZone::listIdentifiers` method. + +The arguments [accepted by the `DateTimeZone::listIdentifiers` method](https://www.php.net/manual/en/datetimezone.listidentifiers.php) may also be provided to this validation rule: + + 'timezone' => 'required|timezone:all'; + + 'timezone' => 'required|timezone:Africa'; + + 'timezone' => 'required|timezone:per_country,US'; #### unique:_table_,_column_ From 7d8524a1caa91de2ece532ae5f5dd1fc3e165f23 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 31 May 2023 04:57:34 +1000 Subject: [PATCH 0954/2609] [10.x] Document file handling (#8817) * wip * wip * wip * wip * formatting * formatting --------- Co-authored-by: Taylor Otwell --- precognition.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/precognition.md b/precognition.md index e7466395296..1b997911970 100644 --- a/precognition.md +++ b/precognition.md @@ -7,6 +7,7 @@ - [Using React](#using-react) - [Using React & Inertia](#using-react-and-inertia) - [Customizing Validation Rules](#customizing-validation-rules) +- [Handling File Uploads](#handling-file-uploads) - [Managing Side-Effects](#managing-side-effects) @@ -130,6 +131,20 @@ You may also determine if an input has passed or failed validation by passing th > **Warning** > A form input will only appear as valid or invalid once it has changed and a validation response has been received. +If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's `forgetError` function to achieve this: + +```html + +``` + Of course, you may also execute code in reaction to the response to the form submission. The form's `submit` function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form inputs on successful submission, or handle a failed request: ```js @@ -278,6 +293,20 @@ You may also determine if an input has passed or failed validation by passing th > **Warning** > A form input will only appear as valid or invalid once it has changed and a validation response has been received. +If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's `forgetError` function to achieve this: + +```jsx + + form.setData('avatar', e.target.value); + + form.forgetError('avatar'); + } +> +``` + Of course, you may also execute code in reaction to the response to the form submission. The form's `submit` function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form's inputs on a successful form submission, or handle a failed request: ```js @@ -367,6 +396,39 @@ class StoreUserRequest extends FormRequest } ``` + +## Handling File Uploads + +By default, Laravel Precognition does not upload or validate files during a precognitive validation request. This ensure that large files are not unnecessarily uploaded multiple times. + +Because of this behavior, you should ensure that your application [customizes the corresponding form request's validation rules](#customizing-validation-rules) to specify the field is only required for full form submissions: + +```php +/** + * Get the validation rules that apply to the request. + * + * @return array + */ +protected function rules() +{ + return [ + 'avatar' => [ + ...$this->isPrecognitive() ? [] : ['required'], + 'image', + 'mimes:jpg,png' + 'dimensions:ratio=3/2', + ], + // ... + ]; +} +``` + +If you would like to include files in every validation request, you may invoke the `validateFiles` function on your client-side form instance: + +```js +form.validateFiles(); +``` + ## Managing Side-Effects From e0dab4b3874db46636354f92b12ab7ec68329b92 Mon Sep 17 00:00:00 2001 From: Daryl Ferrer Legion Date: Wed, 31 May 2023 21:33:39 +0800 Subject: [PATCH 0955/2609] Fix broken link (#8829) --- precognition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precognition.md b/precognition.md index 1b997911970..d2d934cc3fd 100644 --- a/precognition.md +++ b/precognition.md @@ -196,7 +196,7 @@ const submit = () => form.submit({ Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend React application. To illustrate how it works, let's build a form for creating new users within our application. -First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{version}/validation#form-request-validation) to house the route's validation rules: +First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{{version}}/validation#form-request-validation) to house the route's validation rules: ```php use App\Http\Requests\StoreUserRequest; From 8184373c0afddb70562d90cba88c02896b92cea2 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 2 Jun 2023 23:06:38 +1000 Subject: [PATCH 0956/2609] [10.x] Sleep hook (#8835) * wip * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 11f6e392238..66c41d0b93e 100644 --- a/helpers.md +++ b/helpers.md @@ -4434,4 +4434,19 @@ Of course, the `Sleep` class offers a variety of other assertions you may use wh // Assert that, even if Sleep was called, no execution paused occurred... Sleep::assertInsomniac(); -Laravel uses the `Sleep` class under the hood whenever it is pausing execution. For example, the [`retry`](#method-retry) helper uses the `Sleep` class when sleeping, allowing for improved testability when using that helper. +Sometimes it may be useful to perform an action whenever a fake sleep occurs in your application code. To achieve this, you may provide a callback to the `whenFakingSleep` method. In the following example, we use Laravel's [time manipulation helpers](/docs/{{version}}/mocking#interacting-with-time) to instantly progress time by the duration of each sleep: + +```php +use Carbon\CarbonInterval as Duration; + +$this->freezeTime(); + +Sleep::fake(); + +Sleep::whenFakingSleep(function (Duration $duration) { + // Progress time when faking sleep... + $this->travel($duration->totalMilliseconds)->milliseconds(); +}); +``` + +Laravel uses the `Sleep` class internally whenever it is pausing execution. For example, the [`retry`](#method-retry) helper uses the `Sleep` class when sleeping, allowing for improved testability when using that helper. From 84d837ecf8035d01d464ffa5e142080c94a58ea7 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 2 Jun 2023 23:22:56 +1000 Subject: [PATCH 0957/2609] [10.x] Vite HMR + Sail + WSL2 (#8832) * wip * wip * wip * wip * formatting --------- Co-authored-by: Taylor Otwell --- vite.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/vite.md b/vite.md index 2b75c3bbffa..3b2c9d0d198 100644 --- a/vite.md +++ b/vite.md @@ -173,6 +173,26 @@ export default defineConfig({ If you are unable to generate a trusted certificate for your system, you may install and configure the [`@vitejs/plugin-basic-ssl` plugin](https://github.com/vitejs/vite-plugin-basic-ssl). When using untrusted certificates, you will need to accept the certificate warning for Vite's development server in your browser by following the "Local" link in your console when running the `npm run dev` command. + +#### Running The Development Server In Sail On WSL2 + +When running the Vite development server within [Laravel Sail](/docs/{{version}}/sail) on Windows Subsystem for Linux 2 (WSL2), you should add the following configuration to your `vite.config.js` file to ensure the browser can communicate with the development server: + +```js +// ... + +export default defineConfig({ + // ... + server: { // [tl! add:start] + hmr: { + host: 'localhost', + }, + }, // [tl! add:end] +}); +``` + +If your file changes are not being reflected in the browser while the development server is running, you may also need to configure Vite's [`server.watch.usePolling` option](https://vitejs.dev/config/server-options.html#server-watch). + ### Loading Your Scripts And Styles @@ -226,6 +246,8 @@ npm run dev npm run build ``` +If you are running the development server in [Sail](/docs/{{version}}/sail) on WSL2, you may need some [additional configuration](#configuring-hmr-in-sail-on-wsl2) options. + ## Working With JavaScript From 2199ae0239e41df6575cdde336407c968c848d0e Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 2 Jun 2023 23:29:35 +1000 Subject: [PATCH 0958/2609] [10.x] Alpine + Precognition (#8810) * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update precognition.md * Update precognition.md * Add location --------- Co-authored-by: Taylor Otwell --- precognition.md | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/precognition.md b/precognition.md index d2d934cc3fd..1394a1a8c2d 100644 --- a/precognition.md +++ b/precognition.md @@ -6,6 +6,7 @@ - [Using Vue & Inertia](#using-vue-and-inertia) - [Using React](#using-react) - [Using React & Inertia](#using-react-and-inertia) + - [Using Alpine & Blade](#using-alpine) - [Customizing Validation Rules](#customizing-validation-rules) - [Handling File Uploads](#handling-file-uploads) - [Managing Side-Effects](#managing-side-effects) @@ -359,6 +360,162 @@ const submit = (e) => { }; ``` + +### Using Alpine & Blade + +Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend Alpine application. To illustrate how it works, let's build a form for creating new users within our application. + +First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{version}/validation#form-request-validation) to house the route's validation rules: + +```php +use App\Http\Requests\CreateUserRequest; +use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; + +Route::post('/users', function (CreateUserRequest $request) { + // ... +})->middleware([HandlePrecognitiveRequests::class]); +``` + +Next, you should install the Laravel Precognition frontend helpers for Alpine via NPM: + +```shell +npm install laravel-precognition-alpine +``` + +Then, register the Precognition plugin with Alpine in your `resources/js/app.js` file: + +```js +import Alpine from 'alpinejs'; +import Precognition from 'laravel-precognition-alpine'; + +window.Alpine = Alpine; + +Alpine.plugin(Precognition); +Alpine.start(); +``` + +With the Laravel Precognition package installed and registered, you can now create a form object using Precognition's `$form` "magic", providing the HTTP method (`post`), the target URL (`/users`), and the initial form data. + +To enable live validation, you should bind the form's data to it's relevant input and then listen to each input's `change` event. In the `change` event handler, you should invoke the form's `validate` method, providing the input's name: + +```html +
    + @csrf + + + + + + + + + +
    +``` + +Now, as the form is filled by the user, Precognition will provide live validation output powered by the validation rules in the route's form request. When the form's inputs are changed, a debounced "precognitive" validation request will be sent to your Laravel application. You may configure the debounce timeout by calling the form's `setValidationTimeout` function: + +```js +form.setValidationTimeout(3000); +``` + +When a validation request is in-flight, the form's `validating` property will be `true`: + +```html + +``` + +Any validation errors returned during a validation request or a form submission will automatically populate the form's `errors` object: + +```html + +``` + +You can determine if the form has any errors using the form's `hasErrors` property: + +```html + +``` + +You may also determine if an input has passed or failed validation by passing the input's name to the form's `valid` and `invalid` functions, respectively: + +```html + + + +``` + +> **Warning** +> A form input will only appear as valid or invalid once it has changed and a validation response has been received. + + +#### Repopulating Old Form Data + +In the user creation example discussed above, we are using Precognition to perform live validation; however, we are performing a traditional server-side form submission to submit the form. So, the form should be populated with any "old" input and validation errors returned from the server-side form submission: + +```html +
    +``` + +Alternatively, if you would like to submit the form via XHR you may use the form's `submit` function, which returns an Axios request promise: + +```html + +``` + ## Customizing Validation Rules From db6f301ede69b6b57d2cc4634751069fe54f73be Mon Sep 17 00:00:00 2001 From: Matias Hernan Lauriti Date: Mon, 5 Jun 2023 16:03:29 -0300 Subject: [PATCH 0959/2609] [10.x] Update vite.md - Grammar fix (#8840) * Update vite.md - Grammar fix * Update vite.md --------- Co-authored-by: Taylor Otwell --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 3b2c9d0d198..fcb654bdb02 100644 --- a/vite.md +++ b/vite.md @@ -196,7 +196,7 @@ If your file changes are not being reflected in the browser while the developmen ### Loading Your Scripts And Styles -With your Vite entry points configured, you only need reference them in a `@vite()` Blade directive that you add to the `` of your application's root template: +With your Vite entry points configured, you may now reference them in a `@vite()` Blade directive that you add to the `` of your application's root template: ```blade From c9c559cd960792cfb55f635e6d79c0859e8ac4e2 Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Wed, 7 Jun 2023 01:07:18 +0330 Subject: [PATCH 0960/2609] Remove importing classes that are not used in examples (#8841) --- http-tests.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/http-tests.md b/http-tests.md index d2c8a97784c..3041c8bb1e1 100644 --- a/http-tests.md +++ b/http-tests.md @@ -27,8 +27,6 @@ Laravel provides a very fluent API for making HTTP requests to your application namespace Tests\Feature; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithoutMiddleware; use Tests\TestCase; class ExampleTest extends TestCase @@ -58,8 +56,6 @@ Instead of returning an `Illuminate\Http\Response` instance, test request method namespace Tests\Feature; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Tests\TestCase; class ExampleTest extends TestCase @@ -492,8 +488,6 @@ The `Illuminate\Http\UploadedFile` class provides a `fake` method which may be u namespace Tests\Feature; - use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; use Tests\TestCase; From 966519ab3ea2a28b49f1a7c8aaffa4c726faa1ef Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:51:24 +0330 Subject: [PATCH 0961/2609] [10.x] Add `replaceHeaders` method into http-client (#8847) * add `replaceHeaders` method into http-client * Update http-client.md * Update http-client.md --------- Co-authored-by: Taylor Otwell --- http-client.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/http-client.md b/http-client.md index cb9b7aa2631..54e82eb4f32 100644 --- a/http-client.md +++ b/http-client.md @@ -174,6 +174,18 @@ For convenience, you may use the `acceptJson` method to quickly specify that you $response = Http::acceptJson()->get('http://example.com/users'); +The `withHeaders` method merges new headers into the request's existing headers. If needed, you may replace all of the headers entirely using the `replaceHeaders` method: + +```php +$response = Http::withHeaders([ + 'X-Original' => 'foo', +])->replaceHeaders([ + 'X-Replacement' => 'bar', +])->post('http://example.com/users', [ + 'name' => 'Taylor', +]); +``` + ### Authentication From 91b25a1f8850d65abcc1b636b43754558068c705 Mon Sep 17 00:00:00 2001 From: Erik Scheepers Date: Thu, 8 Jun 2023 15:32:04 +0200 Subject: [PATCH 0962/2609] Import Rule class in validation.md (#8846) --- validation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/validation.md b/validation.md index e612a85e7da..0fa990e34eb 100644 --- a/validation.md +++ b/validation.md @@ -2028,6 +2028,7 @@ Laravel provides a variety of validation rules that may be used to validate uplo If your application accepts images uploaded by your users, you may use the `File` rule's `image` constructor method to indicate that the uploaded file should be an image. In addition, the `dimensions` rule may be used to limit the dimensions of the image: use Illuminate\Support\Facades\Validator; + use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\File; Validator::validate($input, [ From b10ef46931d608949100a18b2a7f27da2d57ca92 Mon Sep 17 00:00:00 2001 From: Hernan Martinez Reumann Date: Thu, 8 Jun 2023 10:34:33 -0300 Subject: [PATCH 0963/2609] Update migrations.md - migrate:rollback --pretend (#8845) * Update migrations.md - migrate:rollback --pretend * Update migrations.md --------- Co-authored-by: Taylor Otwell --- migrations.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/migrations.md b/migrations.md index aa734cc6a09..b7fd3b8323e 100644 --- a/migrations.md +++ b/migrations.md @@ -194,6 +194,12 @@ You may roll back a specific "batch" of migrations by providing the `batch` opti php artisan migrate:rollback --batch=3 ``` +If you would like to see the SQL statements that will be executed by the migrations without actually running them, you may provide the `--pretend` flag to the `migrate:rollback` command: + +```shell +php artisan migrate:rollback --pretend +``` + The `migrate:reset` command will roll back all of your application's migrations: ```shell From eb4bfb821ae52f75a69430801208465cf2fdb264 Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Thu, 8 Jun 2023 17:16:37 +0330 Subject: [PATCH 0964/2609] Add the document of `assertMethodNotAllowed` to http-tests.md (#8844) --- http-tests.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-tests.md b/http-tests.md index 3041c8bb1e1..832b0162605 100644 --- a/http-tests.md +++ b/http-tests.md @@ -644,6 +644,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertJsonValidationErrors](#assert-json-validation-errors) [assertJsonValidationErrorFor](#assert-json-validation-error-for) [assertLocation](#assert-location) +[assertMethodNotAllowed](#assert-method-not-allowed) [assertMovedPermanently](#assert-moved-permanently) [assertContent](#assert-content) [assertNoContent](#assert-no-content) @@ -1002,6 +1003,13 @@ Assert the response has any JSON validation errors for the given key: $response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors'); + +#### assertMethodNotAllowed + +Assert that the response has a method not allowed (405) HTTP status code: + + $response->assertMethodNotAllowed(); + #### assertMovedPermanently From 78b6681aa668de6d46671cd319f8f7408bd4eceb Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Fri, 9 Jun 2023 05:30:14 +0330 Subject: [PATCH 0965/2609] Remove importing classes that are not used in examples (#8848) --- database-testing.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/database-testing.md b/database-testing.md index a8845698041..1b96153cec8 100644 --- a/database-testing.md +++ b/database-testing.md @@ -21,7 +21,6 @@ Before proceeding much further, let's discuss how to reset your database after e namespace Tests\Feature; use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Tests\TestCase; class ExampleTest extends TestCase @@ -71,7 +70,6 @@ If you would like to use [database seeders](/docs/{{version}}/seeding) to popula use Database\Seeders\OrderStatusSeeder; use Database\Seeders\TransactionStatusSeeder; use Illuminate\Foundation\Testing\RefreshDatabase; - use Illuminate\Foundation\Testing\WithoutMiddleware; use Tests\TestCase; class ExampleTest extends TestCase From 241c39fda212a17ba8a2c0e0682d3d1c8711e424 Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Fri, 9 Jun 2023 18:19:55 +0330 Subject: [PATCH 0966/2609] Import `Crypt` in the example of `assertSessionHasInput` (#8851) --- http-tests.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http-tests.md b/http-tests.md index 832b0162605..12af10584c0 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1172,6 +1172,8 @@ Assert that the session has a given value in the [flashed input array](/docs/{{v If needed, a closure can be provided as the second argument to the `assertSessionHasInput` method. The assertion will pass if the closure returns `true`: + use Illuminate\Support\Facades\Crypt; + $response->assertSessionHasInput($key, function (string $value) { return Crypt::decryptString($value) === 'secret'; }); From 80241573be28054c74f36fcb7141df7443e25d9b Mon Sep 17 00:00:00 2001 From: Alexander Holzapfel Date: Fri, 9 Jun 2023 16:50:27 +0200 Subject: [PATCH 0967/2609] Change tilde to $HOME for $PATH variable (#8850) --- valet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valet.md b/valet.md index dceb1e75481..0f93059437f 100644 --- a/valet.md +++ b/valet.md @@ -81,7 +81,7 @@ Next, you should use Homebrew to install PHP: brew install php ``` -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: +After installing PHP, you are ready to install the [Composer package manager](https://getcomposer.org). In addition, you should make sure the `$HOME/.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: ```shell composer global require laravel/valet From 58f9883e8106cf4164414ce6260ab9163c83c2a5 Mon Sep 17 00:00:00 2001 From: Alfonz Montelibano Date: Fri, 9 Jun 2023 22:51:31 +0800 Subject: [PATCH 0968/2609] Fix word in Eloquent Relationships page (#8849) --- eloquent-relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 295779d45e7..421795f9714 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1254,7 +1254,7 @@ As demonstrated in the example above, you are free to add additional constraints ->orWhere('votes', '>=', 100) ->get(); -The example above will generate the following SQL. As you can see, the `or` clause instructs the query to return _any_ user with greater than 100 votes. The query is no longer constrained to a specific user: +The example above will generate the following SQL. As you can see, the `or` clause instructs the query to return _any_ post with greater than 100 votes. The query is no longer constrained to a specific user: ```sql select * From 0cdfdb87fd17efd1d1f3e507673b97b3cbe0d43e Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Fri, 9 Jun 2023 23:57:35 +0330 Subject: [PATCH 0969/2609] Remove importing `Controller` from one example in hashing.md (#8852) --- hashing.md | 1 - 1 file changed, 1 deletion(-) diff --git a/hashing.md b/hashing.md index d674335094f..81234246e14 100644 --- a/hashing.md +++ b/hashing.md @@ -31,7 +31,6 @@ You may hash a password by calling the `make` method on the `Hash` facade: namespace App\Http\Controllers; - use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; From d47aca9ae8d8c9353f38cfe1c6d7d6f0a0a8cfd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LURQUIN=20Micha=C3=ABl?= Date: Tue, 13 Jun 2023 09:37:54 +0200 Subject: [PATCH 0970/2609] Update validation.md (#8858) * Update validation.md Correction of the namespace for the use of SQL queries when using the "Rule::unique" function (with "where"). * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/validation.md b/validation.md index 0fa990e34eb..c62fbb879ed 100644 --- a/validation.md +++ b/validation.md @@ -1768,7 +1768,6 @@ Sometimes, you may wish to ignore a given ID during unique validation. For examp 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\Database\Eloquent\Builder; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; From 1eefcc35fdf9b8a7ac2e62b547dbe2087df7b815 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:15:21 +0330 Subject: [PATCH 0971/2609] [10.x] Add `singletonIf` into container (#8856) * add `singletonIf` into container * Update container.md --------- Co-authored-by: Taylor Otwell --- container.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/container.md b/container.md index db92bfef9e6..a42854da3d0 100644 --- a/container.md +++ b/container.md @@ -150,6 +150,14 @@ The `singleton` method binds a class or interface into the container that should return new Transistor($app->make(PodcastParser::class)); }); +You may use the `singletonIf` method to register a singleton container binding only if a binding has not already been registered for the given type: + +```php +$this->app->singletonIf(Transistor::class, function (Application $app) { + return new Transistor($app->make(PodcastParser::class)); +}); +``` + #### Binding Scoped Singletons From 29086d81ffeefa4f7609a79177e6ed3149dd1f9b Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Wed, 14 Jun 2023 00:16:18 +0330 Subject: [PATCH 0972/2609] Remove classes from `encryption` that are not necessary to be imported (#8855) --- encryption.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/encryption.md b/encryption.md index cbc788e1471..eedfd3aabfe 100644 --- a/encryption.md +++ b/encryption.md @@ -26,8 +26,6 @@ You may encrypt a value using the `encryptString` method provided by the `Crypt` namespace App\Http\Controllers; - use App\Http\Controllers\Controller; - use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Crypt; From 70d2160d0525423608c5d1823c361a05be5c22fd Mon Sep 17 00:00:00 2001 From: MD Masud Sikdar Date: Wed, 14 Jun 2023 02:52:38 +0600 Subject: [PATCH 0973/2609] [master] contributions.md edited (#8861) * Contributions.md file edited * Contributions.md edited * [Master] Version 10.x to 11.x --- contributions.md | 4 ++-- helpers.md | 6 +++--- http-client.md | 2 +- readme.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contributions.md b/contributions.md index a46e6f6d0f0..c9332686916 100644 --- a/contributions.md +++ b/contributions.md @@ -75,9 +75,9 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest version that supports bug fixes (currently `9.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `11.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. -**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `9.x`). +**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `11.x`). **Major** new features or features with breaking changes should always be sent to the `master` branch, which contains the upcoming release. diff --git a/helpers.md b/helpers.md index add6aebf236..b79be7306f5 100644 --- a/helpers.md +++ b/helpers.md @@ -1840,11 +1840,11 @@ The `Str::replace` method replaces a given string within the string: use Illuminate\Support\Str; - $string = 'Laravel 8.x'; + $string = 'Laravel 10.x'; - $replaced = Str::replace('8.x', '9.x', $string); + $replaced = Str::replace('10.x', '11.x', $string); - // Laravel 9.x + // Laravel 11.x #### `Str::replaceArray()` {.collection-method} diff --git a/http-client.md b/http-client.md index be726bc2ed8..4e0f607532f 100644 --- a/http-client.md +++ b/http-client.md @@ -84,7 +84,7 @@ The HTTP client also allows you to construct request URLs using the [URI templat Http::withUrlParameters([ 'endpoint' => 'https://laravel.com/', 'page' => 'docs', - 'version' => '9.x', + 'version' => '11.x', 'topic' => 'validation', ])->get('{+endpoint}/{page}/{version}/{topic}'); ``` diff --git a/readme.md b/readme.md index 3db17ac3a66..0999c0db95a 100644 --- a/readme.md +++ b/readme.md @@ -4,4 +4,4 @@ You can find the online version of the Laravel documentation at [https://laravel ## Contribution Guidelines -If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 9 would be submitted to the `9.x` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. +If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 11 would be submitted to the `11.x` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. From 0bf5ba08e6b4d325825662cfa43c798822c03063 Mon Sep 17 00:00:00 2001 From: Robert Boes <2871897+RobertBoes@users.noreply.github.com> Date: Tue, 13 Jun 2023 22:55:23 +0200 Subject: [PATCH 0974/2609] [10.x] Add ESM export to Vite `postcss.config.js` example (#8859) * Update vite.md * Update vite.md --------- Co-authored-by: Taylor Otwell --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index fcb654bdb02..bcac4c9bddc 100644 --- a/vite.md +++ b/vite.md @@ -421,7 +421,7 @@ The following example demonstrates how Vite will treat relative and absolute URL You can learn more about Vite's CSS support within the [Vite documentation](https://vitejs.dev/guide/features.html#css). If you are using PostCSS plugins such as [Tailwind](https://tailwindcss.com), you may create a `postcss.config.js` file in the root of your project and Vite will automatically apply it: ```js -module.exports = { +export default { plugins: { tailwindcss: {}, autoprefixer: {}, From f99f3a1fc30e79e757cddcaaccd75bb7cee18137 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 14 Jun 2023 23:46:41 +0330 Subject: [PATCH 0975/2609] fix sail link (#8865) --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 118e20bf3c1..411188c71df 100644 --- a/sail.md +++ b/sail.md @@ -39,7 +39,7 @@ Laravel Sail is supported on macOS, Linux, and Windows (via [WSL2](https://docs. ## 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. During installation, you will be asked to choose which Sail supported services your application will be interacting with. +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#laravel-and-docker) for your operating system. During installation, you will be asked to choose which Sail supported services your application will be interacting with. ### Installing Sail Into Existing Applications From a5214ee5edc9d86f29443cbac3a12f7d7b639bd1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 18 Jun 2023 11:19:57 +0200 Subject: [PATCH 0976/2609] wip --- starter-kits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starter-kits.md b/starter-kits.md index 31cdc465abd..3e35aee0366 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -49,7 +49,7 @@ After Composer has installed the Laravel Breeze package, you may run the `breeze The default Breeze "stack" is the Blade stack, which utilizes simple [Blade templates](/docs/{{version}}/blade) to render your application's frontend. The Blade stack may be installed by invoking the `breeze:install` command with no other additional arguments. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: ```shell -php artisan breeze:install +php artisan breeze:install blade php artisan migrate npm install From a33df9cf4ffb341c84b7e8976b633f989f13b1d3 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 22 Jun 2023 07:54:39 +0900 Subject: [PATCH 0977/2609] Keep consistency of the allowed array keys (#8872) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index c62fbb879ed..69a050c6e63 100644 --- a/validation.md +++ b/validation.md @@ -1930,7 +1930,7 @@ As discussed in the [`array` validation rule documentation](#rule-array), the `a ]; Validator::make($input, [ - 'user' => 'array:username,locale', + 'user' => 'array:name,username', ]); In general, you should always specify the array keys that are allowed to be present within your array. Otherwise, the validator's `validate` and `validated` methods will return all of the validated data, including the array and all of its keys, even if those keys were not validated by other nested array validation rules. From 1778cb0f1c166b9a5d72edc364d43a2e1ac62ebd Mon Sep 17 00:00:00 2001 From: Scarecrow0w Date: Thu, 22 Jun 2023 01:57:36 +0300 Subject: [PATCH 0978/2609] Added single space around construct (#8871) --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 30b61a466e8..cd45ba47d66 100644 --- a/queries.md +++ b/queries.md @@ -455,7 +455,7 @@ If you need to group an "or" condition within parentheses, you may pass a closur $users = DB::table('users') ->where('votes', '>', 100) - ->orWhere(function(Builder $query) { + ->orWhere(function (Builder $query) { $query->where('name', 'Abigail') ->where('votes', '>', 50); }) From 482c1c179ee8799d96485bc593c4781d36a671d1 Mon Sep 17 00:00:00 2001 From: Mahmoud Mohamed Ramadan <48416569+mahmoudmohamedramadan@users.noreply.github.com> Date: Thu, 22 Jun 2023 02:02:58 +0300 Subject: [PATCH 0979/2609] [10.x] Fix the invalid namespace issue (#8870) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [10.x] Fix the invalid namespace issue When I tried to use `Illuminate\Database\Eloquent\Builder` namespace I got **Argument #1 ($query) must be of type Illuminate\Database\Eloquent\Builder, Illuminate\Database\Eloquent\Relations\HasMany given** error, and when I used `Illuminate\Contracts\Database\Eloquent\Builder` namespace the issue was solved 🎉. * Update eloquent-relationships.md --------- Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 421795f9714..6514a9d1e0f 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1747,15 +1747,14 @@ In this example, Eloquent will only eager load posts where the post's `title` co If you are eager loading a `morphTo` relationship, Eloquent will run multiple queries to fetch each type of related model. You may add additional constraints to each of these queries using the `MorphTo` relation's `constrain` method: - use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\MorphTo; $comments = Comment::with(['commentable' => function (MorphTo $morphTo) { $morphTo->constrain([ - Post::class => function (Builder $query) { + Post::class => function ($query) { $query->whereNull('hidden_at'); }, - Video::class => function (Builder $query) { + Video::class => function ($query) { $query->where('type', 'educational'); }, ]); @@ -1769,9 +1768,8 @@ In this example, Eloquent will only eager load posts that have not been hidden a You may sometimes find yourself needing to check for the existence of a relationship while simultaneously loading the relationship based on the same conditions. For example, you may wish to only retrieve `User` models that have child `Post` models matching a given query condition while also eager loading the matching posts. You may accomplish this using the `withWhereHas` method: use App\Models\User; - use Illuminate\Database\Eloquent\Builder; - $users = User::withWhereHas('posts', function (Builder $query) { + $users = User::withWhereHas('posts', function ($query) { $query->where('featured', true); })->get(); From 7ac8f766a5c487e331f134b1e422e7b3c9d24c53 Mon Sep 17 00:00:00 2001 From: Thai Nguyen Hung Date: Thu, 22 Jun 2023 18:24:28 +0700 Subject: [PATCH 0980/2609] typo in provider (#8874) --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index fce32328098..205d79c611e 100644 --- a/cache.md +++ b/cache.md @@ -452,7 +452,7 @@ To register the custom cache driver with Laravel, we will use the `extend` metho use Illuminate\Support\Facades\Cache; use Illuminate\Support\ServiceProvider; - class CacheServiceProvider extends ServiceProvider + class AppServiceProvider extends ServiceProvider { /** * Register any application services. From b94720ad3eb986b0cb4671edbcf73655c5d84540 Mon Sep 17 00:00:00 2001 From: Chris Brown Date: Fri, 23 Jun 2023 17:56:58 -0400 Subject: [PATCH 0981/2609] Fix typo (#8879) --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index 81f8c0ab322..2fce6135605 100644 --- a/octane.md +++ b/octane.md @@ -178,7 +178,7 @@ By default, applications running via Octane generate links prefixed with `http:/ > **Note** > If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). -In production environments, you should serve your Octane application behind a traditional web server such as a Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. +In production environments, you should serve your Octane application behind a traditional web server such as Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. In the Nginx configuration example below, Nginx will serve the site's static assets and proxy requests to the Octane server that is running on port 8000: From 0dd85c8f3d2f4bb2418d50e37a054a9981c645b4 Mon Sep 17 00:00:00 2001 From: Anjorin Damilare Date: Mon, 26 Jun 2023 18:20:52 +0100 Subject: [PATCH 0982/2609] [10.x] Fix parameter type in hash cast example (#8881) --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index ba0ba1a33d2..ce1b9beb789 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -670,7 +670,7 @@ A classic example of an inbound only cast is a "hashing" cast. For example, we m * Create a new cast class instance. */ public function __construct( - protected string $algorithm = null, + protected string|null $algorithm = null, ) {} /** From 1a031bd5974695ccd89bd5a0ab8bb7b4a3ea4c8b Mon Sep 17 00:00:00 2001 From: Shaya Ulman Date: Mon, 26 Jun 2023 20:34:01 +0300 Subject: [PATCH 0983/2609] [10.x] Update pagination.md - Document `through` method (#8883) * [10.x] Update pagination.md - Document `through` method * [10.x] Update pagination.md - fix: add `through` $callback paramater * Update pagination.md --------- Co-authored-by: Taylor Otwell --- pagination.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pagination.md b/pagination.md index 9d8783fd746..03a6d0f7ef9 100644 --- a/pagination.md +++ b/pagination.md @@ -352,6 +352,7 @@ Method | Description `$paginator->url(https://github.com/$page)` | Get the URL for a given page number. `$paginator->getPageName()` | Get the query string variable used to store the page. `$paginator->setPageName($name)` | Set the query string variable used to store the page. +`$paginator->through($callback)` | Transform each item using a callback. ## Cursor Paginator Instance Methods From 411ce51d536c0c1c0f27b382bc2849f827044baf Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Tue, 27 Jun 2023 09:12:51 -0400 Subject: [PATCH 0984/2609] Update authorization.md (#8885) removes unnecessary import statement --- authorization.md | 1 - 1 file changed, 1 deletion(-) diff --git a/authorization.md b/authorization.md index b5e0d8531eb..1c7eb9ed902 100644 --- a/authorization.md +++ b/authorization.md @@ -633,7 +633,6 @@ The `authorizeResource` method accepts the model's class name as its first argum use App\Http\Controllers\Controller; use App\Models\Post; - use Illuminate\Http\Request; class PostController extends Controller { From 7de1cc1717934174242ead4591574be5031cba0b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Jun 2023 08:33:16 -0500 Subject: [PATCH 0985/2609] withQueryParameters --- http-client.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/http-client.md b/http-client.md index 54e82eb4f32..05f494933c5 100644 --- a/http-client.md +++ b/http-client.md @@ -118,6 +118,13 @@ When making `GET` requests, you may either append a query string to the URL dire 'page' => 1, ]); +Alternatively, the `withQueryParameters` method may be used: + + Http::retry(3, 100)->withQueryParameters([ + 'name' => 'Taylor', + 'page' => 1, + ])->get('http://example.com/users') + #### Sending Form URL Encoded Requests From d5fe657b1439363cc7d5d05ebc0bd183355bd138 Mon Sep 17 00:00:00 2001 From: CalebW Date: Tue, 27 Jun 2023 08:37:20 -0500 Subject: [PATCH 0986/2609] [10.x] Add documentation on how to disable caching for custom casts (#8866) * Add documentation on how to disable caching for custom casts * formatting --------- Co-authored-by: Taylor Otwell --- eloquent-mutators.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index ce1b9beb789..351dfa993d5 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -627,6 +627,22 @@ When casting to value objects, any changes made to the value object will automat > **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. + +#### Value Object Caching + +When attributes that are cast to value objects are resolved, they are cached by Eloquent. Therefore, the same object instance will be returned if the attribute is accessed again. + +If you would like to disable the object caching behavior of custom cast classes, you may declare a public `withoutObjectCaching` property on your custom cast class: + +```php +class Address implements CastsAttributes +{ + public bool $withoutObjectCaching = true; + + // ... +} +``` + ### Array / JSON Serialization From bc2dd4f1acd95d3d4a507a057570200f5f3e62c2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Jun 2023 08:42:14 -0500 Subject: [PATCH 0987/2609] whenAggregated --- eloquent-resources.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/eloquent-resources.md b/eloquent-resources.md index 7bd187198c4..dc6bbe5827e 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -635,6 +635,15 @@ The `whenCounted` method may be used to conditionally include a relationship's c In this example, if the `posts` relationship's count has not been loaded, the `posts_count` key will be removed from the resource response before it is sent to the client. +Other types of aggregates, such as `avg`, `sum`, `min`, and `max` may also be conditionally loaded using the `whenAggregated` method: + +```php +'words_avg' => $this->whenAggregated('posts', 'words', 'avg'), +'words_sum' => $this->whenAggregated('posts', 'words', 'sum'), +'words_min' => $this->whenAggregated('posts', 'words', 'min'), +'words_max' => $this->whenAggregated('posts', 'words', 'max'), +``` + #### Conditional Pivot Information From 13bced9e7e90d9d772e581dd971207430f1158b3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Jun 2023 08:43:46 -0500 Subject: [PATCH 0988/2609] document hasAny on blade component attributes --- blade.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/blade.md b/blade.md index 73b5ecdc825..1ffbe99053b 100644 --- a/blade.md +++ b/blade.md @@ -1051,6 +1051,14 @@ If you would like to check if an attribute is present on the component, you may @endif ``` +The `hasAny` method may be used to determine if any of the given attributes are present on the component: + +```blade +@if ($attributes->hasAny(['href', ':href', 'v-bind:href'])) +
    One of the attributes is present
    +@endif +``` + You may retrieve a specific attribute's value using the `get` method: ```blade From 0d1bd56a83471c489d36ca7e51ec85759989f2d6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Jun 2023 08:45:13 -0500 Subject: [PATCH 0989/2609] has and hasAny doc improvements --- blade.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/blade.md b/blade.md index 1ffbe99053b..9e8186c23f3 100644 --- a/blade.md +++ b/blade.md @@ -1051,6 +1051,14 @@ If you would like to check if an attribute is present on the component, you may @endif ``` +If an array is passed to the `has` method, the method will determine if all of the given attributes are present on the component: + +```blade +@if ($attributes->has(['name', 'class'])) +
    All of the attributes are present
    +@endif +``` + The `hasAny` method may be used to determine if any of the given attributes are present on the component: ```blade From a8843c587a358e47c2dfa46d993fa4adb69a1d54 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Jun 2023 08:52:27 -0500 Subject: [PATCH 0990/2609] http client middleware docs --- http-client.md | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/http-client.md b/http-client.md index 05f494933c5..63963b4cc54 100644 --- a/http-client.md +++ b/http-client.md @@ -332,36 +332,49 @@ If you would like to perform some additional logic before the exception is throw ### Guzzle Middleware -Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guzzle Middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html) to manipulate the outgoing request or inspect the incoming response. To manipulate the outgoing request, register a Guzzle middleware via the `withMiddleware` method in combination with Guzzle's `mapRequest` middleware factory: +Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guzzle Middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html) to manipulate the outgoing request or inspect the incoming response. To manipulate the outgoing request, register a Guzzle middleware via the `withRequestMiddleware` method: - use GuzzleHttp\Middleware; use Illuminate\Support\Facades\Http; use Psr\Http\Message\RequestInterface; - $response = Http::withMiddleware( - Middleware::mapRequest(function (RequestInterface $request) { - $request = $request->withHeader('X-Example', 'Value'); - - return $request; - }) + $response = Http::withRequestMiddleware( + function (RequestInterface $request) { + return $request->withHeader('X-Example', 'Value'); + } )->get('http://example.com/'); -Likewise, you can inspect the incoming HTTP response by registering a middleware via the `withMiddleware` method in combination with Guzzle's `mapResponse` middleware factory: +Likewise, you can inspect the incoming HTTP response by registering a middleware via the `withResponseMiddleware` method: - use GuzzleHttp\Middleware; use Illuminate\Support\Facades\Http; use Psr\Http\Message\ResponseInterface; - $response = Http::withMiddleware( - Middleware::mapResponse(function (ResponseInterface $response) { + $response = Http::withResponseMiddleware( + function (ResponseInterface $response) { $header = $response->getHeader('X-Example'); // ... - + return $response; - }) + } )->get('http://example.com/'); + +#### Global Middleware + +Sometimes, you may want to register a middleware that applies to every outgoing request and incoming response. To accomplish this, you may use the `globalRequestMiddleware` and `globalResponseMiddleware` methods. Typically, these methods should be invoked in the `boot` method of your application's `AppServiceProvider`: + +```php +use Illuminate\Support\Facades\Http; + +Http::globalRequestMiddleware(fn ($request) => $request->withHeader( + 'User-Agent', 'Example Application/1.0' +)); + +Http::globalResponseMiddleware(fn ($response) => $response->withHeader( + 'X-Finished-At', now()->toDateTimeString() +)); +``` + ### Guzzle Options From b2ca25e4ca9d0d622eabdc8adb5600774b3c9f23 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Tue, 27 Jun 2023 22:05:33 +0330 Subject: [PATCH 0991/2609] add `toCssStyles` to helpers (#8888) --- helpers.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/helpers.md b/helpers.md index 66c41d0b93e..97ad81f047e 100644 --- a/helpers.md +++ b/helpers.md @@ -70,6 +70,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::sortRecursive](#method-array-sort-recursive) [Arr::sortRecursiveDesc](#method-array-sort-recursive-desc) [Arr::toCssClasses](#method-array-to-css-classes) +[Arr::toCssStyles](#method-array-to-css-styles) [Arr::undot](#method-array-undot) [Arr::where](#method-array-where) [Arr::whereNotNull](#method-array-where-not-null) @@ -1024,6 +1025,23 @@ The `Arr::toCssClasses` conditionally compiles a CSS class string. The method ac 'p-4 bg-red' */ + +#### `Arr::toCssStyles()` {.collection-method} + +The `Arr::toCssStyles` conditionally compiles a CSS style string. The method accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list: + +```php +$hasColor = true; + +$array = ['background-color: blue', 'color: blue' => $hasColor]; + +$classes = Arr::toCssStyles($array); + +/* + 'background-color: blue; color: blue;' +*/ +``` + This method powers Laravel's functionality allowing [merging classes with a Blade component's attribute bag](/docs/{{version}}/blade#conditionally-merge-classes) as well as the `@class` [Blade directive](/docs/{{version}}/blade#conditional-classes). From 32dedad664a20aca18240f59e0713af830c23d3e Mon Sep 17 00:00:00 2001 From: Fernando Garcia Date: Tue, 27 Jun 2023 13:33:24 -0600 Subject: [PATCH 0992/2609] [10.x] Add anyFilled request method (#8887) * [10.x] Add anyFilled request method * Update requests.md * Update requests.md --------- Co-authored-by: Taylor Otwell --- requests.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/requests.md b/requests.md index 4f19393dbbf..64ab215f823 100644 --- a/requests.md +++ b/requests.md @@ -367,6 +367,12 @@ When given an array, the `has` method will determine if all of the specified val // ... } +The `hasAny` method returns `true` if any of the specified values are present: + + if ($request->hasAny(['name', 'email'])) { + // ... + } + The `whenHas` method will execute the given closure if a value is present on the request: $request->whenHas('name', function (string $input) { @@ -381,15 +387,15 @@ A second closure may be passed to the `whenHas` method that will be executed if // The "name" value is not present... }); -The `hasAny` method returns `true` if any of the specified values are present: +If you would like to determine if a value is present on the request and is not an empty string, you may use the `filled` method: - if ($request->hasAny(['name', 'email'])) { + if ($request->filled('name')) { // ... } -If you would like to determine if a value is present on the request and is not an empty string, you may use the `filled` method: +The `anyFilled` method returns `true` if any of the specified values is not an empty string: - if ($request->filled('name')) { + if ($request->anyFilled(['name', 'email'])) { // ... } From 14f067aa12c9d78f9c2e375bae52854c80988432 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:55:38 +0330 Subject: [PATCH 0993/2609] [10.x] Add `fullUrlWithoutQuery` method in requests (#8890) * add `fullUrlWithoutQuery` method in requests * Update requests.md --------- Co-authored-by: Taylor Otwell --- requests.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/requests.md b/requests.md index 64ab215f823..6089d056ec2 100644 --- a/requests.md +++ b/requests.md @@ -135,6 +135,12 @@ If you would like to append query string data to the current URL, you may call t $request->fullUrlWithQuery(['type' => 'phone']); +If you would like to get the current URL without a given query string parameter, you may utilize the `fullUrlWithoutQuery` method: + +```php +$request->fullUrlWithoutQuery(['type']); +``` + #### Retrieving The Request Host From 6ec5a3f5c37a1d10f9a16e8fcd76e7fb121a8013 Mon Sep 17 00:00:00 2001 From: Mathew Westlake-Toms Date: Wed, 5 Jul 2023 17:03:51 +0100 Subject: [PATCH 0994/2609] Update dusk.md (#8894) --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index 7c3877599da..c378ced06b6 100644 --- a/dusk.md +++ b/dusk.md @@ -236,7 +236,7 @@ If you had test failures the last time you ran the `dusk` command, you may save php artisan dusk:fails ``` -The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/9.5/annotations.html#group): +The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/10.1/annotations.html#group): ```shell php artisan dusk --group=foo From 399143543db9d03e4ea205f059d5d7a384a848c1 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Fri, 7 Jul 2023 16:36:57 +1000 Subject: [PATCH 0995/2609] [10.x] Update SSR server location and include `inertia:start-ssr` command (#8896) * Update SSR server location * Include `inertia:start-ssr` command * Update vite.md --------- Co-authored-by: Taylor Otwell --- vite.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vite.md b/vite.md index bcac4c9bddc..a58ea2feddc 100644 --- a/vite.md +++ b/vite.md @@ -646,7 +646,13 @@ Then, to build and start the SSR server, you may run the following commands: ```sh npm run build -node bootstrap/ssr/ssr.mjs +node bootstrap/ssr/ssr.js +``` + +If you are using [SSR with Inertia](https://inertiajs.com/server-side-rendering), you may instead use the `inertia:start-ssr` Artisan command to start the SSR server: + +```sh +php artisan inertia:start-ssr ``` > **Note** From b96c6a8cfd53f7c226a867224f1e77000036271d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Felipe=20Tissot?= Date: Fri, 7 Jul 2023 03:37:40 -0300 Subject: [PATCH 0996/2609] Add missing Builder import to Dynamic Scopes (#8895) Co-authored-by: Luis Felipe Tissot --- eloquent.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent.md b/eloquent.md index bf88f285d29..d105ecbae67 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1343,6 +1343,7 @@ Sometimes you may wish to define a scope that accepts parameters. To get started namespace App\Models; + use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class User extends Model From 9114ace0c2cf4f6ba404324c14b86d5f0ff63619 Mon Sep 17 00:00:00 2001 From: Ped Date: Sat, 8 Jul 2023 22:34:39 +0200 Subject: [PATCH 0997/2609] Fix erroneous typehint (#8900) Fixes the error `{closure}(): Argument #1 ($match) must be of type Illuminate\Support\Stringable, array given` --- helpers.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/helpers.md b/helpers.md index 97ad81f047e..61ea66402bf 100644 --- a/helpers.md +++ b/helpers.md @@ -3003,10 +3003,9 @@ The `replaceMatches` method replaces all portions of a string matching a pattern The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given pattern, allowing you to perform the replacement logic within the closure and return the replaced value: use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - $replaced = Str::of('123')->replaceMatches('/\d/', function (Stringable $match) { - return '['.$match[0].']'; + $replaced = Str::of('123')->replaceMatches('/\d/', function (array $matches) { + return '['.$matches[0].']'; }); // '[1][2][3]' From 316714a2ee52baa82a36a0eff78afaa1cf295da3 Mon Sep 17 00:00:00 2001 From: Sam Mettam <42416996+Redtama@users.noreply.github.com> Date: Sat, 8 Jul 2023 21:37:43 +0100 Subject: [PATCH 0998/2609] Update mail.md (#8897) * Update mail.md Fixed `from` being referred to as a method when it is in fact a parameter to the Envelope constructor. * Tweaked wording slightly * Update mail.md --------- Co-authored-by: Taylor Otwell --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 3f3398ac102..7dd50f65d05 100644 --- a/mail.md +++ b/mail.md @@ -212,7 +212,7 @@ If you would like, you may also specify a `replyTo` address: #### Using A Global `from` Address -However, if your application uses the same "from" address for all of its emails, it can become cumbersome to call the `from` method in each mailable class you generate. Instead, you may specify a global "from" address in your `config/mail.php` configuration file. This address will be used if no other "from" address is specified within the mailable class: +However, if your application uses the same "from" address for all of its emails, it can become cumbersome to add it to each mailable class you generate. Instead, you may specify a global "from" address in your `config/mail.php` configuration file. This address will be used if no other "from" address is specified within the mailable class: 'from' => [ 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), From ebefbe94c1a3dd008e15b4951e2574ba39be3acc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 8 Jul 2023 16:32:30 -0500 Subject: [PATCH 0999/2609] document mailer send --- mail.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 7dd50f65d05..bf0226a1089 100644 --- a/mail.md +++ b/mail.md @@ -47,7 +47,7 @@ Within your `mail` configuration file, you will find a `mailers` configuration a ### Driver / Transport Prerequisites -The API based drivers such as Mailgun and Postmark are often simpler and faster than sending mail via SMTP servers. Whenever possible, we recommend that you use one of these drivers. +The API based drivers such as Mailgun, Postmark, and MailerSend are often simpler and faster than sending mail via SMTP servers. Whenever possible, we recommend that you use one of these drivers. #### Mailgun Driver @@ -137,6 +137,27 @@ 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: + +```shell +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 +MAIL_MAILER=mailersend +MAIL_FROM_ADDRESS=app@yourdomain.com +MAIL_FROM_NAME="App Name" + +MAILERSEND_API_KEY=your-api-key +``` + +To learn more about MailerSend, including how to use hosted templates, consult the [MailerSend driver documentation](https://github.com/mailersend/mailersend-laravel-driver#usage). + ### Failover Configuration From 34d8cf82c421c34fe13b4ec62ab3e067d44946f8 Mon Sep 17 00:00:00 2001 From: basvdhoeven <58695285+basvdhoeven@users.noreply.github.com> Date: Sat, 8 Jul 2023 23:41:49 +0200 Subject: [PATCH 1000/2609] Add warning to switch docker context (#8901) * Add warning to switch docker context * formatting --------- Co-authored-by: Bas van der Hoeven Co-authored-by: Taylor Otwell --- installation.md | 10 +++++++++- sail.md | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/installation.md b/installation.md index e5b856c5528..565b6cfce5a 100644 --- a/installation.md +++ b/installation.md @@ -159,7 +159,15 @@ Once these tools are installed, you may open any Laravel project by executing th ### Getting Started On Linux -If you're developing on Linux and [Docker Compose](https://docs.docker.com/compose/install/) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: +If you're developing on Linux and [Docker Compose](https://docs.docker.com/compose/install/) is already installed, you can use a simple terminal command to create a new Laravel project. + +First, if you are using Docker Desktop for Linux, you should execute the following command. If you are not using Docker Desktop for Linux, you may skip this step: + +```shell +docker context use default +``` + +Then, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: ```shell curl -s https://laravel.build/example-app | bash diff --git a/sail.md b/sail.md index 411188c71df..ccf4975cfd9 100644 --- a/sail.md +++ b/sail.md @@ -62,6 +62,9 @@ Finally, you may start Sail. To continue learning how to use Sail, please contin ./vendor/bin/sail up ``` +> **Warning** +> If you are using Docker Desktop for Linux, you should use the `default` Docker context by executing the following command: `docker context use default`. + #### Adding Additional Services From ccec1c391cc4afb1ee7285396e0ab76391a82c9c Mon Sep 17 00:00:00 2001 From: AJ Meireles <60591772+ajmeireles@users.noreply.github.com> Date: Sun, 9 Jul 2023 23:44:42 -0300 Subject: [PATCH 1001/2609] Add Doc: `ShouldBeEncrypted` Interface to Queue (#8904) * Add: ShouldBeEncrypted Queue Docummentation * formatting --------- Co-authored-by: Taylor Otwell --- queues.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/queues.md b/queues.md index 9c14e34eb94..0d462ce84e6 100644 --- a/queues.md +++ b/queues.md @@ -7,6 +7,7 @@ - [Generating Job Classes](#generating-job-classes) - [Class Structure](#class-structure) - [Unique Jobs](#unique-jobs) + - [Encrypted Jobs](#encrypted-jobs) - [Job Middleware](#job-middleware) - [Rate Limiting](#rate-limiting) - [Preventing Job Overlaps](#preventing-job-overlaps) @@ -338,6 +339,21 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t > **Note** > If you only need to limit the concurrent processing of a job, use the [`WithoutOverlapping`](/docs/{{version}}/queues#preventing-job-overlaps) job middleware instead. + +### Encrypted Jobs + +Laravel allows you to ensure the privacy and integrity of a job's data via [encryption](/docs/{{version}}/encryption). To get started, simply add the `ShouldBeEncrypted` interface to the job class. Once this interface has been added to the class, Laravel will automatically encrypt your job before pushing it onto a queue: + + ## Job Middleware From 13f3af44dc2ddf9ade8af99b0fe08d88c6f11008 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 10 Jul 2023 06:17:32 +0330 Subject: [PATCH 1002/2609] [10.x] Add `repeat` method into Str (#8903) * add repeat for str * add `repeat` method in fluent str * Update helpers.md * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/helpers.md b/helpers.md index 61ea66402bf..c7f9af20093 100644 --- a/helpers.md +++ b/helpers.md @@ -144,6 +144,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::pluralStudly](#method-str-plural-studly) [Str::random](#method-str-random) [Str::remove](#method-str-remove) +[Str::repeat](#method-str-repeat) [Str::replace](#method-str-replace) [Str::replaceArray](#method-str-replace-array) [Str::replaceFirst](#method-str-replace-first) @@ -227,6 +228,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [plural](#method-fluent-str-plural) [prepend](#method-fluent-str-prepend) [remove](#method-fluent-str-remove) +[repeat](#method-fluent-str-repeat) [replace](#method-fluent-str-replace) [replaceArray](#method-fluent-str-replace-array) [replaceFirst](#method-fluent-str-replace-first) @@ -1893,6 +1895,21 @@ The `Str::remove` method removes the given value or array of values from the str You may also pass `false` as a third argument to the `remove` method to ignore case when removing strings. + +#### `Str::repeat()` {.collection-method} + +The `Str::repeat` method repeats the given string: + +```php +use Illuminate\Support\Str; + +$string = 'a'; + +$repeat = Str::repeat($string, 5); + +// aaaaa +``` + #### `Str::replace()` {.collection-method} @@ -2937,6 +2954,19 @@ The `remove` method removes the given value or array of values from the string: You may also pass `false` as a second parameter to ignore case when removing strings. + +#### `repeat` {.collection-method} + +The `repeat` method repeats the given string: + +```php +use Illuminate\Support\Str; + +$repeated = Str::of('a')->repeat(5); + +// aaaaa +``` + #### `replace` {.collection-method} From 629fbae4339ad21af23d24a6e8e9b93925b15cbb Mon Sep 17 00:00:00 2001 From: Muhammad Murtaza Date: Tue, 11 Jul 2023 08:08:29 +0500 Subject: [PATCH 1003/2609] Fix placeholder in Precognition documentation (#8907) The placeholder {version} in the text was corrected to {{version}} to ensure proper rendering. This change improves the accuracy of the documentation. --- precognition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precognition.md b/precognition.md index 1394a1a8c2d..ef000c96b31 100644 --- a/precognition.md +++ b/precognition.md @@ -365,7 +365,7 @@ const submit = (e) => { Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend Alpine application. To illustrate how it works, let's build a form for creating new users within our application. -First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{version}/validation#form-request-validation) to house the route's validation rules: +First, to enable Precognition for a route, the `HandlePrecognitiveRequests` middleware should be added to the route definition. You should also create a [form request](/docs/{{version}}/validation#form-request-validation) to house the route's validation rules: ```php use App\Http\Requests\CreateUserRequest; From 0f13e00f26997986c3dbc60238ae547bff8be367 Mon Sep 17 00:00:00 2001 From: Chris Rossi <123430556+crossiatlas@users.noreply.github.com> Date: Tue, 11 Jul 2023 22:50:23 +0930 Subject: [PATCH 1004/2609] [10.x] Add missing Sail configuration for Selenium (#8909) * [10.x] Add missing Apple Silicon config for Selenium Add missing extra_hosts line for Selenium Container to work when using Dusk via Sail on Apple Silicon chip As per: https://github.com/laravel/sail/blob/1.x/stubs/selenium.stub * [10.x] Update Sail Selenium documentation for non-Apple hardware --- sail.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sail.md b/sail.md index ccf4975cfd9..3486d96fad1 100644 --- a/sail.md +++ b/sail.md @@ -297,6 +297,8 @@ By default, Sail will create a dedicated `testing` database so that your tests d ```yaml selenium: image: 'selenium/standalone-chrome' + extra_hosts: + - 'host.docker.internal:host-gateway' volumes: - '/dev/shm:/dev/shm' networks: @@ -326,6 +328,8 @@ If your local machine contains an Apple Silicon chip, your `selenium` service mu ```yaml selenium: image: 'seleniarm/standalone-chromium' + extra_hosts: + - 'host.docker.internal:host-gateway' volumes: - '/dev/shm:/dev/shm' networks: From 39446e632107fbd3ab56fd69a83db88ddd8527d3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Jul 2023 09:13:48 -0500 Subject: [PATCH 1005/2609] document dumpRawSql --- queries.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/queries.md b/queries.md index cd45ba47d66..798df641b6f 100644 --- a/queries.md +++ b/queries.md @@ -1035,3 +1035,9 @@ You may use the `dd` and `dump` methods while building a query to dump the curre DB::table('users')->where('votes', '>', 100)->dd(); DB::table('users')->where('votes', '>', 100)->dump(); + +The `dumpRawSql` and `ddRawSql` methods may be invoked on a query to dump the query's SQL with all parameter bindings properly substituted: + + DB::table('users')->where('votes', '>', 100)->dumpRawSql(); + + DB::table('users')->where('votes', '>', 100)->ddRawSql(); From 3e93fc1b262b0ab53328630f7ee334519a207752 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Jul 2023 09:15:46 -0500 Subject: [PATCH 1006/2609] document new mailable assertions --- mail.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mail.md b/mail.md index bf0226a1089..4bfe2664945 100644 --- a/mail.md +++ b/mail.md @@ -1030,16 +1030,18 @@ You may use the `Mail` facade's `fake` method to prevent mail from being sent. A // Assert a mailable was not sent... Mail::assertNotSent(AnotherMailable::class); + + // Assert 3 total mailables were sent... + Mail::assertSentCount(3); } } If you are queueing mailables for delivery in the background, you should use the `assertQueued` method instead of `assertSent`: Mail::assertQueued(OrderShipped::class); - Mail::assertNotQueued(OrderShipped::class); - Mail::assertNothingQueued(); + Mail::assertQueuedCount(3); You may pass a closure to the `assertSent`, `assertNotSent`, `assertQueued`, or `assertNotQueued` 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: From 43ffb9cbc2e286c61e0ee5dc35703d23908ad004 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Jul 2023 09:19:29 -0500 Subject: [PATCH 1007/2609] document str::isurl --- helpers.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/helpers.md b/helpers.md index c7f9af20093..0538ab2c98b 100644 --- a/helpers.md +++ b/helpers.md @@ -127,6 +127,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::isAscii](#method-str-is-ascii) [Str::isJson](#method-str-is-json) [Str::isUlid](#method-str-is-ulid) +[Str::isUrl](#method-str-is-url) [Str::isUuid](#method-str-is-uuid) [Str::kebab](#method-kebab-case) [Str::lcfirst](#method-str-lcfirst) @@ -208,6 +209,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [isNotEmpty](#method-fluent-str-is-not-empty) [isJson](#method-fluent-str-is-json) [isUlid](#method-fluent-str-is-ulid) +[isUrl](#method-fluent-str-is-url) [isUuid](#method-fluent-str-is-uuid) [kebab](#method-fluent-str-kebab) [lcfirst](#method-fluent-str-lcfirst) @@ -1621,6 +1623,21 @@ The `Str::isJson` method determines if the given string is valid JSON: // false + +#### `Str::isUrl()` {.collection-method} + +The `Str::isUrl` method determines if the given string is a valid URL: + + use Illuminate\Support\Str; + + $isUrl = Str::isUrl('http://example.com/'); + + // true + + $isUrl = Str::isUrl('laravel'); + + // false + #### `Str::isUlid()` {.collection-method} @@ -2645,6 +2662,21 @@ The `isUlid` method determines if a given string is a ULID: // false + +#### `isUrl` {.collection-method} + +The `isUrl` method determines if a given string is a URL: + + use Illuminate\Support\Str; + + $result = Str::of('http://example.com/')->isUrl(); + + // true + + $result = Str::of('Taylor')->isUrl(); + + // false + #### `isUuid` {.collection-method} From c0297387a87872bccd5b07c2eaf06d58b69ec7ce Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Jul 2023 09:24:29 -0500 Subject: [PATCH 1008/2609] document data_forget --- helpers.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/helpers.md b/helpers.md index 0538ab2c98b..59f931fbe1b 100644 --- a/helpers.md +++ b/helpers.md @@ -78,6 +78,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [data_fill](#method-data-fill) [data_get](#method-data-get) [data_set](#method-data-set) +[data_forget](#method-data-forget) [head](#method-head) [last](#method-last)
    @@ -1215,6 +1216,37 @@ By default, any existing values are overwritten. If you wish to only set a value // ['products' => ['desk' => ['price' => 100]]] + +#### `data_forget()` {.collection-method} + +The `data_forget` function removes a value within a nested array or object using "dot" notation: + + $data = ['products' => ['desk' => ['price' => 100]]]; + + data_forget($data, 'products.desk.price'); + + // ['products' => ['desk' => []]] + +This function also accepts wildcards using asterisks and will remove values on the target accordingly: + + $data = [ + 'products' => [ + ['name' => 'Desk 1', 'price' => 100], + ['name' => 'Desk 2', 'price' => 150], + ], + ]; + + data_forget($data, 'products.*.price'); + + /* + [ + 'products' => [ + ['name' => 'Desk 1'], + ['name' => 'Desk 2'], + ], + ] + */ + #### `head()` {.collection-method} From e4113bf78a9f9d2c6bc85f93dbefcb3d0419a715 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Jul 2023 13:15:18 -0500 Subject: [PATCH 1009/2609] sub-minute scheduling --- scheduling.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index c1fd1024a46..2754dc6423d 100644 --- a/scheduling.md +++ b/scheduling.md @@ -12,6 +12,7 @@ - [Background Tasks](#background-tasks) - [Maintenance Mode](#maintenance-mode) - [Running The Scheduler](#running-the-scheduler) + - [Sub-Minute Schedule Tasks](#sub-minute-scheduled-tasks) - [Running The Scheduler Locally](#running-the-scheduler-locally) - [Task Output](#task-output) - [Task Hooks](#task-hooks) @@ -106,6 +107,13 @@ We've already seen a few examples of how you may configure a task to run at spec Method | Description ------------- | ------------- `->cron('* * * * *');` | Run the task on a custom cron schedule +`->everySecond();` | Run the task every second +`->everyTwoSeconds();` | Run the task every two seconds +`->everyFiveSeconds();` | Run the task every five seconds +`->everyTenSeconds();` | Run the task every ten seconds +`->everyFifteenSeconds();` | Run the task every fifteen seconds +`->everyTwentySeconds();` | Run the task every twenty seconds +`->everyThirtySeconds();` | Run the task every thirty seconds `->everyMinute();` | Run the task every minute `->everyTwoMinutes();` | Run the task every two minutes `->everyThreeMinutes();` | Run the task every three minutes @@ -345,8 +353,36 @@ So, when using Laravel's scheduler, we only need to add a single cron configurat * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 ``` + +### Sub-Minute Scheduled Tasks + +On most operating systems, cron jobs are limited to running a maximum of once per minute. However, Laravel's scheduler allows you to schedule tasks to run at more frequent intervals, even as often as once per second: + + $schedule->call(function () { + DB::table('recent_users')->delete(); + })->everySecond(); + +When sub-minute tasks are defined within your application, the `schedule:run` command will continue running until the end of the current minute instead of exiting immediately. This allows the command to invoke all required sub-minute tasks throughout the minute. + +Since sub-minute tasks that take longer than expected to run could delay the execution of later sub-minute tasks, it is recommend that all sub-minute tasks dispatch queued jobs to handle the actual task processing: + + use App\Jobs\DeleteRecentUsers; + + $schedule->job(new DeleteRecentUsers)->everyTenSeconds(); + + +#### Interrupting Sub-Minute Tasks + +As the `schedule:run` command runs for the entire minute of invocation when sub-minute tasks are defined, you may sometimes need to interrupt the command when deploying your application. Otherwise, an instance of the `schedule:run` command that is already running would continue using your application's previously deployed code until the current minute ends. + +To interrupt in-progress `schedule:run` invocations, you may add the `schedule:interrupt` command to your application's deployment script. This command should be invoked after your application is finished deploying: + +```shell +php artisan schedule:interrupt +``` + -## Running The Scheduler Locally +### 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: From b898a5757c3317188d75fb793fb3f7ba179e9aa4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Jul 2023 14:05:31 -0500 Subject: [PATCH 1010/2609] wip --- scheduling.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index 2754dc6423d..f043d29cbdc 100644 --- a/scheduling.md +++ b/scheduling.md @@ -364,12 +364,14 @@ On most operating systems, cron jobs are limited to running a maximum of once pe When sub-minute tasks are defined within your application, the `schedule:run` command will continue running until the end of the current minute instead of exiting immediately. This allows the command to invoke all required sub-minute tasks throughout the minute. -Since sub-minute tasks that take longer than expected to run could delay the execution of later sub-minute tasks, it is recommend that all sub-minute tasks dispatch queued jobs to handle the actual task processing: +Since sub-minute tasks that take longer than expected to run could delay the execution of later sub-minute tasks, it is recommend that all sub-minute tasks dispatch queued jobs or background commands to handle the actual task processing: use App\Jobs\DeleteRecentUsers; $schedule->job(new DeleteRecentUsers)->everyTenSeconds(); + $schedule->command('users:delete')->everyTenSeconds()->runInBackground(); + #### Interrupting Sub-Minute Tasks From cb194c6f3c8712e9cbad1d2aefc1b5923437eff5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Jul 2023 14:13:47 -0500 Subject: [PATCH 1011/2609] wip --- scheduling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index f043d29cbdc..b8d77f4d4f7 100644 --- a/scheduling.md +++ b/scheduling.md @@ -12,7 +12,7 @@ - [Background Tasks](#background-tasks) - [Maintenance Mode](#maintenance-mode) - [Running The Scheduler](#running-the-scheduler) - - [Sub-Minute Schedule Tasks](#sub-minute-scheduled-tasks) + - [Sub-Minute Scheduled Tasks](#sub-minute-scheduled-tasks) - [Running The Scheduler Locally](#running-the-scheduler-locally) - [Task Output](#task-output) - [Task Hooks](#task-hooks) From 076bafd492d18a67082d34519f049c787780ad54 Mon Sep 17 00:00:00 2001 From: Peter Hauke <90506472+peterhauke@users.noreply.github.com> Date: Thu, 13 Jul 2023 21:20:02 +0200 Subject: [PATCH 1012/2609] Fix typo (#8914) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index d105ecbae67..0300d71286f 100644 --- a/eloquent.md +++ b/eloquent.md @@ -48,7 +48,7 @@ Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoy #### Laravel Bootcamp -If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Eloquent. It's a great way to get a tour of everything the Laravel and Eloquent have to offer. +If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Eloquent. It's a great way to get a tour of everything that Laravel and Eloquent have to offer. ## Generating Model Classes From c0f53b8758c436fc51aadd96723d9704bbeb707b Mon Sep 17 00:00:00 2001 From: James Brooks Date: Fri, 14 Jul 2023 16:03:44 +0100 Subject: [PATCH 1013/2609] [10.x] Document new Slack Block Kit Notifications & Socialite Slack Provider (#8906) * Document new Slack BlockKit API * wip * formatting * Document Slack driver in Socialite * wip * Add section for notifying external workspaces * formatting * Add Socialite Slack bot token info * wip * formatting --------- Co-authored-by: Taylor Otwell --- notifications.md | 195 +++++++++++++++++++++++++++++++++-------------- socialite.md | 32 +++++++- 2 files changed, 167 insertions(+), 60 deletions(-) diff --git a/notifications.md b/notifications.md index f8a4931969a..cd716726ccf 100644 --- a/notifications.md +++ b/notifications.md @@ -43,8 +43,9 @@ - [Slack Notifications](#slack-notifications) - [Prerequisites](#slack-prerequisites) - [Formatting Slack Notifications](#formatting-slack-notifications) - - [Slack Attachments](#slack-attachments) + - [Slack Interactivity](#slack-interactivity) - [Routing Slack Notifications](#routing-slack-notifications) + - [Notifying External Slack Workspaces](#notifying-external-slack-workspaces) - [Localizing Notifications](#localizing-notifications) - [Testing](#testing) - [Notification Events](#notification-events) @@ -1054,20 +1055,39 @@ To route Vonage notifications to the proper phone number, define a `routeNotific ### Prerequisites -Before you can send notifications via Slack, you must install the Slack notification channel via Composer: +Before sending Slack notifications, you should install the Slack notification channel via Composer: ```shell composer require laravel/slack-notification-channel ``` -You will also need to create a [Slack App](https://api.slack.com/apps?new_app=1) for your team. After creating the App, you should configure an "Incoming Webhook" for the workspace. Slack will then provide you with a webhook URL that you may use when [routing Slack notifications](#routing-slack-notifications). +Additionally, you must create a [Slack App](https://api.slack.com/apps?new_app=1) for your Slack workspace. + +If you only need to send notifications to the same Slack workspace that the App is created in, you should ensure that your App has the `chat:write`, `chat:write.public`, and `chat:write.customize` scopes. These scopes can be added from the "OAuth & Permissions" App management tab within Slack. + +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'), + ], + ], + + +#### App Distribution + +If your application will be sending notifications to external Slack workspaces that are owned by your application's users, you will need to "distribute" your App via Slack. App distribution can be managed from your App's "Manage Distribution" tab within Slack. Once your App has been distributed, you may use [Socialite](/docs/{{version}}/socialite) to [obtain Slack Bot tokens](/docs/{{version}}/socialite#slack-bot-scopes) on behalf of your application's users. ### Formatting Slack Notifications -If a notification supports being sent as a Slack message, you should define a `toSlack` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\SlackMessage` instance. Slack messages may contain text content as well as an "attachment" that formats additional text or an array of fields. Let's take a look at a basic `toSlack` example: +If a notification supports being sent as a Slack message, you should define a `toSlack` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Slack\SlackMessage` instance. You can construct rich notifications using [Slack's Block Kit API](https://api.slack.com/block-kit). The following example may be previewed in [Slack's Block Kit builder](https://app.slack.com/block-kit-builder/T01KWS6K23Z#%7B%22blocks%22:%5B%7B%22type%22:%22header%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Invoice%20Paid%22%7D%7D,%7B%22type%22:%22context%22,%22elements%22:%5B%7B%22type%22:%22plain_text%22,%22text%22:%22Customer%20%231234%22%7D%5D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22An%20invoice%20has%20been%20paid.%22%7D,%22fields%22:%5B%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Invoice%20No:*%5Cn1000%22%7D,%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Invoice%20Recipient:*%5Cntaylor@laravel.com%22%7D%5D%7D,%7B%22type%22:%22divider%22%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Congratulations!%22%7D%7D%5D%7D): - use Illuminate\Notifications\Messages\SlackMessage; + use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; + use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; + use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject; + use Illuminate\Notifications\Slack\SlackMessage; /** * Get the Slack representation of the notification. @@ -1075,88 +1095,146 @@ If a notification supports being sent as a Slack message, you should define a `t public function toSlack(object $notifiable): SlackMessage { return (new SlackMessage) - ->content('One of your invoices has been paid!'); + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->contextBlock(function (ContextBlock $block) { + $block->text('Customer #1234'); + }) + ->sectionBlock(function (SectionBlock $block) { + $block->text('An invoice has been paid.'); + $block->field("*Invoice No:*\n1000")->markdown(); + $block->field("*Invoice Recipient:*\ntaylor@laravel.com")->markdown(); + }) + ->dividerBlock() + ->sectionBlock(function (SectionBlock $block) { + $block->text('Congratulations!'); + }); } - -### Slack Attachments + +### Slack Interactivity -You may also add "attachments" to Slack messages. Attachments provide richer formatting options than simple text messages. In this example, we will send an error notification about an exception that occurred in an application, including a link to view more details about the exception: +Slack's Block Kit notification system provides powerful features to [handle user interaction](https://api.slack.com/interactivity/handling). To utilize these features, your Slack App should have "Interactivity" enabled and a "Request URL" configured that points to a URL served by your application. These settings can be managed from the "Interactivity & Shortcuts" App management tab within Slack. - use Illuminate\Notifications\Messages\SlackAttachment; - use Illuminate\Notifications\Messages\SlackMessage; +In the following example, which utilizes the `actiosnBlock` method, Slack will send a `POST` request to your "Request URL" with a payload containing the Slack user who clicked the button, the ID of the clicked button, and more. Your application can then determine the action to take based on the payload. You should also [verify the request](https://api.slack.com/authentication/verifying-requests-from-slack) was made by Slack: + + use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock; + use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; + use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; + use Illuminate\Notifications\Slack\SlackMessage; /** * Get the Slack representation of the notification. */ public function toSlack(object $notifiable): SlackMessage { - $url = url('https://github.com/exceptions/'.$this-%3Eexception-%3Eid); - return (new SlackMessage) - ->error() - ->content('Whoops! Something went wrong.') - ->attachment(function (SlackAttachment $attachment) use ($url) { - $attachment->title('Exception: File Not Found', $url) - ->content('File [background.jpg] was not found.'); - }); + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->contextBlock(function (ContextBlock $block) { + $block->text('Customer #1234'); + }) + ->sectionBlock(function (SectionBlock $block) { + $block->text('An invoice has been paid.'); + }) + ->actionsBlock(function (ActionsBlock $block) { + // ID defaults to "button_acknowledge_invoice"... + $block->button('Acknowledge Invoice')->primary(); + + // Manually configure the ID... + $block->button('Deny')->danger()->id('deny_invoice'); + }); } -Attachments also allow you to specify an array of data that should be presented to the user. The given data will be presented in a table-style format for easy reading: + +#### Confirmation Modals + +If you would like users to be required to confirm an action before it is performed, you may invoke the `confirm` method when defining your button. The `confirm` method accepts a message and a closure which receives a `ConfirmObject` instance: - use Illuminate\Notifications\Messages\SlackAttachment; - use Illuminate\Notifications\Messages\SlackMessage; + use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock; + use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; + use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; + use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject; + use Illuminate\Notifications\Slack\SlackMessage; /** * Get the Slack representation of the notification. */ public function toSlack(object $notifiable): SlackMessage { - $url = url('https://github.com/invoices/'.$this-%3Einvoice-%3Eid); - return (new SlackMessage) - ->success() - ->content('One of your invoices has been paid!') - ->attachment(function (SlackAttachment $attachment) use ($url) { - $attachment->title('Invoice 1322', $url) - ->fields([ - 'Title' => 'Server Expenses', - 'Amount' => '$1,234', - 'Via' => 'American Express', - 'Was Overdue' => ':-1:', - ]); - }); + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->contextBlock(function (ContextBlock $block) { + $block->text('Customer #1234'); + }) + ->sectionBlock(function (SectionBlock $block) { + $block->text('An invoice has been paid.'); + }) + ->actionsBlock(function (ActionsBlock $block) { + $block->button('Acknowledge Invoice') + ->primary() + ->confirm( + 'Acknowledge the payment and send a thank you email?', + function (ConfirmObject $dialog) { + $dialog->confirm('Yes'); + $dialog->deny('No'); + } + ); + }); } - -#### Markdown Attachment Content + +#### Inspecting Slack Blocks -If some of your attachment fields contain Markdown, you may use the `markdown` method to instruct Slack to parse and display the given attachment fields as Markdown formatted text. The values accepted by this method are: `pretext`, `text`, and / or `fields`. For more information about Slack attachment formatting, check out the [Slack API documentation](https://api.slack.com/docs/message-formatting#message_formatting): +If you would like to quickly inspect the blocks you've been building, you can invoke the `dd` method on the `SlackMessage` instance. The `dd` method will generate and dump a URL to Slack's [Block Kit Builder](https://app.slack.com/block-kit-builder/), which displays a preview of the payload and notification in your browser. You may pass `true` to the `dd` method to dump the raw payload: - use Illuminate\Notifications\Messages\SlackAttachment; - use Illuminate\Notifications\Messages\SlackMessage; + return (new SlackMessage) + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->dd(); - /** - * Get the Slack representation of the notification. - */ - public function toSlack(object $notifiable): SlackMessage + +### Routing Slack Notifications + +To direct Slack notifications to the appropriate Slack team and channel, define a `routeNotificationForSlack` method on your notifiable model. This method can return one of three values: + +- `null` - which defers routing to the channel configured in the notification itself. You may use the `to` method when building your `SlackMessage` to configure the channel within the notification. +- A string specifying the Slack channel to send the notification to, e.g. `#support-channel`. +- A `SlackRoute` instance, which allows you to specify an OAuth token and channel name, e.g. `SlackRoute::make($this->slack_channel, $this->slack_token)`. This method should be used to send notifications to external workspaces. + +For instance, returning `#support-channel` from the `routeNotificationForSlack` method will send the notification to the `#support-channel` channel in the workspace associated with the Bot User OAuth token located in your application's `services.php` configuration file: + + error() - ->content('Whoops! Something went wrong.') - ->attachment(function (SlackAttachment $attachment) use ($url) { - $attachment->title('Exception: File Not Found', $url) - ->content('File [background.jpg] was *not found*.') - ->markdown(['text']); - }); + /** + * Route notifications for the Slack channel. + */ + public function routeNotificationForSlack(Notification $notification): mixed + { + return '#support-channel'; + } } - -### Routing Slack Notifications + +### Notifying External Slack Workspaces + +> **Note** +> Before sending notifications to external Slack workspaces, your Slack App must be [distributed](#slack-app-distribution). + +Of course, you will often want to send notifications to the Slack workspaces owned by your application's users. To do so, you will first need to obtain a Slack OAuth token for the user. Thankfully, [Laravel Socialite](/docs/{{version}}/socialite) includes a Slack driver that will allow you to easily authenticate your application's users with Slack and [obtain a bot token](/docs/{{version}}/socialite#slack-bot-scopes). -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: +Once you have obtained the bot token and stored it within your application's database, you may utilize the `SlackRoute::make` method to route a notification to the user's workspace. In addition, your application will likely need to offer an opportunity for the user to specify which channel notifications should be sent to: slack_channel, $this->slack_token); } } diff --git a/socialite.md b/socialite.md index 88143ae55d9..65985ca18f7 100644 --- a/socialite.md +++ b/socialite.md @@ -8,13 +8,14 @@ - [Routing](#routing) - [Authentication & Storage](#authentication-and-storage) - [Access Scopes](#access-scopes) + - [Slack Bot Scopes](#slack-bot-scopes) - [Optional Parameters](#optional-parameters) - [Retrieving User Details](#retrieving-user-details) ## 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 via 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 via Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, Bitbucket, and Slack. > **Note** > Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. @@ -38,7 +39,7 @@ When upgrading to a new major version of Socialite, it's important that you care Before using Socialite, you will need to add credentials for the OAuth providers your application utilizes. Typically, these credentials may be retrieved by creating a "developer application" within the dashboard of the service you will be authenticating with. -These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin`, `google`, `github`, `gitlab`, or `bitbucket`, depending on the providers your application requires: +These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin`, `google`, `github`, `gitlab`, `bitbucket`, or `slack`, depending on the providers your application requires: 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), @@ -117,6 +118,33 @@ You can overwrite all existing scopes on the authentication request using the `s ->setScopes(['read:user', 'public_repo']) ->redirect(); + +### Slack Bot Scopes + +Slack's API provides [different types of access tokens](https://api.slack.com/authentication/token-types), each with their own set of [permission scopes](https://api.slack.com/scopes). Socialite is compatible with both of the following Slack access tokens types: + +
    + +- Bot (prefixed with `xoxb-`) +- User (prefixed with `xoxp-`) + +
    + +By default, the `slack` driver will generate a `user` token and invoking the the driver's `user` method will return the user's details. + +Bot tokens are primarily useful if your application will be sending notifications to external Slack workspaces that are owned by your application's users. To generate a bot token, invoke the `asBotUser` method before redirecting the user to Slack for authentication: + + return Socialite::driver('slack') + ->asBotUser() + ->setScopes(['chat:write', 'chat:write.public', 'chat:write.customize']) + ->redirect(); + +In addition, you must invoke the `asBotUser` method before invoking the `user` method after Slack redirects the user back to your application after authentication: + + $user = Socialite::driver('slack')->asBotUser()->user(); + +When generating a bot token, the `user` method will still return a `Laravel\Socialite\Two\User` instance; however, only the `token` property will be hydrated. This token may be stored in order to [send notifications to the authenticated user's Slack workspaces](/docs/{{version}}/notifications#notifying-external-slack-workspaces). + ### Optional Parameters From 15e022c0b7b0eb866d03d78a55784736abd01757 Mon Sep 17 00:00:00 2001 From: David Neal Date: Sat, 15 Jul 2023 16:17:51 +0100 Subject: [PATCH 1014/2609] Fixes actionsBlock typo in notifications.md (#8915) --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index cd716726ccf..8cc52ce5934 100644 --- a/notifications.md +++ b/notifications.md @@ -1116,7 +1116,7 @@ If a notification supports being sent as a Slack message, you should define a `t Slack's Block Kit notification system provides powerful features to [handle user interaction](https://api.slack.com/interactivity/handling). To utilize these features, your Slack App should have "Interactivity" enabled and a "Request URL" configured that points to a URL served by your application. These settings can be managed from the "Interactivity & Shortcuts" App management tab within Slack. -In the following example, which utilizes the `actiosnBlock` method, Slack will send a `POST` request to your "Request URL" with a payload containing the Slack user who clicked the button, the ID of the clicked button, and more. Your application can then determine the action to take based on the payload. You should also [verify the request](https://api.slack.com/authentication/verifying-requests-from-slack) was made by Slack: +In the following example, which utilizes the `actionsBlock` method, Slack will send a `POST` request to your "Request URL" with a payload containing the Slack user who clicked the button, the ID of the clicked button, and more. Your application can then determine the action to take based on the payload. You should also [verify the request](https://api.slack.com/authentication/verifying-requests-from-slack) was made by Slack: use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock; use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; From 08df9930020b4c511c2bb6c14aa4c5cfb1c59fc1 Mon Sep 17 00:00:00 2001 From: Ashley Evans Date: Thu, 20 Jul 2023 16:13:33 +0100 Subject: [PATCH 1015/2609] Fix type hint (#8916) --- eloquent-factories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-factories.md b/eloquent-factories.md index 76b4ceed77f..2869a5d4204 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -97,7 +97,7 @@ Then, define a `model` property on the corresponding factory: /** * The name of the factory's corresponding model. * - * @var string + * @var class-string<\Illuminate\Database\Eloquent\Model> */ protected $model = Flight::class; } From 36870165ebdbd9401313a503d5b1ba0b2d6694d2 Mon Sep 17 00:00:00 2001 From: s4muel Date: Mon, 24 Jul 2023 16:09:28 +0200 Subject: [PATCH 1016/2609] update the permissions comment in the public visibility of sftp driver (#8922) update the comment according to the defaults from https://github.com/thephpleague/flysystem/blob/3.x/src/UnixVisibility/PortableVisibilityConverter.php#L13 --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index 4d3ef07caff..c90ee222e1b 100644 --- a/filesystem.md +++ b/filesystem.md @@ -136,7 +136,7 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu 'passphrase' => env('SFTP_PASSPHRASE'), // Settings for file / directory permissions... - 'visibility' => 'private', // `private` = 0600, `public` = 0700 + 'visibility' => 'private', // `private` = 0600, `public` = 0644 'directory_visibility' => 'private', // `private` = 0700, `public` = 0755 // Optional SFTP Settings... From f1cb66924d500b39499b2aee3b1f716453b34810 Mon Sep 17 00:00:00 2001 From: danial rahimy <48244647+danialRahimy@users.noreply.github.com> Date: Mon, 24 Jul 2023 17:41:32 +0330 Subject: [PATCH 1017/2609] change livewire url to livewire.laravel.com (#8920) Co-authored-by: Danial Rahimi --- authentication.md | 4 ++-- blade.md | 2 +- frontend.md | 6 +++--- installation.md | 2 +- starter-kits.md | 4 ++-- vite.md | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/authentication.md b/authentication.md index daa052737ef..381a04b40ea 100644 --- a/authentication.md +++ b/authentication.md @@ -80,7 +80,7 @@ _Laravel Breeze_ is a simple, minimal implementation of all of Laravel's authent _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 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](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 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://livewire.laravel.com), and / or [Inertia](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 @@ -125,7 +125,7 @@ First, you should [install a Laravel application starter kit](/docs/{{version}}/ 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](https://tailwindcss.com). Breeze also offers an [Inertia](https://inertiajs.com) based scaffolding option using Vue or React. -[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 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. +[Laravel Jetstream](https://jetstream.laravel.com) is a more robust application starter kit that includes support for scaffolding your application with [Livewire](https://livewire.laravel.com) or [Inertia 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 diff --git a/blade.md b/blade.md index 9e8186c23f3..ccee85afe02 100644 --- a/blade.md +++ b/blade.md @@ -59,7 +59,7 @@ Blade views may be returned from routes or controllers using the global `view` h ### Supercharging Blade With Livewire -Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://laravel-livewire.com). Livewire allows you to write Blade components that are augmented with dynamic functionality that would typically only be possible via frontend frameworks like React or Vue, providing a great approach to building modern, reactive frontends without the complexities, client-side rendering, or build steps of many JavaScript frameworks. +Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://livewire.laravel.com). Livewire allows you to write Blade components that are augmented with dynamic functionality that would typically only be possible via frontend frameworks like React or Vue, providing a great approach to building modern, reactive frontends without the complexities, client-side rendering, or build steps of many JavaScript frameworks. ## Displaying Data diff --git a/frontend.md b/frontend.md index cce5b1a053f..3340996d080 100644 --- a/frontend.md +++ b/frontend.md @@ -52,12 +52,12 @@ However, as user expectations regarding web applications have matured, many deve Others, preferring to stick with the backend language they are comfortable with, have developed solutions that allow the construction of modern web application UIs while still primarily utilizing their backend language of choice. For example, in the [Rails](https://rubyonrails.org/) ecosystem, this has spurred the creation of libraries such as [Turbo](https://turbo.hotwired.dev/) [Hotwire](https://hotwired.dev/), and [Stimulus](https://stimulus.hotwired.dev/). -Within the Laravel ecosystem, the need to create modern, dynamic frontends by primarily using PHP has led to the creation of [Laravel Livewire](https://laravel-livewire.com) and [Alpine.js](https://alpinejs.dev/). +Within the Laravel ecosystem, the need to create modern, dynamic frontends by primarily using PHP has led to the creation of [Laravel Livewire](https://livewire.laravel.com) and [Alpine.js](https://alpinejs.dev/). ### Livewire -[Laravel Livewire](https://laravel-livewire.com) is a framework for building Laravel powered frontends that feel dynamic, modern, and alive just like frontends built with modern JavaScript frameworks like Vue and React. +[Laravel Livewire](https://livewire.laravel.com) is a framework for building Laravel powered frontends that feel dynamic, modern, and alive just like frontends built with modern JavaScript frameworks like Vue and React. When using Livewire, you will create Livewire "components" that render a discrete portion of your UI and expose methods and data that can be invoked and interacted with from your application's frontend. For example, a simple "Counter" component might look like the following: @@ -97,7 +97,7 @@ As you can see, Livewire enables you to write new HTML attributes such as `wire: For many, Livewire has revolutionized frontend development with Laravel, allowing them to stay within the comfort of Laravel while constructing modern, dynamic web applications. Typically, developers using Livewire will also utilize [Alpine.js](https://alpinejs.dev/) to "sprinkle" JavaScript onto their frontend only where it is needed, such as in order to render a dialog window. -If you're new to Laravel, we recommend getting familiar with the basic usage of [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Then, consult the official [Laravel Livewire documentation](https://laravel-livewire.com/docs) to learn how to take your application to the next level with interactive Livewire components. +If you're new to Laravel, we recommend getting familiar with the basic usage of [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Then, consult the official [Laravel Livewire documentation](https://livewire.laravel.com/docs) to learn how to take your application to the next level with interactive Livewire components. ### Starter Kits diff --git a/installation.md b/installation.md index 565b6cfce5a..e850ef1508c 100644 --- a/installation.md +++ b/installation.md @@ -284,7 +284,7 @@ How you want to use Laravel will also dictate the next steps on your journey. Th Laravel may serve as a full stack framework. By "full stack" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or a single-page application hybrid technology like [Inertia](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel. -If this is how you plan to use Laravel, you may want to check out our documentation on [frontend development](/docs/{{version}}/frontend), [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://laravel-livewire.com) and [Inertia](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications. +If this is how you plan to use Laravel, you may want to check out our documentation on [frontend development](/docs/{{version}}/frontend), [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://livewire.laravel.com) and [Inertia](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications. If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Vite](/docs/{{version}}/vite). diff --git a/starter-kits.md b/starter-kits.md index 3e35aee0366..6aac64c23d9 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -22,7 +22,7 @@ While you are welcome to use these starter kits, they are not required. You are Laravel Breeze's default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Or, Breeze can scaffold your application using Vue or React and [Inertia](https://inertiajs.com). -Breeze provides a wonderful starting point for beginning a fresh Laravel application and is also a great choice for projects that plan to take their Blade templates to the next level with [Laravel Livewire](https://laravel-livewire.com). +Breeze provides a wonderful starting point for beginning a fresh Laravel application and is also a great choice for projects that plan to take their Blade templates to the next level with [Laravel Livewire](https://livewire.laravel.com). @@ -134,6 +134,6 @@ Finally, you are ready to pair this backend with the frontend of your choice. A 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](https://inertiajs.com) driven frontend scaffolding. +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://livewire.laravel.com) or [Inertia](https://inertiajs.com) driven frontend scaffolding. Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/3.x/introduction.html). diff --git a/vite.md b/vite.md index a58ea2feddc..334a130cae8 100644 --- a/vite.md +++ b/vite.md @@ -44,7 +44,7 @@ Laravel integrates seamlessly with Vite by providing an official plugin and Blad Before transitioning to Vite, new Laravel applications utilized [Mix](https://laravel-mix.com/), which is powered by [webpack](https://webpack.js.org/), when bundling assets. Vite focuses on providing a faster and more productive experience when building rich JavaScript applications. If you are developing a Single Page Application (SPA), including those developed with tools like [Inertia](https://inertiajs.com), Vite will be the perfect fit. -Vite also works well with traditional server-side rendered applications with JavaScript "sprinkles", including those using [Livewire](https://laravel-livewire.com). However, it lacks some features that Laravel Mix supports, such as the ability to copy arbitrary assets into the build that are not referenced directly in your JavaScript application. +Vite also works well with traditional server-side rendered applications with JavaScript "sprinkles", including those using [Livewire](https://livewire.laravel.com). However, it lacks some features that Laravel Mix supports, such as the ability to copy arbitrary assets into the build that are not referenced directly in your JavaScript application. #### Migrating Back To Mix From 8c85dd483699f246158f068732809304a5a935ce Mon Sep 17 00:00:00 2001 From: Thai Nguyen Hung Date: Mon, 24 Jul 2023 21:19:36 +0700 Subject: [PATCH 1018/2609] [10.x] `Str::wrap` usage (#8919) * feat: `Str::wrap` usage * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 59f931fbe1b..aae3cfeb53b 100644 --- a/helpers.md +++ b/helpers.md @@ -172,6 +172,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::uuid](#method-str-uuid) [Str::wordCount](#method-str-word-count) [Str::words](#method-str-words) +[Str::wrap](#method-str-wrap) [str](#method-str) [trans](#method-trans) [trans_choice](#method-trans-choice) @@ -2271,6 +2272,21 @@ The `Str::words` method limits the number of words in a string. An additional st // Perfectly balanced, as >>> + +#### `Str::wrap()` {.collection-method} + +The `Str::wrap` method wraps the given string with an additional string or pair of strings: + + use Illuminate\Support\Str; + + Str::wrap('Laravel', '"'); + + // "Laravel" + + Str::wrap('is', before: 'This ', after: ' Laravel!'); + + // This is Laravel! + #### `str()` {.collection-method} From c021c80946f97c4ed7c998e08865c4cf3178093d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 24 Jul 2023 14:02:26 -0500 Subject: [PATCH 1019/2609] mention herd --- installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/installation.md b/installation.md index e850ef1508c..86c55029c56 100644 --- a/installation.md +++ b/installation.md @@ -52,7 +52,7 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ## Your First Laravel Project -Before creating your first Laravel project, you should ensure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS, PHP and Composer can be installed via [Homebrew](https://brew.sh/). In addition, we recommend [installing Node and NPM](https://nodejs.org). +Before creating your first Laravel project, you should ensure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS, PHP and Composer can be installed within minutes via [Laravel Herd](https://herd.laravel.com). In addition, we recommend [installing Node and NPM](https://nodejs.org). After you have installed PHP and Composer, you may create a new Laravel project via the Composer `create-project` command: @@ -60,7 +60,7 @@ After you have installed PHP and Composer, you may create a new Laravel project composer create-project laravel/laravel example-app ``` -Or, you may create new Laravel projects by globally installing the Laravel installer via Composer: +Or, you may create new Laravel projects by globally installing the Laravel installer via Composer. Or, if you installed PHP and Composer via [Laravel Herd](https://herd.laravel.com), the Laravel installer is already available to you: ```nothing composer global require laravel/installer From 6a4949daa0d2e17b0a338983f2bbc0d50b168c87 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 24 Jul 2023 14:06:30 -0500 Subject: [PATCH 1020/2609] wip --- valet.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/valet.md b/valet.md index 0f93059437f..22187feb230 100644 --- a/valet.md +++ b/valet.md @@ -22,6 +22,9 @@ ## Introduction +> **Note** +> Looking for an even easier way to develop Laravel applications on macOS? Check out [Laravel Herd](https://herd.laravel.com). Herd includes everything you need to get started with Laravel development, including Valet, PHP, and Composer. + [Laravel Valet](https://github.com/laravel/valet) is a 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. 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. From 6a8a7647e133018a64bdff8a4ce5ffb6fad0bb81 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 25 Jul 2023 16:13:48 +0200 Subject: [PATCH 1021/2609] Bump supported database versions (#8923) --- database.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database.md b/database.md index a8e26f1261b..4cc6ae248b4 100644 --- a/database.md +++ b/database.md @@ -19,9 +19,9 @@ Almost every modern web application interacts with a database. Laravel makes int
    -- MariaDB 10.3+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) +- MariaDB 10.10+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) - MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) -- PostgreSQL 10.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) +- PostgreSQL 11.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) - SQLite 3.8.8+ - SQL Server 2017+ ([Version Policy](https://docs.microsoft.com/en-us/lifecycle/products/?products=sql-server)) From 7baed56998c629dd68a6b541edf20abe85cdcbe0 Mon Sep 17 00:00:00 2001 From: Propaganistas Date: Sun, 30 Jul 2023 05:17:13 +0200 Subject: [PATCH 1022/2609] Remove faulty parameter (#8927) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 334a130cae8..838cac8266f 100644 --- a/vite.md +++ b/vite.md @@ -683,7 +683,7 @@ class AddContentSecurityPolicyHeaders * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle(Request $request, Closure $next, string $role): Response + public function handle(Request $request, Closure $next): Response { Vite::useCspNonce(); From f844f7299678793a13ec7aba6e70ba74d32e2d6a Mon Sep 17 00:00:00 2001 From: Ronald Intriago <36111224+JRIP24@users.noreply.github.com> Date: Tue, 1 Aug 2023 00:18:37 +0200 Subject: [PATCH 1023/2609] [10.x] Laravel Dusk interacting with elements inside an iframe (#8928) * Laravel Dusk interacting with elements inside an iframe * Update dusk.md * Update dusk.md --------- Co-authored-by: Taylor Otwell --- dusk.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dusk.md b/dusk.md index c378ced06b6..b630a5c8bf7 100644 --- a/dusk.md +++ b/dusk.md @@ -30,6 +30,7 @@ - [Using The Keyboard](#using-the-keyboard) - [Using The Mouse](#using-the-mouse) - [JavaScript Dialogs](#javascript-dialogs) + - [Interacting With Inline Frames](#interacting-with-iframes) - [Scoping Selectors](#scoping-selectors) - [Waiting For Elements](#waiting-for-elements) - [Scrolling An Element Into View](#scrolling-an-element-into-view) @@ -757,6 +758,18 @@ To close an open JavaScript dialog by clicking the "Cancel" button, you may invo $browser->dismissDialog(); + +### Interacting With Inline Frames + +If you need to interact with elements within an iframe, you may use the `withinFrame` method. All element interactions that take place within the closure provided to the `withinFrame` method will be scoped to the context of the specified iframe: + + $browser->withinFrame('#credit-card-details', function ($browser) { + $browser->type('input[name="cardnumber"]', '4242424242424242') + ->type('input[name="exp-date"]', '12/24') + ->type('input[name="cvc"]', '123'); + })->press('Pay'); + }); + ### Scoping Selectors From 3723ee141bb9f7ff2208ccc4102fedb79941c90b Mon Sep 17 00:00:00 2001 From: Dwight Watson Date: Wed, 2 Aug 2023 00:02:10 +1000 Subject: [PATCH 1024/2609] Indicate how states and factory callbacks can be used together (#8929) * Indicate how states and factory callbacks can be used together * Update eloquent-factories.md * Update eloquent-factories.md --------- Co-authored-by: Taylor Otwell --- eloquent-factories.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/eloquent-factories.md b/eloquent-factories.md index 2869a5d4204..f2f715fe34d 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -140,7 +140,6 @@ Factory callbacks are registered using the `afterMaking` and `afterCreating` met use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Str; class UserFactory extends Factory { @@ -159,6 +158,27 @@ Factory callbacks are registered using the `afterMaking` and `afterCreating` met // ... } +You may also register factory callbacks within state methods to perform additional tasks that are specific to a given state: + + use App\Models\User; + use Illuminate\Database\Eloquent\Factories\Factory; + + /** + * Indicate that the user is suspended. + */ + public function suspended(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'account_status' => 'suspended', + ]; + })->afterMaking(function (User $user) { + // ... + })->afterCreating(function (User $user) { + // ... + }); + } + ## Creating Models Using Factories From e1d331a45e7c6a347cf03b2b4a21e0d70eb85c6d Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 2 Aug 2023 00:10:47 +1000 Subject: [PATCH 1025/2609] [10.x] Document Laravel Prompts & prompting for missing input. (#8924) * Document Laravel Prompts * Document prompting for missing arguments * Formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- artisan.md | 91 ++++++++ documentation.md | 1 + prompts.md | 575 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 667 insertions(+) create mode 100644 prompts.md diff --git a/artisan.md b/artisan.md index 02f43fb62c1..c35394bac76 100644 --- a/artisan.md +++ b/artisan.md @@ -12,6 +12,7 @@ - [Options](#options) - [Input Arrays](#input-arrays) - [Input Descriptions](#input-descriptions) + - [Prompting For Missing Input](#prompting-for-missing-input) - [Command I/O](#command-io) - [Retrieving Input](#retrieving-input) - [Prompting For Input](#prompting-for-input) @@ -371,6 +372,93 @@ You may assign descriptions to input arguments and options by separating the arg {user : The ID of the user} {--queue : Whether the job should be queued}'; + +### Prompting For Missing Input + +If your command contains required arguments, the user will receive an error message when they are not provided. Alternatively, you may configure your command to automatically prompt the user when required arguments are missing by implementing the `PromptsForMissingInput` interface: + + 'Which user ID should receive the mail?', + ]; + } + +You may also provide placeholder text by using a tuple containing the question and placeholder: + + return [ + 'user' => ['Which user ID should receive the mail?', 'E.g. 123'], + ]; + +If you would like complete control over the prompt, you may provide a closure that should prompt the user and return their answer: + + use App\Models\User; + use function Laravel\Prompts\search; + + // ... + + return [ + 'user' => fn () => search( + label: 'Search for a user:', + placeholder: 'E.g. Taylor Otwell', + options: fn ($value) => strlen($value) > 0 + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + : [] + ), + ]; + +> **Note** +The comprehensive [Laravel Prompts](/docs/{{version}}/prompts) documentation includes additional information on the available prompts and their usage. + +If you wish to prompt the user to select or enter [options](#options), you may include prompts in your command's `handle` method. However, if you only wish to prompt the user when they have also been automatically prompted for missing arguments, then you may implement the `afterPromptingForMissingArguments` method: + + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + use function Laravel\Prompts\confirm; + + // ... + + /** + * Perform actions after the user was prompted for missing arguments. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return void + */ + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) + { + $input->setOption('queue', confirm( + label: 'Would you like to queue the mail?', + default: $this->option('queue') + )); + } + ## Command I/O @@ -402,6 +490,9 @@ Options may be retrieved just as easily as arguments using the `option` method. ### Prompting For Input +> **Note** +> [Laravel Prompts](/docs/{{version}}/prompts) is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. + In addition to displaying output, you may also ask the user to provide input during the execution of your command. The `ask` method will prompt the user with the given question, accept their input, and then return the user's input back to your command: /** diff --git a/documentation.md b/documentation.md index 4570cae1183..bbd8db7ce3c 100644 --- a/documentation.md +++ b/documentation.md @@ -92,6 +92,7 @@ - [Pennant](/docs/{{version}}/pennant) - [Pint](/docs/{{version}}/pint) - [Precognition](/docs/{{version}}/precognition) + - [Prompts](/docs/{{version}}/prompts) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) - [Scout](/docs/{{version}}/scout) diff --git a/prompts.md b/prompts.md new file mode 100644 index 00000000000..581ca09e33d --- /dev/null +++ b/prompts.md @@ -0,0 +1,575 @@ +# Prompts + +- [Introduction](#introduction) +- [Installation](#installation) +- [Available Prompts](#available-prompts) + - [Text](#text) + - [Password](#password) + - [Confirm](#confirm) + - [Select](#select) + - [Multi-select](#multiselect) + - [Suggest](#suggest) + - [Search](#search) +- [Terminal Considerations](#terminal-considerations) +- [Unsupported Environments & Fallbacks](#fallbacks) + + +## Introduction + +Laravel Prompts is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. + +Laravel Prompts is perfect for accepting user input in your [Artisan console commands](/docs/{{version}}/artisan#writing-commands), but it may also be used in any command-line PHP project. + +> **Note** +> Laravel Prompts supports macOS, Linux, and Windows with WSL. For more information, please see our documentation on [unsupported environments & fallbacks](#fallbacks). + + +## Installation + +Laravel Prompts is already included with the latest release of Laravel. + +Laravel Prompts may also be installed in your other PHP projects by using the Composer package manager: + +```shell +composer require laravel/prompts +``` + + +## Available Prompts + + +### Text + +The `text` function will prompt the user with the given question, accept their input, and then return it: + +```php +use function Laravel\Prompts\text; + +$name = text('What is your name?'); +``` + +You may also include placeholder text and a default value: + +```php +$name = text( + label: 'What is your name?', + placeholder: 'E.g. Taylor Otwell', + default: $user?->name +); +``` + + +#### Required Values + +If you require a value to be entered, you may pass the `required` argument: + +```php +$name = text( + label: 'What is your name?', + required: true +); +``` + +If you would like to customize the validation message, you may also pass a string: + +```php +$name = text( + label: 'What is your name?', + required: 'Your name is required.' +); +``` + + +#### Additional Validation + +Finally, if you would like to perform additional validation logic, you may pass a closure to the `validate` argument: + +```php +$name = text( + label: 'What is your name?', + validate: fn (string $value) => match (true) { + strlen($value) < 3 => 'The name must be at least 3 characters.', + strlen($value) > 255 => 'The name must not exceed 255 characters.', + default => null + } +); +``` + +The closure will receive the value that has been entered and may return an error message, or `null` if the validation passes. + + +### Password + +The `password` function is similar to the `text` function, but the user's input will be masked as they type in the console. This is useful when asking for sensitive information such as passwords: + +```php +use function Laravel\Prompts\text; + +$password = password('What is your password?'); +``` + +You may also include placeholder text: + +```php +$password = password( + label: 'What is your password?', + placeholder: 'Minimum 8 characters...' +); +``` + + +#### Required Values + +If you require a value to be entered, you may pass the `required` argument: + +```php +$name = password( + label: 'What is your password?', + required: true +); +``` + +If you would like to customize the validation message, you may also pass a string: + +```php +$name = password( + label: 'What is your password?', + required: 'The password is required.' +); +``` + + +#### Additional Validation + +Finally, if you would like to perform additional validation logic, you may pass a closure to the `validate` argument: + +```php +$name = password( + label: 'What is your password?', + validate: fn (string $value) => match (true) { + strlen($value) < 8 => 'The password must be at least 8 characters.', + default => null + } +); +``` + +The closure will receive the value that has been entered and may return an error message, or `null` if the validation passes. + + +### Confirm + +If you need to ask the user for a "yes or no" confirmation, you may use the `confirm` function. Users may use the arrow keys or press `y` or `n` to select their response. This function will return either `true` or `false`. + +```php +use function Laravel\Prompts\confirm; + +$confirmed = confirm('Do you accept the terms?'); +``` + +By default, the "Yes" answer will be pre-selected. However, you may configure the default to be "No" by passing `false` to the `default` argument: + +```php +$confirmed = confirm( + label: 'Do you accept the terms?' + default: false, +); +``` + +You may also customize the wording used for the "Yes" and "No" labels by passing the `yes` and `no` arguments: + +```php +$confirmed = confirm( + label: 'Do you accept the terms?', + yes: 'I accept', + no: 'I decline' +); +``` + + +#### Requiring "Yes" + +If necessary, you may require your users to select "Yes" by passing the `required` argument: + +```php +$confirmed = confirm( + label: 'Do you accept the terms?', + required: true +); +``` + +If you would like to customize the validation message, you may also pass a string: + +```php +$confirmed = confirm( + label: 'Do you accept the terms?', + required: 'You must accept the terms to continue.' +); +``` + + +### Select + +If you need the user to select from a predefined set of choices, you may use the `select` function: + +```php +use function Laravel\Prompts\select; + +$role = select( + 'What role should the user have?', + ['Member', 'Contributor', 'Owner'], +); +``` + +You may also specify the default choice by passing the `default` argument: + +```php +$role = select( + label: 'What role should the user have?', + options: ['Member', 'Contributor', 'Owner'], + default: 'Owner' +); +``` + +You may also pass an associative array to the `options` argument to have the selected key returned instead of its value: + +```php +$role = select( + label: 'What role should the user have?', + options: [ + 'member' => 'Member', + 'contributor' => 'Contributor', + 'owner' => 'Owner' + ], + default: 'owner' +); +``` + +Up to five options will be displayed before the list begins to scroll. You may customize this by passing the `scroll` argument: + +```php +$role = select( + label: 'Which category would you like to assign?', + options: Category::pluck('name', 'id'), + scroll: 10 +); +``` + + +#### Validation + +Unlike other prompt functions, the `select` function doesn't accept the `required` argument because it is not possible to select nothing. However, you may pass a closure to the `validate` argument if you need to present an option but prevent it from being selected: + +```php +$role = select( + label: 'What role should the user have?' + options: [ + 'member' => 'Member', + 'contributor' => 'Contributor', + 'owner' => 'Owner' + ], + validate: fn (string $value) => + $value === 'owner' && User::where('role', 'owner')->exists() + ? 'An owner already exists.' + : null + } +) +``` + +If the `options` argument is an associative array, then the closure will receive the selected key, otherwise it will receive the selected value. The closure may return an error message, or `null` if the validation passes. + + +### Multi-select + +If you need to the user to be able to select multiple options, you may use the `multiselect` function: + +```php +use function Laravel\Prompts\multiselect; + +$permissions = multiselect( + 'What permissions should be assigned?' + ['Read', 'Create', 'Update', 'Delete'] +); +``` + +You may also specify options that should be pre-selected by passing an array to the `default` argument: + +```php +use function Laravel\Prompts\multiselect; + +$permissions = multiselect( + label: 'What permissions should be assigned?', + options: ['Read', 'Create', 'Update', 'Delete'], + default: ['Read', 'Create'] +); +``` + +You may also pass an associative array to the `options` argument to return the selected options' keys instead of their values: + +``` +$permissions = multiselect( + label: 'What permissions should be assigned?', + options: [ + 'read' => 'Read', + 'create' => 'Create', + 'update' => 'Update', + 'delete' => 'Delete' + ], + default: ['read', 'create'] +); +``` + +Up to five options will be displayed before the list begins to scroll. You may customize this by passing the `scroll` argument: + +```php +$role = multiselect( + label: 'What categories should be assigned?', + options: Category::pluck('name', 'id'), + scroll: 10 +); +``` + + +#### Requiring a Value + +By default, the user may select zero or more options. You may pass the `required` argument to enforce one or more options instead: + +```php +$role = multiselect( + label: 'What categories should be assigned?', + options: Category::pluck('name', 'id'), + required: true, +); +``` + + +#### Validation + +You may pass a closure to the `validate` argument if you need to present an option but prevent it from being selected: + +``` +$permissions = select( + label: 'What permissions should the user have?' + options: [ + 'read' => 'Read', + 'create' => 'Create', + 'update' => 'Update', + 'delete' => 'Delete' + ], + validate: fn (array $values) => ! in_array('read', $values) => + ? 'All users require the read permission.' + : null + } +); +``` + +If the `options` argument is an associative array then the closure will receive the selected keys, otherwise it will receive the selected values. The closure may return an error message, or `null` if the validation passes. + + +### Suggest + +The `suggest` function can be used to provide auto-completion for possible choices. The user can still provide any answer, regardless of the auto-completion hints: + +```php +use function Laravel\Prompts\suggest; + +$name = suggest('What is your name?', ['Taylor', 'Dayle']); +``` + +Alternatively, you may pass a closure as the second argument to the `suggest` function. 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: + +```php +$name = suggest( + 'What is your name?', + fn ($value) => collect(['Taylor', 'Dayle']) + ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true)) +) +``` + +You may also include placeholder text and a default value: + +```php +$name = suggest( + label: 'What is your name?', + options: ['Taylor', 'Dayle'], + placeholder: 'E.g. Taylor', + default: $user?->name +); +``` + + +#### Required Values + +If you require a value to be entered, you may pass the `required` argument: + +```php +$name = suggest( + label: 'What is your name?', + options: ['Taylor', 'Dayle'], + required: true +); +``` + +If you would like to customize the validation message, you may also pass a string: + +```php +$name = suggest( + label: 'What is your name?', + options: ['Taylor', 'Dayle'], + required: 'Your name is required.' +); +``` + + +#### Additional Validation + +Finally, if you would like to perform additional validation logic, you may pass a closure to the `validate` argument: + +```php +$name = suggest( + label: 'What is your name?', + options: ['Taylor', 'Dayle'], + validate: fn (string $value) => match (true) { + strlen($value) < 3 => 'The name must be at least 3 characters.', + strlen($value) > 255 => 'The name must not exceed 255 characters.', + default => null + } +); +``` + +The closure will receive the value that has been entered and may return an error message, or `null` if the validation passes. + + +### Search + +If you have a lot of options for the user to select from, the `search` function allows the user to type a search query to filter the results before using the arrow keys to select an option: + +```php +use function Laravel\Prompts\search; + +$id = search( + 'Search for the user that should receive the mail', + fn (string $value) => strlen($value) > 0 + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + : [] +); +``` + +The closure will receive the text that has been typed by the user so far and must return an array of options. If you return an associative array then the selected option's key will be returned, otherwise its value will be returned instead. + +You may also include placeholder text: + +```php +$id = search( + label: 'Search for the user that should receive the mail', + placeholder: 'E.g. Taylor Otwell', + options: fn (string $value) => strlen($value) > 0 + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + : [] +); +``` + +Up to five options will be displayed before the list begins to scroll. You may customize this by passing the `scroll` argument: + +```php +$id = search( + label: 'Search for the user that should receive the mail', + options: fn (string $value) => strlen($value) > 0 + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + : [], + scroll: 10 +); +``` + + +#### Validation + +If you would like to perform additional validation logic, you may pass a closure to the `validate` argument: + +```php +$id = search( + label: 'Search for the user that should receive the mail', + options: fn (string $value) => strlen($value) > 0 + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + : [] + validate: function (int|string $value) { + $user = User::findOrFail($value); + + if ($user->opted_out) { + return 'This user has opted-out of receiving mail.'; + } + } +); +``` + +If the `options` closure returns an associative array, then the closure will receive the selected key, otherwise, it will receive the selected value. The closure may return an error message, or `null` if the validation passes. + + +### Terminal Considerations + + +#### Terminal Width + +If the length of any label, option, or validation message exceeds the number of "columns" in the user's terminal, it will be automatically truncated to fit. Consider minimizing the length of these strings if your users may be using narrower terminals. A typically safe maximum length is 74 characters to support an 80-character terminal. + + +#### Terminal Height + +For any prompts that accept the `scroll` argument, the configured value will automatically be reduced to fit the height of the user's terminal, including space for a validation message. + + +### Unsupported Environments & Fallbacks + +Laravel Prompts supports macOS, Linux, and Windows with WSL. Due to limitations in the Windows version of PHP, it is not currently possible to use Laravel Prompts on Windows outside of WSL. + +For this reason, Laravel Prompts supports falling back to an alternative implementation such as the [Symfony Console Question Helper](https://symfony.com/doc/current/components/console/helpers/questionhelper.html). + +> **Note** +> When using Laravel Prompts with the Laravel framework, fallbacks for each prompt have been configured for you and will be automatically enabled in unsupported environments. + + +#### Fallback Conditions + +If you are not using Laravel or need to customize when the fallback behavior is used, you may pass a boolean to the `fallbackWhen` static method on the `Prompt` class: + +```php +use Laravel\Prompts\Prompt; + +Prompt::fallbackWhen( + ! $input->isInteractive() || windows_os() || app()->runningUnitTests() +); +``` + + +#### Fallback Behavior + +If you are not using Laravel or need to customize the fallback behavior, you may pass a closure to the `fallbackUsing` static method on each prompt class: + +```php +use Laravel\Prompts\TextPrompt; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +TextPrompt::fallbackUsing(function (TextPrompt $prompt) use ($input, $output) { + $question = (new Question($prompt->label, $prompt->default ?: null)) + ->setValidator(function ($answer) use ($prompt) { + if ($prompt->required && $answer === null) { + throw new \RuntimeException(is_string($prompt->required) ? $prompt->required : 'Required.'); + } + + if ($prompt->validate) { + $error = ($prompt->validate)($answer ?? ''); + + if ($error) { + throw new \RuntimeException($error); + } + } + + return $answer; + }); + + return (new SymfonyStyle($input, $output)) + ->askQuestion($question); +}); +``` + +Fallbacks must be configured individually for each prompt class. The closure will receive an instance of the prompt class and must return an appropriate type for the prompt. From acdbb69b878276ca9fcd508c39ea9e0e7f9fb872 Mon Sep 17 00:00:00 2001 From: Allan Mariucci Carvalho Date: Tue, 1 Aug 2023 11:44:47 -0300 Subject: [PATCH 1026/2609] fix code example (#8931) A comma is missing --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index 581ca09e33d..512b034e9e5 100644 --- a/prompts.md +++ b/prompts.md @@ -286,7 +286,7 @@ If you need to the user to be able to select multiple options, you may use the ` use function Laravel\Prompts\multiselect; $permissions = multiselect( - 'What permissions should be assigned?' + 'What permissions should be assigned?', ['Read', 'Create', 'Update', 'Delete'] ); ``` From f6c0faf8c5239eac519f6d5c9100e91be3368f74 Mon Sep 17 00:00:00 2001 From: Allan Mariucci Carvalho Date: Tue, 1 Aug 2023 12:45:54 -0300 Subject: [PATCH 1027/2609] Fix a wrong function return (#8932) * Fix a wrong function return Search function expect an array, but on example a collection was provided. * formatting --------- Co-authored-by: Taylor Otwell --- prompts.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prompts.md b/prompts.md index 512b034e9e5..1c1264f9256 100644 --- a/prompts.md +++ b/prompts.md @@ -449,7 +449,7 @@ use function Laravel\Prompts\search; $id = search( 'Search for the user that should receive the mail', fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() : [] ); ``` @@ -463,7 +463,7 @@ $id = search( label: 'Search for the user that should receive the mail', placeholder: 'E.g. Taylor Otwell', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() : [] ); ``` @@ -474,7 +474,7 @@ Up to five options will be displayed before the list begins to scroll. You may c $id = search( label: 'Search for the user that should receive the mail', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() : [], scroll: 10 ); @@ -489,7 +489,7 @@ If you would like to perform additional validation logic, you may pass a closure $id = search( label: 'Search for the user that should receive the mail', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id') + ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() : [] validate: function (int|string $value) { $user = User::findOrFail($value); From d361abf63aee3862fd08ce3d8512f22e43b62855 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 11:49:25 -0500 Subject: [PATCH 1028/2609] document url protocols --- validation.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/validation.md b/validation.md index 69a050c6e63..2e111e98f89 100644 --- a/validation.md +++ b/validation.md @@ -1809,6 +1809,14 @@ The field under validation must be uppercase. The field under validation must be a valid URL. +If you would like to specify the URL protocols that should be considered valid, you may pass the protocols as validation rule parameters: + +```php +'url' => 'url:http,https', + +'game' => 'url:minecraft,steam', +``` + #### ulid From a63e92165476445d27a601eaf4004a092e5922d7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 11:50:23 -0500 Subject: [PATCH 1029/2609] wip --- configuration.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configuration.md b/configuration.md index d94fcb9e2b4..22bc5d6c8a0 100644 --- a/configuration.md +++ b/configuration.md @@ -33,6 +33,12 @@ If you're only interested in a particular section of the application overview ou php artisan about --only=environment ``` +Or, to explore a specific configuration file's values, you may use the `config:show` Artisan command: + +```shell +php artisan config:show database +``` + ## Environment Configuration From b5d62cdb7c2298536abda28bbb029e92c247de8f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 11:51:12 -0500 Subject: [PATCH 1030/2609] wip --- configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index 22bc5d6c8a0..120dface9d5 100644 --- a/configuration.md +++ b/configuration.md @@ -33,7 +33,7 @@ If you're only interested in a particular section of the application overview ou php artisan about --only=environment ``` -Or, to explore a specific configuration file's values, you may use the `config:show` Artisan command: +Or, to explore a specific configuration file's values in detail, you may use the `config:show` Artisan command: ```shell php artisan config:show database From 5ae0ef69363c71c7b41e50e805ec747a8972dc8f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 11:54:47 -0500 Subject: [PATCH 1031/2609] wip --- collections.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/collections.md b/collections.md index 650aa313a0b..9d341fa702d 100644 --- a/collections.md +++ b/collections.md @@ -120,6 +120,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [duplicatesStrict](#method-duplicatesstrict) [each](#method-each) [eachSpread](#method-eachspread) +[ensure](#method-ensure) [every](#method-every) [except](#method-except) [filter](#method-filter) @@ -759,6 +760,17 @@ You may stop iterating through the items by returning `false` from the callback: return false; }); + +#### `ensure()` {.collection-method} + +The `ensure` method may be used to verify that all elements of a collection are of a given type. Otherwise, an `UnexpectedValueException` will be thrown: + + return $collection->ensure(User::class); + +Primitive types such as `string`, `int`, `float`, `bool`, and `array` may also be specified: + + return $collection->ensure('int'); + #### `every()` {.collection-method} From eab32e2cf6a8d8ba4c4a3c102cfe4dbc0476710f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 11:58:52 -0500 Subject: [PATCH 1032/2609] wip --- collections.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/collections.md b/collections.md index 9d341fa702d..05d4f699b56 100644 --- a/collections.md +++ b/collections.md @@ -771,6 +771,9 @@ Primitive types such as `string`, `int`, `float`, `bool`, and `array` may also b return $collection->ensure('int'); +> **Warning** +> The `ensure` method does not guarantee that elements of different types will not be added to the collection at a later time. + #### `every()` {.collection-method} From 56748a894ebba01bb6029df266775a0cfa02af37 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 12:02:56 -0500 Subject: [PATCH 1033/2609] wip --- validation.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/validation.md b/validation.md index 2e111e98f89..5ca0a54967c 100644 --- a/validation.md +++ b/validation.md @@ -2051,6 +2051,17 @@ If your application accepts images uploaded by your users, you may use the `File > **Note** > More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). + +#### File Sizes + +For convenience, minimum and maximum file sizes may be specified as a string with a suffix indicating the file size units. The `kb`, `mb`, `gb`, and `tb` suffixes are supported: + +```php +File::image() + ->min('1kb') + ->max('10mb') +``` + #### File Types From ec9ea34b39f2aca9cd3d6be97a8607e188d38c78 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 12:27:30 -0500 Subject: [PATCH 1034/2609] wip --- artisan.md | 15 +++++++++++++++ queues.md | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index c35394bac76..88ebf3507a8 100644 --- a/artisan.md +++ b/artisan.md @@ -232,6 +232,21 @@ If you would like to specify the exit status code that the command should return php artisan mail:send 1 --isolated=12 ``` + +#### Lock ID + +By default, Laravel will use the command's name to generate the string key that is used to acquire the atomic lock in your application's cache. However, you may customize this key by defining an `isolatableId` method on your Artisan command class, allowing you to integrate the command's arguments or options into the key: + +```php +/** + * Get the isolatable ID for the command. + */ +public function isolatableId(): string +{ + return $this->argument('user'); +} +``` + #### Lock Expiration Time diff --git a/queues.md b/queues.md index 0d462ce84e6..d5691be9af6 100644 --- a/queues.md +++ b/queues.md @@ -286,7 +286,7 @@ In certain cases, you may want to define a specific "key" that makes the job uni public $uniqueFor = 3600; /** - * The unique ID of the job. + * Get the unique ID for the job. */ public function uniqueId(): string { From 08837f791435af68b0e92aee06e4e8481ebcd7df Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Aug 2023 12:29:36 -0500 Subject: [PATCH 1035/2609] wip --- scheduling.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scheduling.md b/scheduling.md index b8d77f4d4f7..f7bb09d9979 100644 --- a/scheduling.md +++ b/scheduling.md @@ -124,11 +124,11 @@ Method | Description `->everyThirtyMinutes();` | Run the task every thirty minutes `->hourly();` | Run the task every hour `->hourlyAt(17);` | Run the task every hour at 17 minutes past the hour -`->everyOddHour();` | Run the task every odd hour -`->everyTwoHours();` | Run the task every two hours -`->everyThreeHours();` | Run the task every three hours -`->everyFourHours();` | Run the task every four hours -`->everySixHours();` | Run the task every six hours +`->everyOddHour($minutes = 0);` | Run the task every odd hour +`->everyTwoHours($minutes = 0);` | Run the task every two hours +`->everyThreeHours($minutes = 0);` | Run the task every three hours +`->everyFourHours($minutes = 0);` | Run the task every four hours +`->everySixHours($minutes = 0);` | Run the task every six hours `->daily();` | Run the task every day at midnight `->dailyAt('13:00');` | Run the task every day at 13:00 `->twiceDaily(1, 13);` | Run the task daily at 1:00 & 13:00 From 3fb7334c2042e7eafa966443978e9a4dc9995100 Mon Sep 17 00:00:00 2001 From: Benjamin Crozat Date: Tue, 1 Aug 2023 19:30:16 +0200 Subject: [PATCH 1036/2609] Fixed incorrect code in one of the examples of multiselect() (#8933) * Fixed incorrect code * Fixed another mistake --- prompts.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/prompts.md b/prompts.md index 1c1264f9256..d7ede6768f8 100644 --- a/prompts.md +++ b/prompts.md @@ -355,10 +355,9 @@ $permissions = select( 'update' => 'Update', 'delete' => 'Delete' ], - validate: fn (array $values) => ! in_array('read', $values) => + validate: fn (array $values) => ! in_array('read', $values) ? 'All users require the read permission.' : null - } ); ``` From 0b077a9e55c4759767875a0e95ad99db13f8a55e Mon Sep 17 00:00:00 2001 From: Padam Shankhadev Date: Wed, 2 Aug 2023 19:39:58 +0545 Subject: [PATCH 1037/2609] Fix typo (#8935) --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index d7ede6768f8..dd3ce905141 100644 --- a/prompts.md +++ b/prompts.md @@ -103,7 +103,7 @@ The closure will receive the value that has been entered and may return an error The `password` function is similar to the `text` function, but the user's input will be masked as they type in the console. This is useful when asking for sensitive information such as passwords: ```php -use function Laravel\Prompts\text; +use function Laravel\Prompts\password; $password = password('What is your password?'); ``` From ae32c83d652e45cf36b24a12e661163005c18223 Mon Sep 17 00:00:00 2001 From: Julius Kiekbusch Date: Wed, 2 Aug 2023 15:55:34 +0200 Subject: [PATCH 1038/2609] Update sail.md (#8934) --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 3486d96fad1..59968832057 100644 --- a/sail.md +++ b/sail.md @@ -182,7 +182,7 @@ docker run --rm \ composer install --ignore-platform-reqs ``` -When using the `laravelsail/phpXX-composer` image, you should use the same version of PHP that you plan to use for your application (`74`, `80`, `81`, or `82`). +When using the `laravelsail/phpXX-composer` image, you should use the same version of PHP that you plan to use for your application (`80`, `81`, or `82`). ### Executing Artisan Commands From 5d27e1c4d99f351fe399cd9d04d4ab8d1e8c2bb6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 2 Aug 2023 14:12:10 -0500 Subject: [PATCH 1039/2609] breeze prompts --- starter-kits.md | 59 +++++++++++++++---------------------------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index 6aac64c23d9..9da2ae46f65 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -39,34 +39,33 @@ First, you should [create a new Laravel application](/docs/{{version}}/installat composer require laravel/breeze --dev ``` -Once Breeze is installed, you may scaffold your application using one of the Breeze "stacks" discussed in the documentation below. - - -### Breeze & Blade - 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. -The default Breeze "stack" is the Blade stack, which utilizes simple [Blade templates](/docs/{{version}}/blade) to render your application's frontend. The Blade stack may be installed by invoking the `breeze:install` command with no other additional arguments. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: +The `breeze:install` command will prompt you for your preferred frontend stack and testing framework: ```shell -php artisan breeze:install blade +php artisan breeze:install php artisan migrate 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. - - -#### Dark Mode + +### Breeze & Blade -If you would like Breeze to include "dark mode" support when scaffolding your application's frontend, simply provide the `--dark` directive when executing the `breeze:install` command: +The default Breeze "stack" is the Blade stack, which utilizes simple [Blade templates](/docs/{{version}}/blade) to render your application's frontend. The Blade stack may be installed by invoking the `breeze:install` command with no other additional arguments and selecting the Blade frontend stack. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: ```shell -php artisan breeze:install --dark +php artisan breeze:install + +php artisan migrate +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. + > **Note** > To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). @@ -75,14 +74,12 @@ php artisan breeze:install --dark Laravel Breeze also offers React and Vue scaffolding via an [Inertia](https://inertiajs.com) frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers. -Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel and lightning-fast [Vite](https://vitejs.dev) compilation. To use an Inertia stack, specify `vue` or `react` as your desired stack when executing the `breeze:install` Artisan command. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: +Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel and lightning-fast [Vite](https://vitejs.dev) compilation. To use an Inertia stack, you may select the Vue or React frontend stacks when executing the `breeze:install` Artisan command. -```shell -php artisan breeze:install vue - -# Or... +When selecting the Vue or React frontend stack, the Breeze installer will also prompt you to determine if you would like [Inertia SSR](https://inertiajs.com/server-side-rendering) or TypeScript support. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: -php artisan breeze:install react +```shell +php artisan breeze:install php artisan migrate npm install @@ -91,33 +88,13 @@ 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. - -#### Server-Side Rendering - -If you would like Breeze to scaffold support for [Inertia SSR](https://inertiajs.com/server-side-rendering), you may provide the `ssr` option when invoking the `breeze:install` command: - -```shell -php artisan breeze:install vue --ssr -php artisan breeze:install react --ssr -``` - - -#### TypeScript - -When using the Vue or React stacks, you may provide the `--typescript` option to generate scaffolding that includes TypeScript support: - -```shell -php artisan breeze:install vue --typescript -php artisan breeze:install react --typescript -``` - ### Breeze & Next.js / API -Laravel Breeze can also scaffold an authentication API that is ready to authenticate modern JavaScript applications such as those powered by [Next](https://nextjs.org), [Nuxt](https://nuxtjs.org), and others. To get started, specify the `api` stack as your desired stack when executing the `breeze:install` Artisan command: +Laravel Breeze can also scaffold an authentication API that is ready to authenticate modern JavaScript applications such as those powered by [Next](https://nextjs.org), [Nuxt](https://nuxtjs.org), and others. To get started, select the API stack as your desired stack when executing the `breeze:install` Artisan command: ```shell -php artisan breeze:install api +php artisan breeze:install php artisan migrate ``` From 6d112dec10d9b052facb6a6a275651402516f85f Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Thu, 3 Aug 2023 16:50:02 -0400 Subject: [PATCH 1040/2609] Add missing exception import (#8939) --- http-client.md | 1 + 1 file changed, 1 insertion(+) diff --git a/http-client.md b/http-client.md index 63963b4cc54..a52eb8e4d19 100644 --- a/http-client.md +++ b/http-client.md @@ -244,6 +244,7 @@ If a request attempt fails, you may wish to make a change to the request before use Exception; use Illuminate\Http\Client\PendingRequest; + use Illuminate\Http\Client\RequestException; $response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) { if (! $exception instanceof RequestException || $exception->response->status() !== 401) { From 4b1cba1d31279ac2e475570191fb6317b3613270 Mon Sep 17 00:00:00 2001 From: uldiszarins <34100020+uldiszarins@users.noreply.github.com> Date: Fri, 4 Aug 2023 17:26:43 +0300 Subject: [PATCH 1041/2609] Update prompts.md (#8941) Missing coma --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index dd3ce905141..9b7849fe048 100644 --- a/prompts.md +++ b/prompts.md @@ -170,7 +170,7 @@ By default, the "Yes" answer will be pre-selected. However, you may configure th ```php $confirmed = confirm( - label: 'Do you accept the terms?' + label: 'Do you accept the terms?', default: false, ); ``` From b17827756e397c0ebc8cfe746074f63ac26cc199 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 4 Aug 2023 11:41:04 -0500 Subject: [PATCH 1042/2609] wip --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index 381a04b40ea..7c7717f3373 100644 --- a/authentication.md +++ b/authentication.md @@ -592,7 +592,7 @@ To get started, call the `Auth::viaRequest` method within the `boot` method of y public function boot(): void { Auth::viaRequest('custom-token', function (Request $request) { - return User::where('token', $request->token)->first(); + return User::where('token', (string) $request->token)->first(); }); } From 62d685ca8ae5cf535946628ff3297ede418e678b Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Mon, 7 Aug 2023 23:35:37 +1000 Subject: [PATCH 1043/2609] [10.x] Document axios customisation (#8942) * Document axios customisation * formatting --------- Co-authored-by: Taylor Otwell --- precognition.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/precognition.md b/precognition.md index ef000c96b31..b350b694706 100644 --- a/precognition.md +++ b/precognition.md @@ -7,6 +7,7 @@ - [Using React](#using-react) - [Using React & Inertia](#using-react-and-inertia) - [Using Alpine & Blade](#using-alpine) + - [Configuring Axios](#configuring-axios) - [Customizing Validation Rules](#customizing-validation-rules) - [Handling File Uploads](#handling-file-uploads) - [Managing Side-Effects](#managing-side-effects) @@ -516,6 +517,32 @@ Alternatively, if you would like to submit the form via XHR you may use the form > ``` + +### Configuring Axios + +The Precognition validation libraries use the [Axios](https://github.com/axios/axios) HTTP client to send requests to your application's backend. For convenience, the Axios instance may be customized if required by your application. For example, when using the `laravel-precognition-vue` library, you may add additional request headers to each outgoing request in your application's `resources/js/app.js` file: + +```js +import { client } from 'laravel-precognition-vue'; + +client.axios().defaults.headers.common['Authorization'] = authToken; +``` + +Or, if you already have a configured Axios instance for your application, you may tell Precognition to use that instance instead: + +```js +import Axios from 'axios'; +import { client } from 'laravel-precognition-vue'; + +window.axios = Axios.create() +window.axios.defaults.headers.common['Authorization'] = authToken; + +client.use(window.axios) +``` + +> **Warning** +> The Inertia flavored Precognition libraries will only use the configured Axios instance for validation requests. Form submissions will always be sent by Inertia. + ## Customizing Validation Rules From 8ab7eaa04febf7c289b09b0bddf100ba06b3f999 Mon Sep 17 00:00:00 2001 From: Stackrats <67356460+stackrats@users.noreply.github.com> Date: Tue, 8 Aug 2023 20:58:57 +0700 Subject: [PATCH 1044/2609] Update mail.md (#8946) "Sendinblue" have re-branded under the name "Brevo", updating to reflect the new name and package name change. --- mail.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mail.md b/mail.md index 4bfe2664945..eb29c4868e3 100644 --- a/mail.md +++ b/mail.md @@ -1224,22 +1224,22 @@ Once your custom transport has been defined and registered, you may create a mai ### Additional Symfony Transports -Laravel includes support for some existing Symfony maintained mail transports like Mailgun and Postmark. However, you may wish to extend Laravel with support for additional Symfony maintained transports. You can do so by requiring the necessary Symfony mailer via Composer and registering the transport with Laravel. For example, you may install and register the "Sendinblue" Symfony mailer: +Laravel includes support for some existing Symfony maintained mail transports like Mailgun and Postmark. However, you may wish to extend Laravel with support for additional Symfony maintained transports. You can do so by requiring the necessary Symfony mailer via Composer and registering the transport with Laravel. For example, you may install and register the "Brevo" (formerly "Sendinblue") Symfony mailer: ```none -composer require symfony/sendinblue-mailer symfony/http-client +composer require symfony/brevo-mailer symfony/http-client ``` -Once the Sendinblue mailer package has been installed, you may add an entry for your Sendinblue API credentials to your application's `services` configuration file: +Once the Brevo mailer package has been installed, you may add an entry for your Brevo API credentials to your application's `services` configuration file: - 'sendinblue' => [ + 'brevo' => [ 'key' => 'your-api-key', ], Next, you may use the `Mail` facade's `extend` method to register the transport with Laravel. Typically, this should be done within the `boot` method of a service provider: use Illuminate\Support\Facades\Mail; - use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; + use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory; use Symfony\Component\Mailer\Transport\Dsn; /** @@ -1247,12 +1247,12 @@ Next, you may use the `Mail` facade's `extend` method to register the transport */ public function boot(): void { - Mail::extend('sendinblue', function () { - return (new SendinblueTransportFactory)->create( + Mail::extend('brevo', function () { + return (new BrevoTransportFactory)->create( new Dsn( - 'sendinblue+api', + 'brevo+api', 'default', - config('services.sendinblue.key') + config('services.brevo.key') ) ); }); @@ -1260,7 +1260,7 @@ Next, you may use the `Mail` facade's `extend` method to register the transport Once your transport has been registered, you may create a mailer definition within your application's config/mail.php configuration file that utilizes the new transport: - 'sendinblue' => [ - 'transport' => 'sendinblue', + 'brevo' => [ + 'transport' => 'brevo', // ... ], From 91a416b79107b1c369d26ba53064594bc10028c9 Mon Sep 17 00:00:00 2001 From: Mike Healy Date: Wed, 9 Aug 2023 00:01:07 +1000 Subject: [PATCH 1045/2609] Remove link to archived Meilisearch Scout package (#8945) Scout now natively supports Meilisearch, and the linked Github repo has been archived. --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 247bdd97422..8a8f46718ff 100644 --- a/sail.md +++ b/sail.md @@ -231,7 +231,7 @@ To connect to your application's Redis database from your local machine, you may ### MeiliSearch -If you chose to install the [MeiliSearch](https://www.meilisearch.com) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this powerful search-engine that is [compatible](https://github.com/meilisearch/meilisearch-laravel-scout) with [Laravel Scout](/docs/{{version}}/scout). Once you have started your containers, you may connect to the MeiliSearch instance within your application by setting your `MEILISEARCH_HOST` environment variable to `http://meilisearch:7700`. +If you chose to install the [MeiliSearch](https://www.meilisearch.com) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this powerful search-engine that is compatible with [Laravel Scout](/docs/{{version}}/scout). Once you have started your containers, you may connect to the MeiliSearch instance within your application by setting your `MEILISEARCH_HOST` environment variable to `http://meilisearch:7700`. From your local machine, you may access MeiliSearch's web based administration panel by navigating to `http://localhost:7700` in your web browser. From a9f1f52118fde25e97bf26d72350363163c87e0a Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 9 Aug 2023 23:22:37 +1000 Subject: [PATCH 1046/2609] Add Herd docs (#8925) --- vite.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vite.md b/vite.md index 838cac8266f..46948046ea4 100644 --- a/vite.md +++ b/vite.md @@ -134,7 +134,7 @@ The Laravel plugin also supports multiple entry points and advanced configuratio If your local development web server is serving your application via HTTPS, you may run into issues connecting to the Vite development server. -If you are using [Laravel Valet](/docs/{{version}}/valet) for local development and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may configure the Vite development server to automatically use Valet's generated TLS certificates: +If you are using [Laravel Herd](https://herd.laravel.com) and have secured the site or you are using [Laravel Valet](/docs/{{version}}/valet) and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may configure the Vite development server to automatically use the generated TLS certificates: ```js import { defineConfig } from 'vite'; @@ -144,7 +144,7 @@ export default defineConfig({ plugins: [ laravel({ // ... - valetTls: 'my-app.test', // [tl! add] + detectTls: 'my-app.test', // [tl! add] }), ], }); From 2c0101dfed820fd1f8482cbd6fc3ef44bb40e4dd Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 9 Aug 2023 23:25:14 +1000 Subject: [PATCH 1047/2609] Document Feature::useMorphMap() (#8867) * Document Feature::useMorphMap() * Fix link * formatting --------- Co-authored-by: Taylor Otwell --- pennant.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pennant.md b/pennant.md index 31b8b91f159..aeb07a1ddb0 100644 --- a/pennant.md +++ b/pennant.md @@ -16,6 +16,7 @@ - [Default Scope](#default-scope) - [Nullable Scope](#nullable-scope) - [Identifying Scope](#identifying-scope) + - [Serializing Scope](#serializing-scope) - [Rich Feature Values](#rich-feature-values) - [Retrieving Multiple Features](#retrieving-multiple-features) - [Eager Loading](#eager-loading) @@ -535,6 +536,25 @@ class User extends Model implements FeatureScopeable } ``` + +### Serializing Scope + +By default, Pennant will use a fully qualified class name when storing a feature associated with an Eloquent model. If you are already using an [Eloquent morph map](/docs/{{version}}/eloquent-relationships#custom-polymorphic-types), you may choose to have Pennant also use the morph map to decouple the stored feature from your application structure. + +To achieve this, after defining your Eloquent morph map in a service provider, you may invoke the `Feature` facade's `useMorphMap` method: + +```php +use Illuminate\Database\Eloquent\Relations\Relation; +use Laravel\Pennant\Feature; + +Relation::enforceMorphMap([ + 'post' => 'App\Models\Post', + 'video' => 'App\Models\Video', +]); + +Feature::useMorphMap(); +``` + ## Rich Feature Values From 36b88b2cd2949294191bada5b16a41e376456758 Mon Sep 17 00:00:00 2001 From: Istvan Palinkas Date: Wed, 9 Aug 2023 17:02:18 +0200 Subject: [PATCH 1048/2609] add Process Facade (#8948) Co-authored-by: Istvan Palinkas --- facades.md | 1 + 1 file changed, 1 insertion(+) diff --git a/facades.md b/facades.md index ef5510b34fa..4f67f50c688 100644 --- a/facades.md +++ b/facades.md @@ -275,6 +275,7 @@ Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/a Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` Pipeline (Instance) | [Illuminate\Pipeline\Pipeline](https://laravel.com/api/{{version}}/Illuminate/Pipeline/Pipeline.html) |   +Process | [Illuminate\Process\Factory](https://laravel.com/api/{{version}}/Illuminate/Process/Factory.html) |   Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) |   From 550117ac99d163d542fcd79368ad469a8c463cb7 Mon Sep 17 00:00:00 2001 From: Miran AL Mehrab Date: Thu, 10 Aug 2023 03:09:36 +0600 Subject: [PATCH 1049/2609] fix typo (#8949) --- http-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index 12af10584c0..41cca8cf445 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1315,7 +1315,7 @@ You may also assert that a given key has a particular validation error message. #### assertViewHas -Assert that the response view contains given a piece of data: +Assert that the response view contains a given piece of data: $response->assertViewHas($key, $value = null); From 3653f54ce56ac8643e5a66edea8e46a70a5e8f86 Mon Sep 17 00:00:00 2001 From: Majid Alaeinia <11965368+majidalaeinia@users.noreply.github.com> Date: Fri, 11 Aug 2023 17:50:14 +0330 Subject: [PATCH 1050/2609] [10.x] Consider 'Models' folder for the models path usages (#8952) * [10.x] Consider 'Models' folder for the models path usages * Update container.md --------- Co-authored-by: Taylor Otwell --- octane.md | 4 ++-- queues.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/octane.md b/octane.md index 2fce6135605..6d05e121ac0 100644 --- a/octane.md +++ b/octane.md @@ -473,8 +473,8 @@ While building your application, you should take special care to avoid creating When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's `concurrently` method. You may combine this method with PHP array destructuring to retrieve the results of each operation: ```php -use App\User; -use App\Server; +use App\Models\User; +use App\Models\Server; use Laravel\Octane\Facades\Octane; [$users, $servers] = Octane::concurrently([ diff --git a/queues.md b/queues.md index d5691be9af6..aa0dae6437d 100644 --- a/queues.md +++ b/queues.md @@ -265,7 +265,7 @@ In certain cases, you may want to define a specific "key" that makes the job uni Date: Mon, 14 Aug 2023 19:45:14 +0530 Subject: [PATCH 1051/2609] Update prompts.md (#8960) --- prompts.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prompts.md b/prompts.md index 9b7849fe048..b3693795b68 100644 --- a/prompts.md +++ b/prompts.md @@ -123,7 +123,7 @@ $password = password( If you require a value to be entered, you may pass the `required` argument: ```php -$name = password( +$password = password( label: 'What is your password?', required: true ); @@ -132,7 +132,7 @@ $name = password( If you would like to customize the validation message, you may also pass a string: ```php -$name = password( +$password = password( label: 'What is your password?', required: 'The password is required.' ); @@ -144,7 +144,7 @@ $name = password( Finally, if you would like to perform additional validation logic, you may pass a closure to the `validate` argument: ```php -$name = password( +$password = password( label: 'What is your password?', validate: fn (string $value) => match (true) { strlen($value) < 8 => 'The password must be at least 8 characters.', From 8723000d6e195ccfecc47714067e7453328e3bf8 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:49:44 +0330 Subject: [PATCH 1052/2609] Update prompts.md (#8956) --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index b3693795b68..81c144fd90e 100644 --- a/prompts.md +++ b/prompts.md @@ -16,7 +16,7 @@ ## Introduction -Laravel Prompts is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. +[Laravel Prompts](https://github.com/laravel/prompts) is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. Laravel Prompts is perfect for accepting user input in your [Artisan console commands](/docs/{{version}}/artisan#writing-commands), but it may also be used in any command-line PHP project. From a5ac7006f096852569621700d8bec1489afbf0ff Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Tue, 15 Aug 2023 17:00:05 +0330 Subject: [PATCH 1053/2609] missing semicolon (#8964) --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index 81c144fd90e..ce64010c5d8 100644 --- a/prompts.md +++ b/prompts.md @@ -272,7 +272,7 @@ $role = select( ? 'An owner already exists.' : null } -) +); ``` If the `options` argument is an associative array, then the closure will receive the selected key, otherwise it will receive the selected value. The closure may return an error message, or `null` if the validation passes. From 1c699203d2ce08300c8e93f045dc31b474379c9b Mon Sep 17 00:00:00 2001 From: Bram Date: Tue, 15 Aug 2023 15:01:25 +0100 Subject: [PATCH 1054/2609] Update validation.md (#8963) --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 5ca0a54967c..f619c0a274d 100644 --- a/validation.md +++ b/validation.md @@ -1338,12 +1338,12 @@ The field under validation must not be empty when it is present. #### gt:_field_ -The field under validation must be greater than the given _field_. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the [`size`](#rule-size) rule. +The field under validation must be greater than the given _field_ or _value_. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the [`size`](#rule-size) rule. #### gte:_field_ -The field under validation must be greater than or equal to the given _field_. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the [`size`](#rule-size) rule. +The field under validation must be greater than or equal to the given _field_ or _value_. The two fields must be of the same type. Strings, numerics, arrays, and files are evaluated using the same conventions as the [`size`](#rule-size) rule. #### image From f23ad4bdf51e47ef9142834903b46d49612edcf4 Mon Sep 17 00:00:00 2001 From: Shane Date: Tue, 15 Aug 2023 22:07:57 +0800 Subject: [PATCH 1055/2609] [10.x] Add more info for deeper nested indexes/positions (#8962) * [10.x] Add more info for deeper nested indexes/positions * Update validation.md * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/validation.md b/validation.md index f619c0a274d..c6a343c55b1 100644 --- a/validation.md +++ b/validation.md @@ -2015,6 +2015,10 @@ When validating arrays, you may want to reference the index or position of a par Given the example above, validation will fail and the user will be presented with the following error of _"Please describe photo #2."_ +If necessary, you may reference more deeply nested indexes and positions via `second-index`, `second-position`, `third-index`, `third-position`, etc. + + 'photos.*.attributes.*.string' => 'Invalid attribute for photo #:second-position.', + ## Validating Files From e6811c5e021cca28f87f9d18f7ccebde0b06d294 Mon Sep 17 00:00:00 2001 From: CalebW Date: Tue, 15 Aug 2023 09:14:24 -0500 Subject: [PATCH 1056/2609] [10.x] Add Listener `withDelay` docs (#8954) * Add Listener `withDelay` docs * formatting * add word * change word * fix wordign --------- Co-authored-by: Taylor Otwell --- events.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/events.md b/events.md index c98c8c0a438..d18f83e81ed 100644 --- a/events.md +++ b/events.md @@ -273,7 +273,7 @@ To specify that a listener should be queued, add the `ShouldQueue` interface to That's it! Now, when an event handled by this listener is dispatched, the listener will automatically be queued by the event dispatcher using Laravel's [queue system](/docs/{{version}}/queues). If no exceptions are thrown when the listener is executed by the queue, the queued job will automatically be deleted after it has finished processing. -#### Customizing The Queue Connection & Queue Name +#### Customizing The Queue Connection, Name, & Delay If you would like to customize the queue connection, queue name, or queue delay time of an event listener, you may define the `$connection`, `$queue`, or `$delay` properties on your listener class: @@ -308,7 +308,7 @@ If you would like to customize the queue connection, queue name, or queue delay public $delay = 60; } -If you would like to define the listener's queue connection or queue name at runtime, you may define `viaConnection` or `viaQueue` methods on the listener: +If you would like to define the listener's queue connection, queue name, or delay at runtime, you may define `viaConnection`, `viaQueue`, or `withDelay` methods on the listener: /** * Get the name of the listener's queue connection. @@ -326,6 +326,14 @@ If you would like to define the listener's queue connection or queue name at run return 'listeners'; } + /** + * Get the number of seconds before the job should be processed. + */ + public function withDelay(SendShipmentNotification $event): int + { + return $event->highPriority ? 0 : 60; + } + #### Conditionally Queueing Listeners From d948ad093728d21e022fb969e257f680617e30ff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Aug 2023 09:18:39 -0500 Subject: [PATCH 1057/2609] document percentage method --- collections.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/collections.md b/collections.md index 05d4f699b56..8120cdfe350 100644 --- a/collections.md +++ b/collections.md @@ -164,6 +164,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [only](#method-only) [pad](#method-pad) [partition](#method-partition) +[percentage](#method-percentage) [pipe](#method-pipe) [pipeInto](#method-pipeinto) [pipeThrough](#method-pipethrough) @@ -1685,6 +1686,27 @@ The `partition` method may be combined with PHP array destructuring to separate // [3, 4, 5, 6] + +#### `percentage()` {.collection-method} + +The `percentage` method may be used to quickly determine the percentage of items in the collection that pass a given truth test: + +```php +$collection = collect([1, 1, 2, 2, 2, 3]); + +$percentage = $collection->percentage(fn ($value) => $value === 1); + +// 33.33 +``` + +By default, the percentage will be rounded to two decimal places. However, you may customize this behavior by providing a second argument to the method: + +```php +$percentage = $collection->percentage(fn ($value) => $value === 1, precision: 3); + +// 33.333 +``` + #### `pipe()` {.collection-method} From 2defd36cae3801346819d0008a1cce8dceb65f22 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Aug 2023 09:26:19 -0500 Subject: [PATCH 1058/2609] formatting --- queues.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index aa0dae6437d..b7198dbc9cc 100644 --- a/queues.md +++ b/queues.md @@ -227,7 +227,9 @@ If you would like to take total control over how the container injects dependenc #### Queued 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: +Because all loaded Eloquent model relationships also get serialized when a job is queued, the serialized job string can sometimes become quite large. Furthermore, when a job is deserialized and model relationships are re-retrieved from the database, they will be retrieved in their entirety. Any previous relationship constraints that were applied before the model was serialized during the job queueing process will not be applied when the job is deserialized. Therefore, if you wish to work with a subset of a given relationship, you should re-constrain that relationship within your queued job. + +Or, 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. @@ -237,7 +239,18 @@ Because loaded relationships also get serialized, the serialized job string can $this->podcast = $podcast->withoutRelations(); } -Furthermore, when a job is deserialized and model relationships are re-retrieved from the database, they will be retrieved in their entirety. Any previous relationship constraints that were applied before the model was serialized during the job queueing process will not be applied when the job is deserialized. Therefore, if you wish to work with a subset of a given relationship, you should re-constrain that relationship within your queued job. +If you are using PHP constructor property promotion and would like to indicate that an Eloquent model should not have its relations serialized, you may use the `WithoutRelations` attribute: + + use Illuminate\Queue\Attributes\WithoutRelations; + + /** + * Create a new job instance. + */ + public function __construct( + #[WithoutRelations] + public Podcast $podcast + ) { + } If a job receives a collection or array of Eloquent models instead of a single model, the models within that collection will not have their relationships restored when the job is deserialized and executed. This is to prevent excessive resource usage on jobs that deal with large numbers of models. From 01c1313bf27c436714218ba3782523756bb52620 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Aug 2023 09:32:45 -0500 Subject: [PATCH 1059/2609] document hints --- prompts.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/prompts.md b/prompts.md index ce64010c5d8..62f666ed27d 100644 --- a/prompts.md +++ b/prompts.md @@ -10,6 +10,7 @@ - [Multi-select](#multiselect) - [Suggest](#suggest) - [Search](#search) +- [Hint Text](#hint-text) - [Terminal Considerations](#terminal-considerations) - [Unsupported Environments & Fallbacks](#fallbacks) @@ -502,6 +503,18 @@ $id = search( If the `options` closure returns an associative array, then the closure will receive the selected key, otherwise, it will receive the selected value. The closure may return an error message, or `null` if the validation passes. + +### Hint Text + +All prompt functions also support "hint text". This text will be displayed underneath the prompt to give the user information or instructions regarding the prompt: + +```php +$email = text( + label: 'What is your email address?', + hint: 'We will never share your email address with anyone.', +); +``` + ### Terminal Considerations From 6e7124c78d94f7e329234c7d6b3f13d6ef1bef5b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Aug 2023 09:38:06 -0500 Subject: [PATCH 1060/2609] document note functions --- prompts.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/prompts.md b/prompts.md index 62f666ed27d..6beb790a229 100644 --- a/prompts.md +++ b/prompts.md @@ -10,6 +10,7 @@ - [Multi-select](#multiselect) - [Suggest](#suggest) - [Search](#search) +- [Informational Messages](#informational-messages) - [Hint Text](#hint-text) - [Terminal Considerations](#terminal-considerations) - [Unsupported Environments & Fallbacks](#fallbacks) @@ -503,6 +504,17 @@ $id = search( If the `options` closure returns an associative array, then the closure will receive the selected key, otherwise, it will receive the selected value. The closure may return an error message, or `null` if the validation passes. + +### Informational Messages + +The `note`, `info`, `warning`, `error`, and `alert` functions may be used to display informational messages: + +```php +use function Laravel\Prompts\info; + +info('Package installed successfully.'); +``` + ### Hint Text From 8b76b003f580231b05fdbd83e75453628ffb0d80 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 15 Aug 2023 15:19:46 +0000 Subject: [PATCH 1061/2609] Adds prompts example image (#8965) --- prompts.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prompts.md b/prompts.md index 6beb790a229..06e03d08dba 100644 --- a/prompts.md +++ b/prompts.md @@ -20,6 +20,8 @@ [Laravel Prompts](https://github.com/laravel/prompts) is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. + + Laravel Prompts is perfect for accepting user input in your [Artisan console commands](/docs/{{version}}/artisan#writing-commands), but it may also be used in any command-line PHP project. > **Note** From 535e79c4e6681738769d93695c34dcd51d832d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Sat, 19 Aug 2023 22:54:20 +0700 Subject: [PATCH 1062/2609] Add `per` preset to pint docs (#8967) --- pint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint.md b/pint.md index 7111f7f0d6e..50d6719a97a 100644 --- a/pint.md +++ b/pint.md @@ -93,7 +93,7 @@ If you wish, you may also set the preset in your project's `pint.json` file: } ``` -Pint's currently supported presets are: `laravel`, `psr12`, and `symfony`. +Pint's currently supported presets are: `laravel`, `per`, `psr12`, and `symfony`. ### Rules From e361bc86158142161ceb4b6b2d23d6e641a500a0 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Mon, 21 Aug 2023 14:19:42 +0100 Subject: [PATCH 1063/2609] Update rescue helper examples (#8968) * Update rescue helper examples The docs don't provide an example of the third parameter available to the rescue helper function. * Update helpers.md * Update helpers.md * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/helpers.md b/helpers.md index aae3cfeb53b..39ad85a7646 100644 --- a/helpers.md +++ b/helpers.md @@ -4123,6 +4123,14 @@ You may also pass a second argument to the `rescue` function. This argument will return $this->failure(); }); +A `report` argument may be provided to the `rescue` function to determine if the exception should be reported via the `report` function: + + return rescue(function () { + return $this->method(); + }, report: function (Throwable $throwable) { + return $throwable instanceof InvalidArgumentException; + }); + #### `resolve()` {.collection-method} From 1de358c33d286c3492b361ef160cbed664a02f67 Mon Sep 17 00:00:00 2001 From: Gary Green <1702638+garygreen@users.noreply.github.com> Date: Mon, 21 Aug 2023 20:42:16 +0100 Subject: [PATCH 1064/2609] Move updateOrCreate from upsert to update section (#8969) * Move updateOrCreate from upsert to update section * formatting * formatting --------- Co-authored-by: Taylor Otwell --- eloquent.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/eloquent.md b/eloquent.md index abb7353d208..578bd5535fe 100644 --- a/eloquent.md +++ b/eloquent.md @@ -729,6 +729,15 @@ The `save` method may also be used to update models that already exist in the da $flight->save(); +Occasionally, you may need to update an existing model or create a new model if no matching model exists. Like the `firstOrCreate` method, the `updateOrCreate` method persists the model, so there's no need to manually call the `save` method. + +In the example below, if a flight exists with a `departure` location of `Oakland` and a `destination` location of `San Diego`, its `price` and `discounted` columns will be updated. If no such flight exists, a new flight will be created which has the attributes resulting from merging the first argument array with the second argument array: + + $flight = Flight::updateOrCreate( + ['departure' => 'Oakland', 'destination' => 'San Diego'], + ['price' => 99, 'discounted' => 1] + ); + #### Mass Updates @@ -893,21 +902,12 @@ If you wish, you may instruct Laravel to throw an exception when attempting to f ### Upserts -Occasionally, you may need to update an existing model or create a new model if no matching model exists. Like the `firstOrCreate` method, the `updateOrCreate` method persists the model, so there's no need to manually call the `save` method. - -In the example below, if a flight exists with a `departure` location of `Oakland` and a `destination` location of `San Diego`, its `price` and `discounted` columns will be updated. If no such flight exists, a new flight will be created which has the attributes resulting from merging the first argument array with the second argument array: - - $flight = Flight::updateOrCreate( - ['departure' => 'Oakland', 'destination' => 'San Diego'], - ['price' => 99, 'discounted' => 1] - ); - -If you would like to perform multiple "upserts" in a single query, then you should use the `upsert` method instead. 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 the columns that should be updated if a matching record already exists in the database. The `upsert` method will automatically set the `created_at` and `updated_at` timestamps if timestamps are enabled on the model: +Eloquent's `upsert` method may be used to update or create records in a single, atomic operation. 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 the columns that should be updated if a matching record already exists in the database. The `upsert` method will automatically set the `created_at` and `updated_at` timestamps if timestamps are enabled on the model: Flight::upsert([ ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] - ], ['departure', 'destination'], ['price']); + ], uniqueBy: ['departure', 'destination'], update: ['price']); > **Warning** > All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. From f327fc63d695f4ad6e4775b83632e47ac2e04073 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 22 Aug 2023 08:46:14 -0500 Subject: [PATCH 1065/2609] wip --- helpers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/helpers.md b/helpers.md index 39ad85a7646..734e3a0b4bc 100644 --- a/helpers.md +++ b/helpers.md @@ -4366,6 +4366,10 @@ To invoke a callback more than once, you may specify the number of iterations th Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms +Sometimes, you may want to benchmark the execution of a callback while still obtaining the value returned by the callback. The `value` method will return a tuple containing the value returned by the callback and the amount of milliseconds it took to execute the callback: + + [$count, $duration] = Benchmark::value(fn () => User::count()); + ### Dates From e331fe13ed7ac9299decb23ced10231eed86fe7c Mon Sep 17 00:00:00 2001 From: Majid Alaeinia <11965368+majidalaeinia@users.noreply.github.com> Date: Wed, 23 Aug 2023 17:20:25 +0330 Subject: [PATCH 1066/2609] Add '&' to special meaning characters for URL (#8971) * [10.x] Consider 'Models' folder for the models path usages * Update container.md * Update configuration.md --------- Co-authored-by: Taylor Otwell --- configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index 120dface9d5..3d10310c427 100644 --- a/configuration.md +++ b/configuration.md @@ -264,7 +264,7 @@ https://example.com/1630542a-246b-4b66-afa1-dd72a4c43515 When accessing this hidden route, you will then be redirected to the `/` route of the application. Once the cookie has been issued to your browser, you will be able to browse the application normally as if it was not in maintenance mode. > **Note** -> Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?`. +> Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?` or `&`. #### Pre-Rendering The Maintenance Mode View From 23f5260ecc6a3c321d649f61456af283652e1e67 Mon Sep 17 00:00:00 2001 From: Joel Van Horn <129096+joelvh@users.noreply.github.com> Date: Wed, 23 Aug 2023 09:54:38 -0400 Subject: [PATCH 1067/2609] getBaseQuery -> toBase was reverted (#8970) Deprecated removal was brought back in https://github.com/laravel/framework/pull/41923 --- upgrade.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/upgrade.md b/upgrade.md index 6db4bfc83e7..2f4069a6e9b 100644 --- a/upgrade.md +++ b/upgrade.md @@ -36,7 +36,6 @@ - [Public Path Binding](#public-path-binding) - [Query Exception Constructor](#query-exception-constructor) - [Rate Limiter Return Values](#rate-limiter-return-values) -- [Relation `getBaseQuery` Method](#relation-getbasequery-method) - [The `Redirect::home` Method](#redirect-home) - [The `Bus::dispatchNow` Method](#dispatch-now) - [The `registerPolicies` Method](#register-policies) @@ -191,13 +190,6 @@ protected $casts = [ ]; ``` - -#### Relation `getBaseQuery` Method - -**Likelihood Of Impact: Very Low** - -The `getBaseQuery` method on the `Illuminate\Database\Eloquent\Relations\Relation` class has been renamed to `toBase`. - ### Localization From 07215d43d910c1b63a298c560058a557ae2844c2 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 23 Aug 2023 17:53:10 +0100 Subject: [PATCH 1068/2609] [10.x] Moves Folio documentation (#8972) * Moves Folio documentation * wip --------- Co-authored-by: Taylor Otwell --- documentation.md | 1 + folio.md | 329 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 330 insertions(+) create mode 100644 folio.md diff --git a/documentation.md b/documentation.md index bbd8db7ce3c..64930006f17 100644 --- a/documentation.md +++ b/documentation.md @@ -83,6 +83,7 @@ - [Dusk](/docs/{{version}}/dusk) - [Envoy](/docs/{{version}}/envoy) - [Fortify](/docs/{{version}}/fortify) + - [Folio](/docs/{{version}}/folio) - [Homestead](/docs/{{version}}/homestead) - [Horizon](/docs/{{version}}/horizon) - [Jetstream](https://jetstream.laravel.com) diff --git a/folio.md b/folio.md new file mode 100644 index 00000000000..62f9f4b8a45 --- /dev/null +++ b/folio.md @@ -0,0 +1,329 @@ +# Laravel Folio + +- [Introduction](#introduction) +- [Installation](#installation) + - [Page Paths / URIs](#page-paths-uris) + - [Subdomain Routing](#subdomain-routing) +- [Creating Routes](#creating-routes) + - [Nested Routes](#nested-routes) + - [Index Routes](#index-routes) +- [Route Parameters](#route-parameters) +- [Named Routes](#named-routes) +- [Route Model Binding](#route-model-binding) + - [Soft Deleted Models](#soft-deleted-models) +- [Middleware](#middleware) +- [PHP Blocks](#php-blocks) +- [Route Caching](#route-caching) + + +## Introduction + +[Laravel Folio](https://github.com/laravel/folio) is a powerful page based router designed to simplify routing in Laravel applications. With Laravel Folio, generating a route becomes as effortless as creating a Blade template within your application's `resources/views/pages` directory. + +For example, to create a page that is accessible at the `/greeting` URL, just create a `greeting.blade.php` file in your application's `resources/views/pages` directory: + +```php +
    + Hello World +
    +``` + + +## Installation + +To get started, install Folio into your project using the Composer package manager: + +```bash +composer require laravel/folio +``` + +After installing Folio, you may execute the `folio:install` Artisan command, which will install Folio's service provider into your application. This service provider registers the directory where Folio will search for routes / pages: + +```bash +php artisan folio:install +``` + + +### Page Paths / URIs + +By default, Folio serves pages from your application's `resources/views/pages` directory, but you may customize these directories in your Folio service provider's `boot` method. + +For example, sometimes it may be convenient to specify multiple Folio paths in the same Laravel application. You may wish to have a separate directory of Folio pages for your application's "admin" area, while using another directory for the rest of your application's pages. + +You may accomplish this using the `Folio::path` and `Folio::uri` methods. The `path` method registers a directory that Folio will scan for pages when routing incoming HTTP requests, while the `uri` method specifies the "base URI" for that directory of pages: + +```php +use Laravel\Folio\Folio; + +Folio::path(resource_path('views/pages/guest'))->uri('/'); + +Folio::path(resource_path('views/pages/admin')) + ->uri('/admin') + ->middleware([ + '*' => [ + 'auth', + 'verified', + + // ... + ], + ]); +``` + + +### Subdomain Routing + +You may also route to pages based on the incoming request's subdomain. For example, you may wish to route requests from `admin.example.com` to a different page directory than the rest of your Folio pages. You may accomplish this by invoking the `domain` method after invoking the `Folio::path` method: + +```php +use Laravel\Folio\Folio; + +Folio::domain('admin.example.com') + ->path(resource_path('views/pages/admin')); +``` + +The `domain` method also allows you to capture parts of the domain or subdomain as parameters. These parameters will be injected into your page template: + +```php +use Laravel\Folio\Folio; + +Folio::domain('{account}.example.com') + ->path(resource_path('views/pages/admin')); +``` + + +## Creating Routes + +You may create a Folio route by placing a Blade template in any of your Folio mounted directories. By default, Folio mounts the `resources/views/pages` directory, but you may customize these directories in your Folio service provider's `boot` method. + +Once a Blade template has been placed in a Folio mounted directory, you may immediately access it via your browser. For example, a page placed in `pages/schedule.blade.php` may be accessed in your browser at `http://example.com/schedule`. + +To quickly view a list of all of your Folio pages / routes, you may invoke the `folio:list` Artisan command: + +```bash +php artisan folio:list +``` + + +### Nested Routes + +You may create a nested route by creating one or more directories within one of Folio's directories. For instance, to create a page that is accessible via `/user/profile`, create a `profile.blade.php` template within the `pages/user` directory: + +```bash +php artisan make:folio user/profile + +# pages/user/profile.blade.php → /user/profile +``` + + +### Index Routes + +Sometimes, you may wish to make a given page the "index" of a directory. By placing an `index.blade.php` template within a Folio directory, any requests to the root of that directory will be routed to that page: + +```bash +php artisan make:folio index +# pages/index.blade.php → / + +php artisan make:folio users/index +# pages/users/index.blade.php → /users +``` + + +## Route Parameters + +Often, you will need to have segments of the incoming request's URL injected into your page so that you can interact with them. For example, you may need to access the "ID" of the user whose profile is being displayed. To accomplish this, you may encapsulate a segment of the page's filename in square brackets: + +```bash +php artisan make:folio "users/[id]" + +# pages/users/[id].blade.php → /users/1 +``` + +Captured segments can be accessed as variables within your Blade template: + +```html +
    + User {{ $id }} +
    +``` + +To capture multiple segments, you can prefix the encapsulated segment with three dots `...`: + +```bash +php artisan make:folio "users/[...ids]" + +# pages/users/[...ids].blade.php → /users/1/2/3 +``` + +When capturing multiple segments, the captured segments will be injected into the page as an array: + +```html +
      + @foreach ($ids as $id) +
    • User {{ $id }}
    • + @endforeach +
    +``` + + +## Named Routes + +You may specify a name for a given page's route using the `name` function: + +```php + + All Users + +``` + +If the page has parameters, you may simply pass their values to the `route` function: + +```php +route('users.show', ['user' => $user]); +``` + + +## Route Model Binding + +If a wildcard segment of your page template's filename corresponds one of your application's Eloquent models, Folio will automatically take advantage of Laravel's route model binding capabilities and attempt to inject the resolved model instance into your page: + +```bash +php artisan make:folio "users/[User]" + +# pages/users/[User].blade.php → /users/1 +``` + +Captured models can be accessed as variables within your Blade template. The model's variable name will be converted to "camel case": + +```html +
    + User {{ $user->id }} +
    +``` + +#### Customizing The Key + +Sometimes you may wish to resolve bound Eloquent models using a column other than `id`. To do so, you may specify the column in the page's filename. For example, a page with the filename `[Post:slug].blade.php` will attempt to resolve the bound model via the `slug` column instead of the `id` column. + +#### Model Location + +By default, Folio will search for your model within your application's `app/Models` directory. However, if needed, you may specify the fully-qualified model class name in your template's filename: + +```bash +php artisan make:folio "users/[.App.Models.User]" + +# pages/users/[.App.Models.User].blade.php → /users/1 +``` + + +### Soft Deleted Models + +By default, models that have been soft deleted are not retrieved when resolving implicit model bindings. However, if you wish, you can instruct Folio to retrieve soft deleted models by invoking the `withTrashed` function within the page's template: + +```php + + +
    + User {{ $user->id }} +
    +``` + + +## Middleware + +You can apply middleware to a specific page by invoking the `middleware` function within the page's template: + +```php + + +
    + Dashboard +
    +``` + +Or, to assign middleware to a group of pages, you may chain the `middleware` method after invoking the `Folio::path` method. + +To specify which pages the middleware should be applied to, the array of middleware may be keyed using the corresponding URL patterns of the pages they should be applied to. The `*` character may be utilized as a wildcard character: + +```php +use Laravel\Folio\Folio; + +Folio::path(resource_path('views/pages'))->middleware([ + 'admin/*' => [ + 'auth', + 'verified', + + // ... + ], +]); +``` + +You may include closures in the array of middleware to define inline, anonymous middleware: + +```php +use Closure; +use Illuminate\Http\Request; +use Laravel\Folio\Folio; + +Folio::path(resource_path('views/pages'))->middleware([ + 'admin/*' => [ + 'auth', + 'verified', + + function (Request $request, Closure $next) { + // ... + + return $next($request); + }, + ], +]); +``` + + +## PHP Blocks + +When using Folio, the `` tags are reserved for the Folio page definition functions such as `middleware` and `withTrashed`. + +Therefore, if you need to write PHP code that should be executed within your Blade template, you should use the `@php` Blade directive: + +```php +@php + if (! Auth::user()->can('view-posts', $user)) { + abort(403); + } + + $posts = $user->posts; +@endphp + +@foreach ($posts as $post) +
    + {{ $post->title }} +
    +@endforeach +``` + + +## Route Caching + +When using Folio, you should always take advantage of [Laravel's route caching capabilities](/docs/{{version}}/routing#route-caching). Folio listens for the `route:cache` Artisan command to ensure that Folio page definitions and route names are properly cached for maximum performance. From 195b712ad5c2ad5a2c96c878753af437ebe70704 Mon Sep 17 00:00:00 2001 From: Majid Alaeinia <11965368+majidalaeinia@users.noreply.github.com> Date: Thu, 24 Aug 2023 16:52:15 +0330 Subject: [PATCH 1069/2609] Update nuxt URL (#8976) * [10.x] Consider 'Models' folder for the models path usages * Update container.md * Update configuration.md * Update nuxt URL --------- Co-authored-by: Taylor Otwell --- frontend.md | 2 +- starter-kits.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend.md b/frontend.md index 3340996d080..0e5c0a41999 100644 --- a/frontend.md +++ b/frontend.md @@ -109,7 +109,7 @@ If you would like to build your frontend using PHP and Livewire, you can leverag Although it's possible to build modern frontends using Laravel and Livewire, many developers still prefer to leverage the power of a JavaScript framework like Vue or React. This allows developers to take advantage of the rich ecosystem of JavaScript packages and tools available via NPM. -However, without additional tooling, pairing Laravel with Vue or React would leave us needing to solve a variety of complicated problems such as client-side routing, data hydration, and authentication. Client-side routing is often simplified by using opinionated Vue / React frameworks such as [Nuxt](https://nuxtjs.org/) and [Next](https://nextjs.org/); however, data hydration and authentication remain complicated and cumbersome problems to solve when pairing a backend framework like Laravel with these frontend frameworks. +However, without additional tooling, pairing Laravel with Vue or React would leave us needing to solve a variety of complicated problems such as client-side routing, data hydration, and authentication. Client-side routing is often simplified by using opinionated Vue / React frameworks such as [Nuxt](https://nuxt.com/) and [Next](https://nextjs.org/); however, data hydration and authentication remain complicated and cumbersome problems to solve when pairing a backend framework like Laravel with these frontend frameworks. In addition, developers are left maintaining two separate code repositories, often needing to coordinate maintenance, releases, and deployments across both repositories. While these problems are not insurmountable, we don't believe it's a productive or enjoyable way to develop applications. diff --git a/starter-kits.md b/starter-kits.md index 9da2ae46f65..8d7af5b1749 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -91,7 +91,7 @@ Next, you may navigate to your application's `/login` or `/register` URLs in you ### Breeze & Next.js / API -Laravel Breeze can also scaffold an authentication API that is ready to authenticate modern JavaScript applications such as those powered by [Next](https://nextjs.org), [Nuxt](https://nuxtjs.org), and others. To get started, select the API stack as your desired stack when executing the `breeze:install` Artisan command: +Laravel Breeze can also scaffold an authentication API that is ready to authenticate modern JavaScript applications such as those powered by [Next](https://nextjs.org), [Nuxt](https://nuxt.com), and others. To get started, select the API stack as your desired stack when executing the `breeze:install` Artisan command: ```shell php artisan breeze:install From 1cafd1c8372d6b98d588fc8e6e68a153702fb08b Mon Sep 17 00:00:00 2001 From: Utsav Somaiya Date: Fri, 25 Aug 2023 18:46:23 +0530 Subject: [PATCH 1070/2609] improve: a torchlight add and remove (#8989) --- facades.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/facades.md b/facades.md index 4f67f50c688..febc13e1037 100644 --- a/facades.md +++ b/facades.md @@ -194,7 +194,8 @@ Injecting a publisher implementation into the method allows us to easily test th namespace App\Models; - use Facades\App\Contracts\Publisher; + use App\Contracts\Publisher; // [tl! remove] + use Facades\App\Contracts\Publisher; // // [tl! add] use Illuminate\Database\Eloquent\Model; class Podcast extends Model From 08566f5df782f9263c978f7d9032f47fe3ec915d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 25 Aug 2023 08:17:20 -0500 Subject: [PATCH 1071/2609] wip --- facades.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facades.md b/facades.md index febc13e1037..4e1d8bba739 100644 --- a/facades.md +++ b/facades.md @@ -195,7 +195,7 @@ Injecting a publisher implementation into the method allows us to easily test th namespace App\Models; use App\Contracts\Publisher; // [tl! remove] - use Facades\App\Contracts\Publisher; // // [tl! add] + use Facades\App\Contracts\Publisher; // [tl! add] use Illuminate\Database\Eloquent\Model; class Podcast extends Model From 72dfcf8b3aee3683931e17aaf75d2138505184e9 Mon Sep 17 00:00:00 2001 From: George Buckingham Date: Fri, 25 Aug 2023 14:33:54 +0100 Subject: [PATCH 1072/2609] Recommend scoped method for binding terminable middleware (#8980) Current recommendation is to use the `singleton` binding method - but this won't return the correct instance if running under Laravel Octane. --- middleware.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/middleware.md b/middleware.md index 13e6cff8a36..1ece73170b0 100644 --- a/middleware.md +++ b/middleware.md @@ -323,7 +323,7 @@ Sometimes a middleware may need to do some work after the HTTP response has been The `terminate` method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of routes or global middleware in the `app/Http/Kernel.php` file. -When calling the `terminate` method on your middleware, Laravel will resolve a fresh instance of the middleware from the [service container](/docs/{{version}}/container). If you would like to use the same middleware instance when the `handle` and `terminate` methods are called, register the middleware with the container using the container's `singleton` method. Typically this should be done in the `register` method of your `AppServiceProvider`: +When calling the `terminate` method on your middleware, Laravel will resolve a fresh instance of the middleware from the [service container](/docs/{{version}}/container). If you would like to use the same middleware instance when the `handle` and `terminate` methods are called, register the middleware with the container using the container's `scoped` method. Typically this should be done in the `register` method of your `AppServiceProvider`: use App\Http\Middleware\TerminatingMiddleware; @@ -332,5 +332,5 @@ When calling the `terminate` method on your middleware, Laravel will resolve a f */ public function register(): void { - $this->app->singleton(TerminatingMiddleware::class); + $this->app->scoped(TerminatingMiddleware::class); } From 9ecc4c49b6cf3680400afda7907bbcce8337ab33 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 25 Aug 2023 09:34:20 -0400 Subject: [PATCH 1073/2609] Revert "Recommend scoped method for binding terminable middleware (#8980)" (#8990) This reverts commit 72dfcf8b3aee3683931e17aaf75d2138505184e9. --- middleware.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/middleware.md b/middleware.md index 1ece73170b0..13e6cff8a36 100644 --- a/middleware.md +++ b/middleware.md @@ -323,7 +323,7 @@ Sometimes a middleware may need to do some work after the HTTP response has been The `terminate` method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of routes or global middleware in the `app/Http/Kernel.php` file. -When calling the `terminate` method on your middleware, Laravel will resolve a fresh instance of the middleware from the [service container](/docs/{{version}}/container). If you would like to use the same middleware instance when the `handle` and `terminate` methods are called, register the middleware with the container using the container's `scoped` method. Typically this should be done in the `register` method of your `AppServiceProvider`: +When calling the `terminate` method on your middleware, Laravel will resolve a fresh instance of the middleware from the [service container](/docs/{{version}}/container). If you would like to use the same middleware instance when the `handle` and `terminate` methods are called, register the middleware with the container using the container's `singleton` method. Typically this should be done in the `register` method of your `AppServiceProvider`: use App\Http\Middleware\TerminatingMiddleware; @@ -332,5 +332,5 @@ When calling the `terminate` method on your middleware, Laravel will resolve a f */ public function register(): void { - $this->app->scoped(TerminatingMiddleware::class); + $this->app->singleton(TerminatingMiddleware::class); } From 6161f44e9a4f15e68827be36182a9c4f77fdfd1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mauricio=20U=C3=B1ate=20Castro?= <91748598+rmunate@users.noreply.github.com> Date: Sun, 27 Aug 2023 09:44:39 -0500 Subject: [PATCH 1074/2609] [10.x] Description of use of the new method `Str::wordWrap` (#8991) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Descripcion de uso del nuevo método `Str::wordWrap` Descripción de uso del nuevo método `Str::wordWrap` * Adjust Comment * formatting --------- Co-authored-by: Taylor Otwell --- helpers.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/helpers.md b/helpers.md index 734e3a0b4bc..8cf09b66ac0 100644 --- a/helpers.md +++ b/helpers.md @@ -171,6 +171,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::ulid](#method-str-ulid) [Str::uuid](#method-str-uuid) [Str::wordCount](#method-str-word-count) +[Str::wordWrap](#method-str-word-wrap) [Str::words](#method-str-words) [Str::wrap](#method-str-wrap) [str](#method-str) @@ -2261,6 +2262,23 @@ use Illuminate\Support\Str; Str::wordCount('Hello, world!'); // 2 ``` + +#### `Str::wordWrap()` {.collection-method} + +The `Str::wordWrap` method wraps a string to a given number of characters: + + use Illuminate\Support\Str; + + $text = "The quick brown fox jumped over the lazy dog." + + Str::wordWrap($text, characters: 20, break: "
    \n"); + + /* + The quick brown fox
    + jumped over the lazy
    + dog. + */ + #### `Str::words()` {.collection-method} From f27b5f18e367cd9ad88dcc3b7f6a39cb755628a6 Mon Sep 17 00:00:00 2001 From: Marijn Bent Date: Mon, 28 Aug 2023 15:51:25 +0200 Subject: [PATCH 1075/2609] Add mailgun transport to example (#8992) --- mail.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mail.md b/mail.md index eb29c4868e3..e9aff18de05 100644 --- a/mail.md +++ b/mail.md @@ -61,6 +61,7 @@ composer require symfony/mailgun-mailer symfony/http-client Next, set the `default` option in your application's `config/mail.php` configuration file to `mailgun`. After configuring your application's default mailer, verify that your `config/services.php` configuration file contains the following options: 'mailgun' => [ + 'transport' => 'mailgun', 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), ], From 34bd2920cc455651fa49749990a28884da04f65a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Aug 2023 09:11:48 -0500 Subject: [PATCH 1076/2609] wip --- helpers.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/helpers.md b/helpers.md index 8cf09b66ac0..dd4b0869b11 100644 --- a/helpers.md +++ b/helpers.md @@ -151,6 +151,8 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::replaceArray](#method-str-replace-array) [Str::replaceFirst](#method-str-replace-first) [Str::replaceLast](#method-str-replace-last) +[Str::replaceStart](#method-str-replace-start) +[Str::replaceEnd](#method-str-replace-end) [Str::reverse](#method-str-reverse) [Str::singular](#method-str-singular) [Str::slug](#method-str-slug) @@ -2014,6 +2016,36 @@ The `Str::replaceLast` method replaces the last occurrence of a given value in a // the quick brown fox jumps over a lazy dog + +#### `Str::replaceStart()` {.collection-method} + +The `Str::replaceStart` replaces the first occurrence of the given value only if the value appears at the start of the string: + + use Illuminate\Support\Str; + + $replaced = Str::replaceStart('Hello World', 'Hello', 'Laravel'); + + // Laravel World + + $replaced = Str::replaceStart('Hello World', 'World', 'Laravel'); + + // Hello World + + +#### `Str::replaceEnd()` {.collection-method} + +The `Str::replaceEnd` replaces the first occurrence of the given value only if the value appears at the end of the string: + + use Illuminate\Support\Str; + + $replaced = Str::replaceEnd('Hello World', 'World', 'Laravel'); + + // Hello Laravel + + $replaced = Str::replaceEnd('Hello World', 'Hello', 'Laravel'); + + // Hello World + #### `Str::reverse()` {.collection-method} From 0a9e490038467907ebff7d591cb487b10b8246bb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Aug 2023 09:14:29 -0500 Subject: [PATCH 1077/2609] wip --- helpers.md | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index dd4b0869b11..e80221c07c6 100644 --- a/helpers.md +++ b/helpers.md @@ -241,6 +241,8 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [replaceFirst](#method-fluent-str-replace-first) [replaceLast](#method-fluent-str-replace-last) [replaceMatches](#method-fluent-str-replace-matches) +[replaceStart](#method-fluent-str-replace-start) +[replaceEnd](#method-fluent-str-replace-end) [rtrim](#method-fluent-str-rtrim) [scan](#method-fluent-str-scan) [singular](#method-fluent-str-singular) @@ -2019,7 +2021,7 @@ The `Str::replaceLast` method replaces the last occurrence of a given value in a #### `Str::replaceStart()` {.collection-method} -The `Str::replaceStart` replaces the first occurrence of the given value only if the value appears at the start of the string: +The `Str::replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: use Illuminate\Support\Str; @@ -2034,7 +2036,7 @@ The `Str::replaceStart` replaces the first occurrence of the given value only if #### `Str::replaceEnd()` {.collection-method} -The `Str::replaceEnd` replaces the first occurrence of the given value only if the value appears at the end of the string: +The `Str::replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: use Illuminate\Support\Str; @@ -3170,6 +3172,36 @@ The `replaceMatches` method also accepts a closure that will be invoked with eac // '[1][2][3]' + +#### `replaceStart` {.collection-method} + +The `replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: + + use Illuminate\Support\Str; + + $replaced = Str::of('Hello World')->replaceStart('Hello', 'Laravel'); + + // Laravel World + + $replaced = Str::of('Hello World')->replaceStart('World', 'Laravel'); + + // Hello World + + +#### `replaceEnd` {.collection-method} + +The `replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: + + use Illuminate\Support\Str; + + $replaced = Str::of('Hello World')->replaceEnd('World', 'Laravel'); + + // Hello Laravel + + $replaced = Str::of('Hello World')->replaceEnd('Hello', 'Laravel'); + + // Hello World + #### `rtrim` {.collection-method} From d67575cc97d079b179f7d4e758118eac050d6b61 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Tue, 29 Aug 2023 19:15:51 +0200 Subject: [PATCH 1078/2609] fix order (#8995) --- helpers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/helpers.md b/helpers.md index e80221c07c6..3e0e99b49a3 100644 --- a/helpers.md +++ b/helpers.md @@ -2025,11 +2025,11 @@ The `Str::replaceStart` method replaces the first occurrence of the given value use Illuminate\Support\Str; - $replaced = Str::replaceStart('Hello World', 'Hello', 'Laravel'); + $replaced = Str::replaceStart('Hello', 'Laravel', 'Hello World'); // Laravel World - $replaced = Str::replaceStart('Hello World', 'World', 'Laravel'); + $replaced = Str::replaceStart('World', 'Laravel', 'Hello World'); // Hello World @@ -2040,11 +2040,11 @@ The `Str::replaceEnd` method replaces the last occurrence of the given value onl use Illuminate\Support\Str; - $replaced = Str::replaceEnd('Hello World', 'World', 'Laravel'); + $replaced = Str::replaceEnd('World', 'Laravel', 'Hello World'); // Hello Laravel - $replaced = Str::replaceEnd('Hello World', 'Hello', 'Laravel'); + $replaced = Str::replaceEnd('Hello', 'Laravel', 'Hello World'); // Hello World From 7d49b2c434a3ad5c3fe947a079c0a7709c914f15 Mon Sep 17 00:00:00 2001 From: Mehul Kaushik Date: Wed, 30 Aug 2023 19:26:25 +0530 Subject: [PATCH 1079/2609] Fix Slack notification routing (#8997) * Fix the example of on demand routing for slack * Adds note for people upgrading versions * remove note --------- Co-authored-by: Taylor Otwell --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 8cc52ce5934..350d504ddb9 100644 --- a/notifications.md +++ b/notifications.md @@ -301,7 +301,7 @@ Sometimes you may need to send a notification to someone who is not stored as a Notification::route('mail', 'taylor@example.com') ->route('vonage', '5555555555') - ->route('slack', 'https://hooks.slack.com/services/...') + ->route('slack', '#slack-channel') ->route('broadcast', [new Channel('channel-name')]) ->notify(new InvoicePaid($invoice)); From fc79b2ccb72f58c88daef8757255ff1266434912 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 30 Aug 2023 15:37:42 +0100 Subject: [PATCH 1080/2609] [10.x] Adds documentation about Folio's `render` function (#8998) * Adds documentation about Folio's `render` function * Fixes missing removal * Update folio.md * formatting * remove extra words * formatting --------- Co-authored-by: Taylor Otwell --- folio.md | 114 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/folio.md b/folio.md index 62f9f4b8a45..c10966876f2 100644 --- a/folio.md +++ b/folio.md @@ -8,11 +8,11 @@ - [Nested Routes](#nested-routes) - [Index Routes](#index-routes) - [Route Parameters](#route-parameters) -- [Named Routes](#named-routes) - [Route Model Binding](#route-model-binding) - [Soft Deleted Models](#soft-deleted-models) +- [Render Hooks](#render-hooks) +- [Named Routes](#named-routes) - [Middleware](#middleware) -- [PHP Blocks](#php-blocks) - [Route Caching](#route-caching) @@ -164,33 +164,6 @@ When capturing multiple segments, the captured segments will be injected into th ``` - -## Named Routes - -You may specify a name for a given page's route using the `name` function: - -```php - - All Users - -``` - -If the page has parameters, you may simply pass their values to the `route` function: - -```php -route('users.show', ['user' => $user]); -``` - ## Route Model Binding @@ -243,6 +216,66 @@ withTrashed();
    ``` + +## Render Hooks + +By default, Folio will return the content of the page's Blade template as the response to the incoming request. However, you may customize the response by invoking the `render` function within the page's template. + +The `render` function accepts a closure which will receive the `View` instance being rendered by Folio, allowing you to add additional data to the view or customize the entire response. In addition to receiving the `View` instance, any additional route parameters or model bindings will also be provided to the `render` closure: + +```php +can('view', $post)) { + return response('Unauthorized', 403); + } + + return $view->with('photos', $post->author->photos); +}); ?> + +
    + {{ $post->content }} +
    + +
    + This author has also taken {{ count($photos) }} photos. +
    +``` + + +## Named Routes + +You may specify a name for a given page's route using the `name` function: + +```php + + All Users + +``` + +If the page has parameters, you may simply pass their values to the `route` function: + +```php +route('users.show', ['user' => $user]); +``` + ## Middleware @@ -300,29 +333,6 @@ Folio::path(resource_path('views/pages'))->middleware([ ]); ``` - -## PHP Blocks - -When using Folio, the `` tags are reserved for the Folio page definition functions such as `middleware` and `withTrashed`. - -Therefore, if you need to write PHP code that should be executed within your Blade template, you should use the `@php` Blade directive: - -```php -@php - if (! Auth::user()->can('view-posts', $user)) { - abort(403); - } - - $posts = $user->posts; -@endphp - -@foreach ($posts as $post) -
    - {{ $post->title }} -
    -@endforeach -``` - ## Route Caching From 3246bdcf66f2c8fd7a198816a3da9445a37b01e9 Mon Sep 17 00:00:00 2001 From: Mike Scott Date: Wed, 30 Aug 2023 16:58:24 +0100 Subject: [PATCH 1081/2609] Corrected misspelling of "complement" on Pennant page (#8999) --- pennant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index aeb07a1ddb0..650f5abbaf1 100644 --- a/pennant.md +++ b/pennant.md @@ -32,7 +32,7 @@ ## Introduction -[Laravel Pennant](https://github.com/laravel/pennant) is a simple and light-weight feature flag package - without the cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, compliment a trunk-based development strategy, and much more. +[Laravel Pennant](https://github.com/laravel/pennant) is a simple and light-weight feature flag package - without the cruft. Feature flags enable you to incrementally roll out new application features with confidence, A/B test new interface designs, complement a trunk-based development strategy, and much more. ## Installation From 9838f3423c7691b0561b21766724a364f9cac18b Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 31 Aug 2023 19:31:58 +0200 Subject: [PATCH 1082/2609] Add waring for redis queue serializer and compression (#9000) * add waring for redis serializer and compression * formatting --------- Co-authored-by: Taylor Otwell --- queues.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/queues.md b/queues.md index b7198dbc9cc..adc36f2907a 100644 --- a/queues.md +++ b/queues.md @@ -107,6 +107,9 @@ Finally, don't forget to instruct your application to use the `database` driver In order to use the `redis` queue driver, you should configure a Redis database connection in your `config/database.php` configuration file. +> **Warning** +> The `serializer` and `compression` Redis options are not supported by the `redis` queue driver. + **Redis Cluster** If your Redis queue connection uses a Redis Cluster, your queue names must contain a [key hash tag](https://redis.io/docs/reference/cluster-spec/#hash-tags). This is required in order to ensure all of the Redis keys for a given queue are placed into the same hash slot: From 2b47c9a66ae7cd95d3d1c6699aa49dfb9212d73e Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 2 Sep 2023 00:50:08 +1000 Subject: [PATCH 1083/2609] [10.x] Document `form.processing` (#8834) * Add processing docs * Update precognition.md * Update precognition.md --------- Co-authored-by: Taylor Otwell --- precognition.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/precognition.md b/precognition.md index b350b694706..f2f914357cb 100644 --- a/precognition.md +++ b/precognition.md @@ -83,7 +83,9 @@ const submit = () => form.submit(); {{ form.errors.email }}
    - + ``` @@ -161,6 +163,14 @@ const submit = () => form.submit() }); ``` +You may determine if a form submission request is in-flight by inspecting the form's `processing` property: + +```html + +``` + ### Using Vue & Inertia @@ -254,7 +264,9 @@ export default function Form() { /> {form.invalid('email') &&
    {form.errors.email}
    } - + ); }; @@ -327,6 +339,14 @@ const submit = (e) => { }; ``` +You may determine if a form submission request is in-flight by inspecting the form's `processing` property: + +```html + +``` + ### Using React & Inertia @@ -429,7 +449,9 @@ To enable live validation, you should bind the form's data to it's relevant inpu
    - + ``` @@ -478,6 +500,14 @@ You may also determine if an input has passed or failed validation by passing th > **Warning** > A form input will only appear as valid or invalid once it has changed and a validation response has been received. +You may determine if a form submission request is in-flight by inspecting the form's `processing` property: + +```html + +``` + #### Repopulating Old Form Data From 10d8f7766e11396acef573a8b71d3a69fd279c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20F=C3=B6rster?= Date: Mon, 4 Sep 2023 16:21:29 +0200 Subject: [PATCH 1084/2609] [10.x] shorten `jetstream` link (#9001) --- starter-kits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starter-kits.md b/starter-kits.md index 8d7af5b1749..2feb876d629 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -113,4 +113,4 @@ While Laravel Breeze provides a simple and minimal starting point for building a 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://livewire.laravel.com) or [Inertia](https://inertiajs.com) driven frontend scaffolding. -Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/3.x/introduction.html). +Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com). From d95b569c568ebcb6266b1c38ebfb63d0f33607b0 Mon Sep 17 00:00:00 2001 From: Marcus Jaschen Date: Mon, 4 Sep 2023 16:22:26 +0200 Subject: [PATCH 1085/2609] quote command argument to prevent shell filename expansion (#9002) Using an unquoted asterisk `*` in a command argument may lead to expansion into filenames - that's definitely not wanted here. --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index adc36f2907a..17aaa16c3ec 100644 --- a/queues.md +++ b/queues.md @@ -1697,7 +1697,7 @@ sudo supervisorctl reread 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). From 87030fcf7817abca5a9d52a2500257875ee889a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20F=C3=B6rster?= Date: Mon, 4 Sep 2023 17:59:27 +0200 Subject: [PATCH 1086/2609] [11.x] shorten jetstream link (#9003) --- starter-kits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starter-kits.md b/starter-kits.md index b4fab3552d0..c8b359c9bbf 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -126,4 +126,4 @@ While Laravel Breeze provides a simple and minimal starting point for building a 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](https://inertiajs.com) driven frontend scaffolding. -Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/2.x/introduction.html). +Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com). From 39273a81584fab4ca26005be36b325d883fd9714 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Sep 2023 08:34:26 -0500 Subject: [PATCH 1087/2609] wip --- pennant.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pennant.md b/pennant.md index 650f5abbaf1..23bdcd2aef8 100644 --- a/pennant.md +++ b/pennant.md @@ -363,6 +363,14 @@ Route::get('/api/servers', function () { })->middleware(['features:new-api,servers-api']); ``` +The `EnsureFeaturesAreActive` middleware also provides a static `all` method which may be used to assign to the middleware to routes: + +```php +Route::get('/api/servers', function () { + // ... +})->middleware([EnsureFeaturesAreActive::all(['new-api', 'servers-api'])]); +``` + #### Customizing The Response From 13adc5211e8767eda54bc316f79f2825c3626464 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Sep 2023 16:28:30 +0100 Subject: [PATCH 1088/2609] Adds `linkedin-openid` (#9004) --- socialite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/socialite.md b/socialite.md index 65985ca18f7..40c7ff253d8 100644 --- a/socialite.md +++ b/socialite.md @@ -39,7 +39,7 @@ When upgrading to a new major version of Socialite, it's important that you care Before using Socialite, you will need to add credentials for the OAuth providers your application utilizes. Typically, these credentials may be retrieved by creating a "developer application" within the dashboard of the service you will be authenticating with. -These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin`, `google`, `github`, `gitlab`, `bitbucket`, or `slack`, depending on the providers your application requires: +These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin-openid`, `google`, `github`, `gitlab`, `bitbucket`, or `slack`, depending on the providers your application requires: 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), From 03b62e608c7e71e1f5dcee4b7af07a69176b3ed8 Mon Sep 17 00:00:00 2001 From: Liam Anderson Date: Wed, 6 Sep 2023 16:55:39 +0100 Subject: [PATCH 1089/2609] [10.x] Add Vite::content() to Vite docs (#9009) * [10.x] Add Vite::content() to Vite docs * formatting --------- Co-authored-by: Taylor Otwell --- vite.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/vite.md b/vite.md index 46948046ea4..80e73c480fe 100644 --- a/vite.md +++ b/vite.md @@ -231,6 +231,29 @@ If needed, you may also specify the build path of your compiled assets when invo ``` + +#### Inline Assets + +Sometimes it may be necessary to include the raw content of assets rather than linking to the versioned URL of the asset. For example, you may need to include asset content directly into your page when passing HTML content to a PDF generator. You may output the content of Vite assets using the `content` method provided by the `Vite` facade: + +```blade +@php +use Illuminate\Support\Facades\Vite; +@endphp + + + + {{-- ... --}} + + + + +``` + ## Running Vite From 23754952f2b74b8d5398aeeab700ef7ac72ee156 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 7 Sep 2023 01:56:49 +1000 Subject: [PATCH 1090/2609] Simplify middleware documentation (#9006) --- pennant.md | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/pennant.md b/pennant.md index 23bdcd2aef8..862d8f10ec7 100644 --- a/pennant.md +++ b/pennant.md @@ -344,31 +344,15 @@ To make checking features in Blade a seamless experience, Pennant offers a `@fea ### Middleware -Pennant also includes a [middleware](/docs/{{version}}/middleware) that may be used to verify the currently authenticated user has access to a feature before a route is even invoked. To get started, you should add a middleware alias for the `EnsureFeaturesAreActive` middleware to your application's `app/Http/Kernel.php` file: +Pennant also includes a [middleware](/docs/{{version}}/middleware) that may be used to verify the currently authenticated user has access to a feature before a route is even invoked. You may assign the middleware to a route and specify the features that are required to access the route. If any of the specified features are inactive for the currently authenticated user, a `400 Bad Request` HTTP response will be returned by the route. Multiple features may be passed to the static `using` method. ```php +use Illuminate\Support\Facades\Route; use Laravel\Pennant\Middleware\EnsureFeaturesAreActive; -protected $middlewareAliases = [ - // ... - 'features' => EnsureFeaturesAreActive::class, -]; -``` - -Next, you may assign the middleware to a route and specify the features that are required to access the route. If any of the specified features are inactive for the currently authenticated user, a `400 Bad Request` HTTP response will be returned by the route. Multiple features may be specified using a comma-delimited list: - -```php -Route::get('/api/servers', function () { - // ... -})->middleware(['features:new-api,servers-api']); -``` - -The `EnsureFeaturesAreActive` middleware also provides a static `all` method which may be used to assign to the middleware to routes: - -```php Route::get('/api/servers', function () { // ... -})->middleware([EnsureFeaturesAreActive::all(['new-api', 'servers-api'])]); +})->middleware(EnsureFeaturesAreActive::using('new-api', 'servers-api')); ``` From d499f271b6a5c6f02b9d992a6fab40fb60d81770 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 7 Sep 2023 01:59:34 +1000 Subject: [PATCH 1091/2609] [10.x] Document Precognition testing (#9007) * Document Precognition testing * Update precognition.md --------- Co-authored-by: Taylor Otwell --- precognition.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/precognition.md b/precognition.md index f2f914357cb..f0c06826eab 100644 --- a/precognition.md +++ b/precognition.md @@ -11,6 +11,7 @@ - [Customizing Validation Rules](#customizing-validation-rules) - [Handling File Uploads](#handling-file-uploads) - [Managing Side-Effects](#managing-side-effects) +- [Testing](#testing) ## Introduction @@ -674,3 +675,23 @@ class InteractionMiddleware } } ``` + + +## Testing + +If you would like to make precognitive requests in your tests, Laravel's `TestCase` includes a `withPrecognition` helper which will add the `Precognition` request header. + +Additionally, if you would like to assert that a precognitive request was successful, e.g., did not return any validation errors, you may use the `assertSuccessfulPrecognition` method on the response: + +```php +public function test_it_validates_registration_form_with_precognition() +{ + $response = $this->withPrecognition() + ->post('/register', [ + 'name' => 'Taylor Otwell', + ]); + + $response->assertSuccessfulPrecognition(); + $this->assertSame(0, User::count()); +} +``` From bb641d67e649904171148ffe5fa5bd2681adf69a Mon Sep 17 00:00:00 2001 From: Pimienta Date: Wed, 6 Sep 2023 10:02:31 -0600 Subject: [PATCH 1092/2609] Updated usage examples for whereIn and whereNotIn in Laravel Scout Query Builder documentation (#9005) * Updated usage examples for whereIn and whereNotIn in Laravel Scout Query Builder documentation * Update scout.md --------- Co-authored-by: Taylor Otwell --- scout.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scout.md b/scout.md index a72136ac408..a7ab2c53939 100644 --- a/scout.md +++ b/scout.md @@ -572,10 +572,16 @@ Scout allows you to add simple "where" clauses to your search queries. Currently $orders = Order::search('Star Trek')->where('user_id', 1)->get(); -You may use the `whereIn` method to constrain results against a given set of values: +In addition, the `whereIn` method may be used to verify that a given column's value is contained within the given array: $orders = Order::search('Star Trek')->whereIn( - 'status', ['paid', 'open'] + 'status', ['open', 'paid'] + )->get(); + +The `whereNotIn` method verifies that the given column's value is not contained in the given array: + + $orders = Order::search('Star Trek')->whereNotIn( + 'status', ['closed'] )->get(); Since a search index is not a relational database, more advanced "where" clauses are not currently supported. From 30e267731cdf8e2339a16f92d05eee26d282b876 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 7 Sep 2023 00:12:30 +0800 Subject: [PATCH 1093/2609] [10.x] Dusk - Add Keyboard Support Improvements documentation (#9008) * [10.x] Dusk - Add Keyboard Support Improvements documentation PR: laravel/dusk#1053 Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * formatting * formatting --------- Signed-off-by: Mior Muhammad Zaki Co-authored-by: Taylor Otwell --- dusk.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/dusk.md b/dusk.md index b630a5c8bf7..719f6e67a2a 100644 --- a/dusk.md +++ b/dusk.md @@ -676,6 +676,66 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" > **Note** > All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). + +#### Fluent Keyboard Interactions + +Dusk also provides a `withKeyboard` method, allowing you to fluently perform complex keyboard interactions via the `Laravel\Dusk\Keyboard` class. The `Keyboard` class provides `press`, `release`, `type`, and `pause` methods: + + use Laravel\Dusk\Keyboard; + + $browser->withKeyboard(function (Keyboard $keyboard) { + $keyboard->press('c') + ->pause(1000) + ->release('c') + ->type(['c', 'e', 'o']); + }); + + +#### Keyboard Macros + +If you would like to define custom keyboard interactions that you can easily re-use throughout your test suite, you may use the `macro` method provided by the `Keyboard` class. Typically, you should call this method from a [service provider's](/docs/{{version}}/providers) `boot` method: + + type([ + OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', + ]); + + return $this; + }); + + Keyboard::macro('paste', function (string $element = null) { + $this->type([ + OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', + ]); + + return $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 as a method on a `Keyboard` instance: + + $browser->click('@textarea') + ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) + ->click('@another-textarea') + ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste()); + ### Using The Mouse @@ -710,6 +770,10 @@ The `clickAndHold` method may be used to simulate a mouse button being clicked a ->pause(1000) ->releaseMouse(); +The `controlClick` method may be used to simulate the `ctrl+click` event within the browser: + + $browser->controlClick(); + #### Mouseover From 6e349907d080a5dfda42b1c733b4dad38aa02bdb Mon Sep 17 00:00:00 2001 From: Tamsin Date: Wed, 6 Sep 2023 17:18:12 +0100 Subject: [PATCH 1094/2609] [10.x] Added details on using relative signed urls (#9010) * Added details on using relative signed urls Explained how to create a signature for the relative part of a url only, and how to validate it. * formatting --------- Co-authored-by: Taylor Otwell --- urls.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/urls.md b/urls.md index 464b48cf871..7550bea126f 100644 --- a/urls.md +++ b/urls.md @@ -97,6 +97,10 @@ For example, you might use signed URLs to implement a public "unsubscribe" link return URL::signedRoute('unsubscribe', ['user' => 1]); +You may exclude the domain from the signed URL hash by providing the `absolute` argument to the `signedRoute` method: + + return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false); + 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; @@ -145,6 +149,12 @@ Once you have registered the middleware in your kernel, you may attach it to a r // ... })->name('unsubscribe')->middleware('signed'); +If your signed URLs do not include the domain in the URL hash, you should provide the `relative` argument to the middleware: + + Route::post('/unsubscribe/{user}', function (Request $request) { + // ... + })->name('unsubscribe')->middleware('signed:relative'); + #### Responding To Invalid Signed Routes From 5c839958504b02bd331c30ec118a8846ae6c8f1a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Sep 2023 17:08:39 -0500 Subject: [PATCH 1095/2609] wip --- installation.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/installation.md b/installation.md index 86c55029c56..3c964c1729c 100644 --- a/installation.md +++ b/installation.md @@ -12,6 +12,7 @@ - [Environment Based Configuration](#environment-based-configuration) - [Databases & Migrations](#databases-and-migrations) - [Directory Configuration](#directory-configuration) +- [IDE Support](#ide-support) - [Next Steps](#next-steps) - [Laravel The Full Stack Framework](#laravel-the-fullstack-framework) - [Laravel The API Backend](#laravel-the-api-backend) @@ -258,6 +259,13 @@ php artisan migrate Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files present within your application. + +## IDE Support + +You are free to use any code editor you wish when developing Laravel applications; however, [PhpStorm](https://www.jetbrains.com/phpstorm/laravel/) offers extensive support for Laravel and its ecosystem, including [Laravel Pint](https://www.jetbrains.com/help/phpstorm/using-laravel-pint.html). + +In addition, the community maintained [Laravel Idea](https://laravel-idea.com/) PhpStorm plugin offers a variety of helpful IDE augmentations, including code generation, Eloquent syntax completion, validation rule completion, and more. + ## Next Steps From f138fb4c6a500de04f97ccf460fd4f932d0303d7 Mon Sep 17 00:00:00 2001 From: Jan Skopljak <12979324+ollosh@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:16:14 +0200 Subject: [PATCH 1096/2609] [10.x] Document additional attempts for exponential backoff (#9012) * Document additional attempts for exponential backoff * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 17aaa16c3ec..40c94fc89ec 100644 --- a/queues.md +++ b/queues.md @@ -1746,7 +1746,7 @@ If you require more complex logic for determining the job's backoff time, you ma 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 second 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, 10 seconds for the third retry, and 10 seconds for every subsequent retry if there are more attempts remaining: /** * Calculate the number of seconds to wait before retrying the job. From c58a6cbb1c9f407594695adf0af1061c6abd4d72 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 11 Sep 2023 17:21:33 +0330 Subject: [PATCH 1097/2609] Update database.md (#9017) --- database.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database.md b/database.md index 64123d35997..9e5cad1a760 100644 --- a/database.md +++ b/database.md @@ -19,10 +19,10 @@ Almost every modern web application interacts with a database. Laravel makes int
    -- MariaDB 10.3+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) -- MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) -- PostgreSQL 10.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) -- SQLite 3.8.8+ +- MariaDB 10.11+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) +- MySQL 8.0+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) +- PostgreSQL 12.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) +- SQLite 3.35.0+ - SQL Server 2017+ ([Version Policy](https://docs.microsoft.com/en-us/lifecycle/products/?products=sql-server))
    From 03b81924bb564048ec415dc3e8f2e9b62008ebf3 Mon Sep 17 00:00:00 2001 From: Kieran Mountford <43787971+kieran-paddle@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:56:34 +0100 Subject: [PATCH 1098/2609] Add warning re: Paddle Classic (#9018) * Add warning re: Paddle Classic * Update cashier-paddle.md --------- Co-authored-by: Taylor Otwell --- cashier-paddle.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 8e6e6214904..56b56d5696a 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -47,8 +47,11 @@ ## Introduction -[Laravel Cashier Paddle](https://github.com/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. +> **Warning** +> At this time, Cashier Paddle only supports Paddle Classic, which is not available to new Paddle customers unless you contact Paddle support. +[Laravel Cashier Paddle](https://github.com/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. +z 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). From 4b552e3d8eda7643403c114e0ad94013e6c8d5c8 Mon Sep 17 00:00:00 2001 From: Kieran Mountford <43787971+kieran-paddle@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:07:17 +0100 Subject: [PATCH 1099/2609] Fix typo (#9019) --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 56b56d5696a..d14ee95fd71 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -51,7 +51,7 @@ > At this time, Cashier Paddle only supports Paddle Classic, which is not available to new Paddle customers unless you contact Paddle support. [Laravel Cashier Paddle](https://github.com/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. -z + 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). From 4031d021c03b793e89243691d8cee417a6fec740 Mon Sep 17 00:00:00 2001 From: Paulo Freitas Date: Mon, 11 Sep 2023 11:19:13 -0300 Subject: [PATCH 1100/2609] [10.x] Document Collection::diffAssocUsing() method (#9016) * [10.x] Document Collection::diffAssocUsing() method Documentation for the `Collection::diffAssocUsing()` method introduced in laravel/framework#23192 * formatting --------- Co-authored-by: Taylor Otwell --- collections.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/collections.md b/collections.md index 8120cdfe350..370ebb4b9a0 100644 --- a/collections.md +++ b/collections.md @@ -112,6 +112,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [dd](#method-dd) [diff](#method-diff) [diffAssoc](#method-diffassoc) +[diffAssocUsing](#method-diffassocusing) [diffKeys](#method-diffkeys) [doesntContain](#method-doesntcontain) [dot](#method-dot) @@ -602,6 +603,29 @@ The `diffAssoc` method compares the collection against another collection or a p // ['color' => 'orange', 'remain' => 6] + +#### `diffAssocUsing()` {.collection-method} + +Unlike `diffAssoc`, `diffAssocUsing` accepts a user supplied callback function for the indices comparison: + + $collection = collect([ + 'color' => 'orange', + 'type' => 'fruit', + 'remain' => 6, + ]); + + $diff = $collection->diffAssocUsing([ + 'Color' => 'yellow', + 'Type' => 'fruit', + 'Remain' => 3, + ], 'strnatcasecmp'); + + $diff->all(); + + // ['color' => 'orange', 'remain' => 6] + +The callback must be a comparison function that returns an integer less than, equal to, or greater than zero. For more information, refer to the PHP documentation on [`array_diff_uassoc`](https://www.php.net/array_diff_uassoc#refsect1-function.array-diff-uassoc-parameters), which is the PHP function that the `diffAssocUsing` method utilizes internally. + #### `diffKeys()` {.collection-method} From d34d403a7fd72aa092b045476c5cef9436893ce2 Mon Sep 17 00:00:00 2001 From: Emil Andersson Date: Mon, 11 Sep 2023 16:27:53 +0200 Subject: [PATCH 1101/2609] Update queues.md (#9014) * Update queues.md Make note about timeouts interaction with retry_after. * Update queues.md * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/queues.md b/queues.md index 40c94fc89ec..debaa41b747 100644 --- a/queues.md +++ b/queues.md @@ -1094,9 +1094,6 @@ In this example, the job is released for ten seconds if the application is unabl #### Timeout -> **Warning** -> The `pcntl` PHP extension must be installed in order to specify job timeouts. - Often, you know roughly how long you expect your queued jobs to take. For this reason, Laravel allows you to specify a "timeout" value. By default, the timeout value is 60 seconds. 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: @@ -1125,6 +1122,9 @@ You may also define the maximum number of seconds a job should be allowed to run Sometimes, IO blocking processes such as sockets or outgoing HTTP connections may not respect your specified timeout. Therefore, when using these features, you should always attempt to specify a timeout using their APIs as well. For example, when using Guzzle, you should always specify a connection and request timeout value. +> **Warning** +> The `pcntl` PHP extension must be installed in order to specify job timeouts. In addition, a job's "timeout" value should always be less than its ["retry after"](#job-expiration) value. Otherwise, the job may be re-attempted before it has actually finished executing or timed out. + #### Failing On Timeout From 1d271881a6d72589db2f782bdb1af20bc50a9005 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Sep 2023 15:34:23 -0400 Subject: [PATCH 1102/2609] move string helpers to their own page (#9021) --- documentation.md | 1 + helpers.md | 2527 --------------------------------------------- strings.md | 2541 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2542 insertions(+), 2527 deletions(-) create mode 100644 strings.md diff --git a/documentation.md b/documentation.md index 64930006f17..73126d807e2 100644 --- a/documentation.md +++ b/documentation.md @@ -46,6 +46,7 @@ - [Processes](/docs/{{version}}/processes) - [Queues](/docs/{{version}}/queues) - [Rate Limiting](/docs/{{version}}/rate-limiting) + - [Strings](/docs/{{version}}/strings) - [Task Scheduling](/docs/{{version}}/scheduling) - ## Security - [Authentication](/docs/{{version}}/authentication) diff --git a/helpers.md b/helpers.md index 3e0e99b49a3..959966c204e 100644 --- a/helpers.md +++ b/helpers.md @@ -100,186 +100,6 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct
    - -### Strings - -
    - -[\__](#method-__) -[class_basename](#method-class-basename) -[e](#method-e) -[preg_replace_array](#method-preg-replace-array) -[Str::after](#method-str-after) -[Str::afterLast](#method-str-after-last) -[Str::ascii](#method-str-ascii) -[Str::before](#method-str-before) -[Str::beforeLast](#method-str-before-last) -[Str::between](#method-str-between) -[Str::betweenFirst](#method-str-between-first) -[Str::camel](#method-camel-case) -[Str::contains](#method-str-contains) -[Str::containsAll](#method-str-contains-all) -[Str::endsWith](#method-ends-with) -[Str::excerpt](#method-excerpt) -[Str::finish](#method-str-finish) -[Str::headline](#method-str-headline) -[Str::inlineMarkdown](#method-str-inline-markdown) -[Str::is](#method-str-is) -[Str::isAscii](#method-str-is-ascii) -[Str::isJson](#method-str-is-json) -[Str::isUlid](#method-str-is-ulid) -[Str::isUrl](#method-str-is-url) -[Str::isUuid](#method-str-is-uuid) -[Str::kebab](#method-kebab-case) -[Str::lcfirst](#method-str-lcfirst) -[Str::length](#method-str-length) -[Str::limit](#method-str-limit) -[Str::lower](#method-str-lower) -[Str::markdown](#method-str-markdown) -[Str::mask](#method-str-mask) -[Str::orderedUuid](#method-str-ordered-uuid) -[Str::padBoth](#method-str-padboth) -[Str::padLeft](#method-str-padleft) -[Str::padRight](#method-str-padright) -[Str::password](#method-str-password) -[Str::plural](#method-str-plural) -[Str::pluralStudly](#method-str-plural-studly) -[Str::random](#method-str-random) -[Str::remove](#method-str-remove) -[Str::repeat](#method-str-repeat) -[Str::replace](#method-str-replace) -[Str::replaceArray](#method-str-replace-array) -[Str::replaceFirst](#method-str-replace-first) -[Str::replaceLast](#method-str-replace-last) -[Str::replaceStart](#method-str-replace-start) -[Str::replaceEnd](#method-str-replace-end) -[Str::reverse](#method-str-reverse) -[Str::singular](#method-str-singular) -[Str::slug](#method-str-slug) -[Str::snake](#method-snake-case) -[Str::squish](#method-str-squish) -[Str::start](#method-str-start) -[Str::startsWith](#method-starts-with) -[Str::studly](#method-studly-case) -[Str::substr](#method-str-substr) -[Str::substrCount](#method-str-substrcount) -[Str::substrReplace](#method-str-substrreplace) -[Str::swap](#method-str-swap) -[Str::title](#method-title-case) -[Str::toHtmlString](#method-str-to-html-string) -[Str::ucfirst](#method-str-ucfirst) -[Str::ucsplit](#method-str-ucsplit) -[Str::upper](#method-str-upper) -[Str::ulid](#method-str-ulid) -[Str::uuid](#method-str-uuid) -[Str::wordCount](#method-str-word-count) -[Str::wordWrap](#method-str-word-wrap) -[Str::words](#method-str-words) -[Str::wrap](#method-str-wrap) -[str](#method-str) -[trans](#method-trans) -[trans_choice](#method-trans-choice) - -
    - - -### Fluent Strings - -
    - -[after](#method-fluent-str-after) -[afterLast](#method-fluent-str-after-last) -[append](#method-fluent-str-append) -[ascii](#method-fluent-str-ascii) -[basename](#method-fluent-str-basename) -[before](#method-fluent-str-before) -[beforeLast](#method-fluent-str-before-last) -[between](#method-fluent-str-between) -[betweenFirst](#method-fluent-str-between-first) -[camel](#method-fluent-str-camel) -[classBasename](#method-fluent-str-class-basename) -[contains](#method-fluent-str-contains) -[containsAll](#method-fluent-str-contains-all) -[dirname](#method-fluent-str-dirname) -[endsWith](#method-fluent-str-ends-with) -[excerpt](#method-fluent-str-excerpt) -[exactly](#method-fluent-str-exactly) -[explode](#method-fluent-str-explode) -[finish](#method-fluent-str-finish) -[headline](#method-fluent-str-headline) -[inlineMarkdown](#method-fluent-str-inline-markdown) -[is](#method-fluent-str-is) -[isAscii](#method-fluent-str-is-ascii) -[isEmpty](#method-fluent-str-is-empty) -[isNotEmpty](#method-fluent-str-is-not-empty) -[isJson](#method-fluent-str-is-json) -[isUlid](#method-fluent-str-is-ulid) -[isUrl](#method-fluent-str-is-url) -[isUuid](#method-fluent-str-is-uuid) -[kebab](#method-fluent-str-kebab) -[lcfirst](#method-fluent-str-lcfirst) -[length](#method-fluent-str-length) -[limit](#method-fluent-str-limit) -[lower](#method-fluent-str-lower) -[ltrim](#method-fluent-str-ltrim) -[markdown](#method-fluent-str-markdown) -[mask](#method-fluent-str-mask) -[match](#method-fluent-str-match) -[matchAll](#method-fluent-str-match-all) -[isMatch](#method-fluent-str-is-match) -[newLine](#method-fluent-str-new-line) -[padBoth](#method-fluent-str-padboth) -[padLeft](#method-fluent-str-padleft) -[padRight](#method-fluent-str-padright) -[pipe](#method-fluent-str-pipe) -[plural](#method-fluent-str-plural) -[prepend](#method-fluent-str-prepend) -[remove](#method-fluent-str-remove) -[repeat](#method-fluent-str-repeat) -[replace](#method-fluent-str-replace) -[replaceArray](#method-fluent-str-replace-array) -[replaceFirst](#method-fluent-str-replace-first) -[replaceLast](#method-fluent-str-replace-last) -[replaceMatches](#method-fluent-str-replace-matches) -[replaceStart](#method-fluent-str-replace-start) -[replaceEnd](#method-fluent-str-replace-end) -[rtrim](#method-fluent-str-rtrim) -[scan](#method-fluent-str-scan) -[singular](#method-fluent-str-singular) -[slug](#method-fluent-str-slug) -[snake](#method-fluent-str-snake) -[split](#method-fluent-str-split) -[squish](#method-fluent-str-squish) -[start](#method-fluent-str-start) -[startsWith](#method-fluent-str-starts-with) -[studly](#method-fluent-str-studly) -[substr](#method-fluent-str-substr) -[substrReplace](#method-fluent-str-substrreplace) -[swap](#method-fluent-str-swap) -[tap](#method-fluent-str-tap) -[test](#method-fluent-str-test) -[title](#method-fluent-str-title) -[trim](#method-fluent-str-trim) -[ucfirst](#method-fluent-str-ucfirst) -[ucsplit](#method-fluent-str-ucsplit) -[upper](#method-fluent-str-upper) -[when](#method-fluent-str-when) -[whenContains](#method-fluent-str-when-contains) -[whenContainsAll](#method-fluent-str-when-contains-all) -[whenEmpty](#method-fluent-str-when-empty) -[whenNotEmpty](#method-fluent-str-when-not-empty) -[whenStartsWith](#method-fluent-str-when-starts-with) -[whenEndsWith](#method-fluent-str-when-ends-with) -[whenExactly](#method-fluent-str-when-exactly) -[whenNotExactly](#method-fluent-str-when-not-exactly) -[whenIs](#method-fluent-str-when-is) -[whenIsAscii](#method-fluent-str-when-is-ascii) -[whenIsUlid](#method-fluent-str-when-is-ulid) -[whenIsUuid](#method-fluent-str-when-is-uuid) -[whenTest](#method-fluent-str-when-test) -[wordCount](#method-fluent-str-word-count) -[words](#method-fluent-str-words) -
    @@ -358,19 +178,6 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct
    - -## Method Listing - - - ## Arrays & Objects @@ -1360,2340 +1167,6 @@ The `storage_path` function returns the fully qualified path to your application $path = storage_path('app/file.txt'); - -## Strings - - -#### `__()` {.collection-method} - -The `__` function translates the given translation string or translation key using your [language files](/docs/{{version}}/localization): - - echo __('Welcome to our application'); - - echo __('messages.welcome'); - -If the specified translation string or key does not exist, the `__` function will return the given value. So, using the example above, the `__` function would return `messages.welcome` if that translation key does not exist. - - -#### `class_basename()` {.collection-method} - -The `class_basename` function returns the class name of the given class with the class's namespace removed: - - $class = class_basename('Foo\Bar\Baz'); - - // Baz - - -#### `e()` {.collection-method} - -The `e` function runs PHP's `htmlspecialchars` function with the `double_encode` option set to `true` by default: - - echo e('foo'); - - // <html>foo</html> - - -#### `preg_replace_array()` {.collection-method} - -The `preg_replace_array` function replaces a given pattern in the string sequentially using an array: - - $string = 'The event will take place between :start and :end'; - - $replaced = preg_replace_array('/:[a-z_]+/', ['8:30', '9:00'], $string); - - // The event will take place between 8:30 and 9:00 - - -#### `Str::after()` {.collection-method} - -The `Str::after` method returns everything after the given value in a string. The entire string will be returned if the value does not exist within the string: - - use Illuminate\Support\Str; - - $slice = Str::after('This is my name', 'This is'); - - // ' my name' - - -#### `Str::afterLast()` {.collection-method} - -The `Str::afterLast` method returns everything after the last occurrence of the given value in a string. The entire string will be returned if the value does not exist within the string: - - use Illuminate\Support\Str; - - $slice = Str::afterLast('App\Http\Controllers\Controller', '\\'); - - // 'Controller' - - -#### `Str::ascii()` {.collection-method} - -The `Str::ascii` method will attempt to transliterate the string into an ASCII value: - - use Illuminate\Support\Str; - - $slice = Str::ascii('û'); - - // 'u' - - -#### `Str::before()` {.collection-method} - -The `Str::before` method returns everything before the given value in a string: - - use Illuminate\Support\Str; - - $slice = Str::before('This is my name', 'my name'); - - // 'This is ' - - -#### `Str::beforeLast()` {.collection-method} - -The `Str::beforeLast` method returns everything before the last occurrence of the given value in a string: - - use Illuminate\Support\Str; - - $slice = Str::beforeLast('This is my name', 'is'); - - // 'This ' - - -#### `Str::between()` {.collection-method} - -The `Str::between` method returns the portion of a string between two values: - - use Illuminate\Support\Str; - - $slice = Str::between('This is my name', 'This', 'name'); - - // ' is my ' - - -#### `Str::betweenFirst()` {.collection-method} - -The `Str::betweenFirst` method returns the smallest possible portion of a string between two values: - - use Illuminate\Support\Str; - - $slice = Str::betweenFirst('[a] bc [d]', '[', ']'); - - // 'a' - - -#### `Str::camel()` {.collection-method} - -The `Str::camel` method converts the given string to `camelCase`: - - use Illuminate\Support\Str; - - $converted = Str::camel('foo_bar'); - - // fooBar - - -#### `Str::contains()` {.collection-method} - -The `Str::contains` method determines if the given string contains the given value. This method is case sensitive: - - use Illuminate\Support\Str; - - $contains = Str::contains('This is my name', 'my'); - - // true - -You may also pass an array of values to determine if the given string contains any of the values in the array: - - use Illuminate\Support\Str; - - $contains = Str::contains('This is my name', ['my', 'foo']); - - // true - - -#### `Str::containsAll()` {.collection-method} - -The `Str::containsAll` method determines if the given string contains all of the values in a given array: - - use Illuminate\Support\Str; - - $containsAll = Str::containsAll('This is my name', ['my', 'name']); - - // true - - -#### `Str::endsWith()` {.collection-method} - -The `Str::endsWith` method determines if the given string ends with the given value: - - use Illuminate\Support\Str; - - $result = Str::endsWith('This is my name', 'name'); - - // true - - -You may also pass an array of values to determine if the given string ends with any of the values in the array: - - use Illuminate\Support\Str; - - $result = Str::endsWith('This is my name', ['name', 'foo']); - - // true - - $result = Str::endsWith('This is my name', ['this', 'foo']); - - // false - - -#### `Str::excerpt()` {.collection-method} - -The `Str::excerpt` method extracts an excerpt from a given string that matches the first instance of a phrase within that string: - - use Illuminate\Support\Str; - - $excerpt = Str::excerpt('This is my name', 'my', [ - 'radius' => 3 - ]); - - // '...is my na...' - -The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. - -In addition, you may use the `omission` option to define the string that will be prepended and appended to the truncated string: - - use Illuminate\Support\Str; - - $excerpt = Str::excerpt('This is my name', 'name', [ - 'radius' => 3, - 'omission' => '(...) ' - ]); - - // '(...) my name' - - -#### `Str::finish()` {.collection-method} - -The `Str::finish` method adds a single instance of the given value to a string if it does not already end with that value: - - use Illuminate\Support\Str; - - $adjusted = Str::finish('this/string', '/'); - - // this/string/ - - $adjusted = Str::finish('this/string/', '/'); - - // this/string/ - - -#### `Str::headline()` {.collection-method} - -The `Str::headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: - - use Illuminate\Support\Str; - - $headline = Str::headline('steve_jobs'); - - // Steve Jobs - - $headline = Str::headline('EmailNotificationSent'); - - // Email Notification Sent - - -#### `Str::inlineMarkdown()` {.collection-method} - -The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: - - use Illuminate\Support\Str; - - $html = Str::inlineMarkdown('**Laravel**'); - - // Laravel - - -#### `Str::is()` {.collection-method} - -The `Str::is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values: - - use Illuminate\Support\Str; - - $matches = Str::is('foo*', 'foobar'); - - // true - - $matches = Str::is('baz*', 'foobar'); - - // false - - -#### `Str::isAscii()` {.collection-method} - -The `Str::isAscii` method determines if a given string is 7 bit ASCII: - - use Illuminate\Support\Str; - - $isAscii = Str::isAscii('Taylor'); - - // true - - $isAscii = Str::isAscii('ü'); - - // false - - -#### `Str::isJson()` {.collection-method} - -The `Str::isJson` method determines if the given string is valid JSON: - - use Illuminate\Support\Str; - - $result = Str::isJson('[1,2,3]'); - - // true - - $result = Str::isJson('{"first": "John", "last": "Doe"}'); - - // true - - $result = Str::isJson('{first: "John", last: "Doe"}'); - - // false - - -#### `Str::isUrl()` {.collection-method} - -The `Str::isUrl` method determines if the given string is a valid URL: - - use Illuminate\Support\Str; - - $isUrl = Str::isUrl('http://example.com/'); - - // true - - $isUrl = Str::isUrl('laravel'); - - // false - - -#### `Str::isUlid()` {.collection-method} - -The `Str::isUlid` method determines if the given string is a valid ULID: - - use Illuminate\Support\Str; - - $isUlid = Str::isUlid('01gd6r360bp37zj17nxb55yv40'); - - // true - - $isUlid = Str::isUlid('laravel'); - - // false - - -#### `Str::isUuid()` {.collection-method} - -The `Str::isUuid` method determines if the given string is a valid UUID: - - use Illuminate\Support\Str; - - $isUuid = Str::isUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de'); - - // true - - $isUuid = Str::isUuid('laravel'); - - // false - - -#### `Str::kebab()` {.collection-method} - -The `Str::kebab` method converts the given string to `kebab-case`: - - use Illuminate\Support\Str; - - $converted = Str::kebab('fooBar'); - - // foo-bar - - -#### `Str::lcfirst()` {.collection-method} - -The `Str::lcfirst` method returns the given string with the first character lowercased: - - use Illuminate\Support\Str; - - $string = Str::lcfirst('Foo Bar'); - - // foo Bar - - -#### `Str::length()` {.collection-method} - -The `Str::length` method returns the length of the given string: - - use Illuminate\Support\Str; - - $length = Str::length('Laravel'); - - // 7 - - -#### `Str::limit()` {.collection-method} - -The `Str::limit` method truncates the given string to the specified length: - - use Illuminate\Support\Str; - - $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20); - - // The quick brown fox... - -You may pass a third argument to the method to change the string that will be appended to the end of the truncated string: - - use Illuminate\Support\Str; - - $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20, ' (...)'); - - // The quick brown fox (...) - - -#### `Str::lower()` {.collection-method} - -The `Str::lower` method converts the given string to lowercase: - - use Illuminate\Support\Str; - - $converted = Str::lower('LARAVEL'); - - // laravel - - -#### `Str::markdown()` {.collection-method} - -The `Str::markdown` method converts GitHub flavored Markdown into HTML using [CommonMark](https://commonmark.thephpleague.com/): - - use Illuminate\Support\Str; - - $html = Str::markdown('# Laravel'); - - //

    Laravel

    - - $html = Str::markdown('# Taylor Otwell', [ - 'html_input' => 'strip', - ]); - - //

    Taylor Otwell

    - - -#### `Str::mask()` {.collection-method} - -The `Str::mask` method masks a portion of a string with a repeated character, and may be used to obfuscate segments of strings such as email addresses and phone numbers: - - use Illuminate\Support\Str; - - $string = Str::mask('taylor@example.com', '*', 3); - - // tay*************** - -If needed, you provide a negative number as the third argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: - - $string = Str::mask('taylor@example.com', '*', -15, 3); - - // tay***@example.com - - -#### `Str::orderedUuid()` {.collection-method} - -The `Str::orderedUuid` method generates a "timestamp first" UUID that may be efficiently stored in an indexed database column. Each UUID that is generated using this method will be sorted after UUIDs previously generated using the method: - - use Illuminate\Support\Str; - - return (string) Str::orderedUuid(); - - -#### `Str::padBoth()` {.collection-method} - -The `Str::padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches a desired length: - - use Illuminate\Support\Str; - - $padded = Str::padBoth('James', 10, '_'); - - // '__James___' - - $padded = Str::padBoth('James', 10); - - // ' James ' - - -#### `Str::padLeft()` {.collection-method} - -The `Str::padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches a desired length: - - use Illuminate\Support\Str; - - $padded = Str::padLeft('James', 10, '-='); - - // '-=-=-James' - - $padded = Str::padLeft('James', 10); - - // ' James' - - -#### `Str::padRight()` {.collection-method} - -The `Str::padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches a desired length: - - use Illuminate\Support\Str; - - $padded = Str::padRight('James', 10, '-'); - - // 'James-----' - - $padded = Str::padRight('James', 10); - - // 'James ' - - -#### `Str::password()` {.collection-method} - -The `Str::password` method may be used to generate a secure, random password of a given length. The password will consist of a combination of letters, numbers, symbols, and spaces. By default, passwords are 32 characters long: - - use Illuminate\Support\Str; - - $password = Str::password(); - - // 'EbJo2vE-AS:U,$%_gkrV4n,q~1xy/-_4' - - $password = Str::password(12); - - // 'qwuar>#V|i]N' - - -#### `Str::plural()` {.collection-method} - -The `Str::plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - - use Illuminate\Support\Str; - - $plural = Str::plural('car'); - - // cars - - $plural = Str::plural('child'); - - // children - -You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: - - use Illuminate\Support\Str; - - $plural = Str::plural('child', 2); - - // children - - $singular = Str::plural('child', 1); - - // child - - -#### `Str::pluralStudly()` {.collection-method} - -The `Str::pluralStudly` method converts a singular word string formatted in studly caps case to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - - use Illuminate\Support\Str; - - $plural = Str::pluralStudly('VerifiedHuman'); - - // VerifiedHumans - - $plural = Str::pluralStudly('UserFeedback'); - - // UserFeedback - -You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: - - use Illuminate\Support\Str; - - $plural = Str::pluralStudly('VerifiedHuman', 2); - - // VerifiedHumans - - $singular = Str::pluralStudly('VerifiedHuman', 1); - - // VerifiedHuman - - -#### `Str::random()` {.collection-method} - -The `Str::random` method generates a random string of the specified length. This function uses PHP's `random_bytes` function: - - use Illuminate\Support\Str; - - $random = Str::random(40); - - -#### `Str::remove()` {.collection-method} - -The `Str::remove` method removes the given value or array of values from the string: - - use Illuminate\Support\Str; - - $string = 'Peter Piper picked a peck of pickled peppers.'; - - $removed = Str::remove('e', $string); - - // Ptr Pipr pickd a pck of pickld ppprs. - -You may also pass `false` as a third argument to the `remove` method to ignore case when removing strings. - - -#### `Str::repeat()` {.collection-method} - -The `Str::repeat` method repeats the given string: - -```php -use Illuminate\Support\Str; - -$string = 'a'; - -$repeat = Str::repeat($string, 5); - -// aaaaa -``` - - -#### `Str::replace()` {.collection-method} - -The `Str::replace` method replaces a given string within the string: - - use Illuminate\Support\Str; - - $string = 'Laravel 8.x'; - - $replaced = Str::replace('8.x', '9.x', $string); - - // Laravel 9.x - -The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: - - Str::replace('Framework', 'Laravel', caseSensitive: false); - - -#### `Str::replaceArray()` {.collection-method} - -The `Str::replaceArray` method replaces a given value in the string sequentially using an array: - - use Illuminate\Support\Str; - - $string = 'The event will take place between ? and ?'; - - $replaced = Str::replaceArray('?', ['8:30', '9:00'], $string); - - // The event will take place between 8:30 and 9:00 - - -#### `Str::replaceFirst()` {.collection-method} - -The `Str::replaceFirst` method replaces the first occurrence of a given value in a string: - - use Illuminate\Support\Str; - - $replaced = Str::replaceFirst('the', 'a', 'the quick brown fox jumps over the lazy dog'); - - // a quick brown fox jumps over the lazy dog - - -#### `Str::replaceLast()` {.collection-method} - -The `Str::replaceLast` method replaces the last occurrence of a given value in a string: - - use Illuminate\Support\Str; - - $replaced = Str::replaceLast('the', 'a', 'the quick brown fox jumps over the lazy dog'); - - // the quick brown fox jumps over a lazy dog - - - -#### `Str::replaceStart()` {.collection-method} - -The `Str::replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: - - use Illuminate\Support\Str; - - $replaced = Str::replaceStart('Hello', 'Laravel', 'Hello World'); - - // Laravel World - - $replaced = Str::replaceStart('World', 'Laravel', 'Hello World'); - - // Hello World - - -#### `Str::replaceEnd()` {.collection-method} - -The `Str::replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: - - use Illuminate\Support\Str; - - $replaced = Str::replaceEnd('World', 'Laravel', 'Hello World'); - - // Hello Laravel - - $replaced = Str::replaceEnd('Hello', 'Laravel', 'Hello World'); - - // Hello World - - -#### `Str::reverse()` {.collection-method} - -The `Str::reverse` method reverses the given string: - - use Illuminate\Support\Str; - - $reversed = Str::reverse('Hello World'); - - // dlroW olleH - - -#### `Str::singular()` {.collection-method} - -The `Str::singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - - use Illuminate\Support\Str; - - $singular = Str::singular('cars'); - - // car - - $singular = Str::singular('children'); - - // child - - -#### `Str::slug()` {.collection-method} - -The `Str::slug` method generates a URL friendly "slug" from the given string: - - use Illuminate\Support\Str; - - $slug = Str::slug('Laravel 5 Framework', '-'); - - // laravel-5-framework - - -#### `Str::snake()` {.collection-method} - -The `Str::snake` method converts the given string to `snake_case`: - - use Illuminate\Support\Str; - - $converted = Str::snake('fooBar'); - - // foo_bar - - $converted = Str::snake('fooBar', '-'); - - // foo-bar - - -#### `Str::squish()` {.collection-method} - -The `Str::squish` method removes all extraneous white space from a string, including extraneous white space between words: - - use Illuminate\Support\Str; - - $string = Str::squish(' laravel framework '); - - // laravel framework - - -#### `Str::start()` {.collection-method} - -The `Str::start` method adds a single instance of the given value to a string if it does not already start with that value: - - use Illuminate\Support\Str; - - $adjusted = Str::start('this/string', '/'); - - // /this/string - - $adjusted = Str::start('/this/string', '/'); - - // /this/string - - -#### `Str::startsWith()` {.collection-method} - -The `Str::startsWith` method determines if the given string begins with the given value: - - use Illuminate\Support\Str; - - $result = Str::startsWith('This is my name', 'This'); - - // true - -If an array of possible values is passed, the `startsWith` method will return `true` if the string begins with any of the given values: - - $result = Str::startsWith('This is my name', ['This', 'That', 'There']); - - // true - - -#### `Str::studly()` {.collection-method} - -The `Str::studly` method converts the given string to `StudlyCase`: - - use Illuminate\Support\Str; - - $converted = Str::studly('foo_bar'); - - // FooBar - - -#### `Str::substr()` {.collection-method} - -The `Str::substr` method returns the portion of string specified by the start and length parameters: - - use Illuminate\Support\Str; - - $converted = Str::substr('The Laravel Framework', 4, 7); - - // Laravel - - -#### `Str::substrCount()` {.collection-method} - -The `Str::substrCount` method returns the number of occurrences of a given value in the given string: - - use Illuminate\Support\Str; - - $count = Str::substrCount('If you like ice cream, you will like snow cones.', 'like'); - - // 2 - - -#### `Str::substrReplace()` {.collection-method} - -The `Str::substrReplace` method replaces text within a portion of a string, starting at the position specified by the third argument and replacing the number of characters specified by the fourth argument. Passing `0` to the method's fourth argument will insert the string at the specified position without replacing any of the existing characters in the string: - - use Illuminate\Support\Str; - - $result = Str::substrReplace('1300', ':', 2); - // 13: - - $result = Str::substrReplace('1300', ':', 2, 0); - // 13:00 - - -#### `Str::swap()` {.collection-method} - -The `Str::swap` method replaces multiple values in the given string using PHP's `strtr` function: - - use Illuminate\Support\Str; - - $string = Str::swap([ - 'Tacos' => 'Burritos', - 'great' => 'fantastic', - ], 'Tacos are great!'); - - // Burritos are fantastic! - - -#### `Str::title()` {.collection-method} - -The `Str::title` method converts the given string to `Title Case`: - - use Illuminate\Support\Str; - - $converted = Str::title('a nice title uses the correct case'); - - // A Nice Title Uses The Correct Case - - -#### `Str::toHtmlString()` {.collection-method} - -The `Str::toHtmlString` method converts the string instance to an instance of `Illuminate\Support\HtmlString`, which may be displayed in Blade templates: - - use Illuminate\Support\Str; - - $htmlString = Str::of('Nuno Maduro')->toHtmlString(); - - -#### `Str::ucfirst()` {.collection-method} - -The `Str::ucfirst` method returns the given string with the first character capitalized: - - use Illuminate\Support\Str; - - $string = Str::ucfirst('foo bar'); - - // Foo bar - - -#### `Str::ucsplit()` {.collection-method} - -The `Str::ucsplit` method splits the given string into an array by uppercase characters: - - use Illuminate\Support\Str; - - $segments = Str::ucsplit('FooBar'); - - // [0 => 'Foo', 1 => 'Bar'] - - -#### `Str::upper()` {.collection-method} - -The `Str::upper` method converts the given string to uppercase: - - use Illuminate\Support\Str; - - $string = Str::upper('laravel'); - - // LARAVEL - - -#### `Str::ulid()` {.collection-method} - -The `Str::ulid` method generates a ULID, which is a compact, time-ordered unique identifier: - - use Illuminate\Support\Str; - - return (string) Str::ulid(); - - // 01gd6r360bp37zj17nxb55yv40 - -If you would like to retrieve a `Illuminate\Support\Carbon` date instance representing the date and time that a given ULID was created, you may use the `createFromId` method provided by Laravel's Carbon integration: - -```php -use Illuminate\Support\Carbon; -use Illuminate\Support\Str; - -$date = Carbon::createFromId((string) Str::ulid()); -``` - - -#### `Str::uuid()` {.collection-method} - -The `Str::uuid` method generates a UUID (version 4): - - use Illuminate\Support\Str; - - return (string) Str::uuid(); - - -#### `Str::wordCount()` {.collection-method} - -The `Str::wordCount` method returns the number of words that a string contains: - -```php -use Illuminate\Support\Str; - -Str::wordCount('Hello, world!'); // 2 -``` - - -#### `Str::wordWrap()` {.collection-method} - -The `Str::wordWrap` method wraps a string to a given number of characters: - - use Illuminate\Support\Str; - - $text = "The quick brown fox jumped over the lazy dog." - - Str::wordWrap($text, characters: 20, break: "
    \n"); - - /* - The quick brown fox
    - jumped over the lazy
    - dog. - */ - - -#### `Str::words()` {.collection-method} - -The `Str::words` method limits the number of words in a string. An additional string may be passed to this method via its third argument to specify which string should be appended to the end of the truncated string: - - use Illuminate\Support\Str; - - return Str::words('Perfectly balanced, as all things should be.', 3, ' >>>'); - - // Perfectly balanced, as >>> - - -#### `Str::wrap()` {.collection-method} - -The `Str::wrap` method wraps the given string with an additional string or pair of strings: - - use Illuminate\Support\Str; - - Str::wrap('Laravel', '"'); - - // "Laravel" - - Str::wrap('is', before: 'This ', after: ' Laravel!'); - - // This is Laravel! - - -#### `str()` {.collection-method} - -The `str` function returns a new `Illuminate\Support\Stringable` instance of the given string. This function is equivalent to the `Str::of` method: - - $string = str('Taylor')->append(' Otwell'); - - // 'Taylor Otwell' - -If no argument is provided to the `str` function, the function returns an instance of `Illuminate\Support\Str`: - - $snake = str()->snake('FooBar'); - - // 'foo_bar' - - -#### `trans()` {.collection-method} - -The `trans` function translates the given translation key using your [language files](/docs/{{version}}/localization): - - echo trans('messages.welcome'); - -If the specified translation key does not exist, the `trans` function will return the given key. So, using the example above, the `trans` function would return `messages.welcome` if the translation key does not exist. - - -#### `trans_choice()` {.collection-method} - -The `trans_choice` function translates the given translation key with inflection: - - echo trans_choice('messages.notifications', $unreadCount); - -If the specified translation key does not exist, the `trans_choice` function will return the given key. So, using the example above, the `trans_choice` function would return `messages.notifications` if the translation key does not exist. - - -## Fluent Strings - -Fluent strings provide a more fluent, object-oriented interface for working with string values, allowing you to chain multiple string operations together using a more readable syntax compared to traditional string operations. - - -#### `after` {.collection-method} - -The `after` method returns everything after the given value in a string. The entire string will be returned if the value does not exist within the string: - - use Illuminate\Support\Str; - - $slice = Str::of('This is my name')->after('This is'); - - // ' my name' - - -#### `afterLast` {.collection-method} - -The `afterLast` method returns everything after the last occurrence of the given value in a string. The entire string will be returned if the value does not exist within the string: - - use Illuminate\Support\Str; - - $slice = Str::of('App\Http\Controllers\Controller')->afterLast('\\'); - - // 'Controller' - - -#### `append` {.collection-method} - -The `append` method appends the given values to the string: - - use Illuminate\Support\Str; - - $string = Str::of('Taylor')->append(' Otwell'); - - // 'Taylor Otwell' - - -#### `ascii` {.collection-method} - -The `ascii` method will attempt to transliterate the string into an ASCII value: - - use Illuminate\Support\Str; - - $string = Str::of('ü')->ascii(); - - // 'u' - - -#### `basename` {.collection-method} - -The `basename` method will return the trailing name component of the given string: - - use Illuminate\Support\Str; - - $string = Str::of('/foo/bar/baz')->basename(); - - // 'baz' - -If needed, you may provide an "extension" that will be removed from the trailing component: - - use Illuminate\Support\Str; - - $string = Str::of('/foo/bar/baz.jpg')->basename('.jpg'); - - // 'baz' - - -#### `before` {.collection-method} - -The `before` method returns everything before the given value in a string: - - use Illuminate\Support\Str; - - $slice = Str::of('This is my name')->before('my name'); - - // 'This is ' - - -#### `beforeLast` {.collection-method} - -The `beforeLast` method returns everything before the last occurrence of the given value in a string: - - use Illuminate\Support\Str; - - $slice = Str::of('This is my name')->beforeLast('is'); - - // 'This ' - - -#### `between` {.collection-method} - -The `between` method returns the portion of a string between two values: - - use Illuminate\Support\Str; - - $converted = Str::of('This is my name')->between('This', 'name'); - - // ' is my ' - - -#### `betweenFirst` {.collection-method} - -The `betweenFirst` method returns the smallest possible portion of a string between two values: - - use Illuminate\Support\Str; - - $converted = Str::of('[a] bc [d]')->betweenFirst('[', ']'); - - // 'a' - - -#### `camel` {.collection-method} - -The `camel` method converts the given string to `camelCase`: - - use Illuminate\Support\Str; - - $converted = Str::of('foo_bar')->camel(); - - // fooBar - - -#### `classBasename` {.collection-method} - -The `classBasename` method returns the class name of the given class with the class's namespace removed: - - use Illuminate\Support\Str; - - $class = Str::of('Foo\Bar\Baz')->classBasename(); - - // Baz - - -#### `contains` {.collection-method} - -The `contains` method determines if the given string contains the given value. This method is case sensitive: - - use Illuminate\Support\Str; - - $contains = Str::of('This is my name')->contains('my'); - - // true - -You may also pass an array of values to determine if the given string contains any of the values in the array: - - use Illuminate\Support\Str; - - $contains = Str::of('This is my name')->contains(['my', 'foo']); - - // true - - -#### `containsAll` {.collection-method} - -The `containsAll` method determines if the given string contains all of the values in the given array: - - use Illuminate\Support\Str; - - $containsAll = Str::of('This is my name')->containsAll(['my', 'name']); - - // true - - -#### `dirname` {.collection-method} - -The `dirname` method returns the parent directory portion of the given string: - - use Illuminate\Support\Str; - - $string = Str::of('/foo/bar/baz')->dirname(); - - // '/foo/bar' - -If necessary, you may specify how many directory levels you wish to trim from the string: - - use Illuminate\Support\Str; - - $string = Str::of('/foo/bar/baz')->dirname(2); - - // '/foo' - - -#### `excerpt` {.collection-method} - -The `excerpt` method extracts an excerpt from the string that matches the first instance of a phrase within that string: - - use Illuminate\Support\Str; - - $excerpt = Str::of('This is my name')->excerpt('my', [ - 'radius' => 3 - ]); - - // '...is my na...' - -The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. - -In addition, you may use the `omission` option to change the string that will be prepended and appended to the truncated string: - - use Illuminate\Support\Str; - - $excerpt = Str::of('This is my name')->excerpt('name', [ - 'radius' => 3, - 'omission' => '(...) ' - ]); - - // '(...) my name' - - -#### `endsWith` {.collection-method} - -The `endsWith` method determines if the given string ends with the given value: - - use Illuminate\Support\Str; - - $result = Str::of('This is my name')->endsWith('name'); - - // true - -You may also pass an array of values to determine if the given string ends with any of the values in the array: - - use Illuminate\Support\Str; - - $result = Str::of('This is my name')->endsWith(['name', 'foo']); - - // true - - $result = Str::of('This is my name')->endsWith(['this', 'foo']); - - // false - - -#### `exactly` {.collection-method} - -The `exactly` method determines if the given string is an exact match with another string: - - use Illuminate\Support\Str; - - $result = Str::of('Laravel')->exactly('Laravel'); - - // true - - -#### `explode` {.collection-method} - -The `explode` method splits the string by the given delimiter and returns a collection containing each section of the split string: - - use Illuminate\Support\Str; - - $collection = Str::of('foo bar baz')->explode(' '); - - // collect(['foo', 'bar', 'baz']) - - -#### `finish` {.collection-method} - -The `finish` method adds a single instance of the given value to a string if it does not already end with that value: - - use Illuminate\Support\Str; - - $adjusted = Str::of('this/string')->finish('/'); - - // this/string/ - - $adjusted = Str::of('this/string/')->finish('/'); - - // this/string/ - - -#### `headline` {.collection-method} - -The `headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: - - use Illuminate\Support\Str; - - $headline = Str::of('taylor_otwell')->headline(); - - // Taylor Otwell - - $headline = Str::of('EmailNotificationSent')->headline(); - - // Email Notification Sent - - -#### `inlineMarkdown` {.collection-method} - -The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: - - use Illuminate\Support\Str; - - $html = Str::of('**Laravel**')->inlineMarkdown(); - - // Laravel - - -#### `is` {.collection-method} - -The `is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values - - use Illuminate\Support\Str; - - $matches = Str::of('foobar')->is('foo*'); - - // true - - $matches = Str::of('foobar')->is('baz*'); - - // false - - -#### `isAscii` {.collection-method} - -The `isAscii` method determines if a given string is an ASCII string: - - use Illuminate\Support\Str; - - $result = Str::of('Taylor')->isAscii(); - - // true - - $result = Str::of('ü')->isAscii(); - - // false - - -#### `isEmpty` {.collection-method} - -The `isEmpty` method determines if the given string is empty: - - use Illuminate\Support\Str; - - $result = Str::of(' ')->trim()->isEmpty(); - - // true - - $result = Str::of('Laravel')->trim()->isEmpty(); - - // false - - -#### `isNotEmpty` {.collection-method} - -The `isNotEmpty` method determines if the given string is not empty: - - - use Illuminate\Support\Str; - - $result = Str::of(' ')->trim()->isNotEmpty(); - - // false - - $result = Str::of('Laravel')->trim()->isNotEmpty(); - - // true - - -#### `isJson` {.collection-method} - -The `isJson` method determines if a given string is valid JSON: - - use Illuminate\Support\Str; - - $result = Str::of('[1,2,3]')->isJson(); - - // true - - $result = Str::of('{"first": "John", "last": "Doe"}')->isJson(); - - // true - - $result = Str::of('{first: "John", last: "Doe"}')->isJson(); - - // false - - -#### `isUlid` {.collection-method} - -The `isUlid` method determines if a given string is a ULID: - - use Illuminate\Support\Str; - - $result = Str::of('01gd6r360bp37zj17nxb55yv40')->isUlid(); - - // true - - $result = Str::of('Taylor')->isUlid(); - - // false - - -#### `isUrl` {.collection-method} - -The `isUrl` method determines if a given string is a URL: - - use Illuminate\Support\Str; - - $result = Str::of('http://example.com/')->isUrl(); - - // true - - $result = Str::of('Taylor')->isUrl(); - - // false - - -#### `isUuid` {.collection-method} - -The `isUuid` method determines if a given string is a UUID: - - use Illuminate\Support\Str; - - $result = Str::of('5ace9ab9-e9cf-4ec6-a19d-5881212a452c')->isUuid(); - - // true - - $result = Str::of('Taylor')->isUuid(); - - // false - - -#### `kebab` {.collection-method} - -The `kebab` method converts the given string to `kebab-case`: - - use Illuminate\Support\Str; - - $converted = Str::of('fooBar')->kebab(); - - // foo-bar - - -#### `lcfirst` {.collection-method} - -The `lcfirst` method returns the given string with the first character lowercased: - - use Illuminate\Support\Str; - - $string = Str::of('Foo Bar')->lcfirst(); - - // foo Bar - - - -#### `length` {.collection-method} - -The `length` method returns the length of the given string: - - use Illuminate\Support\Str; - - $length = Str::of('Laravel')->length(); - - // 7 - - -#### `limit` {.collection-method} - -The `limit` method truncates the given string to the specified length: - - use Illuminate\Support\Str; - - $truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20); - - // The quick brown fox... - -You may also pass a second argument to change the string that will be appended to the end of the truncated string: - - use Illuminate\Support\Str; - - $truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20, ' (...)'); - - // The quick brown fox (...) - - -#### `lower` {.collection-method} - -The `lower` method converts the given string to lowercase: - - use Illuminate\Support\Str; - - $result = Str::of('LARAVEL')->lower(); - - // 'laravel' - - -#### `ltrim` {.collection-method} - -The `ltrim` method trims the left side of the string: - - use Illuminate\Support\Str; - - $string = Str::of(' Laravel ')->ltrim(); - - // 'Laravel ' - - $string = Str::of('/Laravel/')->ltrim('/'); - - // 'Laravel/' - - -#### `markdown` {.collection-method} - -The `markdown` method converts GitHub flavored Markdown into HTML: - - use Illuminate\Support\Str; - - $html = Str::of('# Laravel')->markdown(); - - //

    Laravel

    - - $html = Str::of('# Taylor Otwell')->markdown([ - 'html_input' => 'strip', - ]); - - //

    Taylor Otwell

    - - -#### `mask` {.collection-method} - -The `mask` method masks a portion of a string with a repeated character, and may be used to obfuscate segments of strings such as email addresses and phone numbers: - - use Illuminate\Support\Str; - - $string = Str::of('taylor@example.com')->mask('*', 3); - - // tay*************** - -If needed, you may provide negative numbers as the third or fourth argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: - - $string = Str::of('taylor@example.com')->mask('*', -15, 3); - - // tay***@example.com - - $string = Str::of('taylor@example.com')->mask('*', 4, -4); - - // tayl**********.com - - -#### `match` {.collection-method} - -The `match` method will return the portion of a string that matches a given regular expression pattern: - - use Illuminate\Support\Str; - - $result = Str::of('foo bar')->match('/bar/'); - - // 'bar' - - $result = Str::of('foo bar')->match('/foo (.*)/'); - - // 'bar' - - -#### `matchAll` {.collection-method} - -The `matchAll` method will return a collection containing the portions of a string that match a given regular expression pattern: - - use Illuminate\Support\Str; - - $result = Str::of('bar foo bar')->matchAll('/bar/'); - - // collect(['bar', 'bar']) - -If you specify a matching group within the expression, Laravel will return a collection of that group's matches: - - use Illuminate\Support\Str; - - $result = Str::of('bar fun bar fly')->matchAll('/f(\w*)/'); - - // collect(['un', 'ly']); - -If no matches are found, an empty collection will be returned. - - -#### `isMatch` {.collection-method} - -The `isMatch` method will return `true` if the string matches a given regular expression: - - use Illuminate\Support\Str; - - $result = Str::of('foo bar')->isMatch('/foo (.*)/'); - - // true - - $result = Str::of('laravel')->isMatch('/foo (.*)/'); - - // false - - -#### `newLine` {.collection-method} - -The `newLine` method appends an "end of line" character to a string: - - use Illuminate\Support\Str; - - $padded = Str::of('Laravel')->newLine()->append('Framework'); - - // 'Laravel - // Framework' - - -#### `padBoth` {.collection-method} - -The `padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches the desired length: - - use Illuminate\Support\Str; - - $padded = Str::of('James')->padBoth(10, '_'); - - // '__James___' - - $padded = Str::of('James')->padBoth(10); - - // ' James ' - - -#### `padLeft` {.collection-method} - -The `padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches the desired length: - - use Illuminate\Support\Str; - - $padded = Str::of('James')->padLeft(10, '-='); - - // '-=-=-James' - - $padded = Str::of('James')->padLeft(10); - - // ' James' - - -#### `padRight` {.collection-method} - -The `padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches the desired length: - - use Illuminate\Support\Str; - - $padded = Str::of('James')->padRight(10, '-'); - - // 'James-----' - - $padded = Str::of('James')->padRight(10); - - // 'James ' - - -#### `pipe` {.collection-method} - -The `pipe` method allows you to transform the string by passing its current value to the given callable: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $hash = Str::of('Laravel')->pipe('md5')->prepend('Checksum: '); - - // 'Checksum: a5c95b86291ea299fcbe64458ed12702' - - $closure = Str::of('foo')->pipe(function (Stringable $str) { - return 'bar'; - }); - - // 'bar' - - -#### `plural` {.collection-method} - -The `plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - - use Illuminate\Support\Str; - - $plural = Str::of('car')->plural(); - - // cars - - $plural = Str::of('child')->plural(); - - // children - -You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: - - use Illuminate\Support\Str; - - $plural = Str::of('child')->plural(2); - - // children - - $plural = Str::of('child')->plural(1); - - // child - - -#### `prepend` {.collection-method} - -The `prepend` method prepends the given values onto the string: - - use Illuminate\Support\Str; - - $string = Str::of('Framework')->prepend('Laravel '); - - // Laravel Framework - - -#### `remove` {.collection-method} - -The `remove` method removes the given value or array of values from the string: - - use Illuminate\Support\Str; - - $string = Str::of('Arkansas is quite beautiful!')->remove('quite'); - - // Arkansas is beautiful! - -You may also pass `false` as a second parameter to ignore case when removing strings. - - -#### `repeat` {.collection-method} - -The `repeat` method repeats the given string: - -```php -use Illuminate\Support\Str; - -$repeated = Str::of('a')->repeat(5); - -// aaaaa -``` - - -#### `replace` {.collection-method} - -The `replace` method replaces a given string within the string: - - use Illuminate\Support\Str; - - $replaced = Str::of('Laravel 6.x')->replace('6.x', '7.x'); - - // Laravel 7.x - -The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: - - $replaced = Str::of('macOS 13.x')->replace( - 'macOS', 'iOS', caseSensitive: false - ); - - -#### `replaceArray` {.collection-method} - -The `replaceArray` method replaces a given value in the string sequentially using an array: - - use Illuminate\Support\Str; - - $string = 'The event will take place between ? and ?'; - - $replaced = Str::of($string)->replaceArray('?', ['8:30', '9:00']); - - // The event will take place between 8:30 and 9:00 - - -#### `replaceFirst` {.collection-method} - -The `replaceFirst` method replaces the first occurrence of a given value in a string: - - use Illuminate\Support\Str; - - $replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceFirst('the', 'a'); - - // a quick brown fox jumps over the lazy dog - - -#### `replaceLast` {.collection-method} - -The `replaceLast` method replaces the last occurrence of a given value in a string: - - use Illuminate\Support\Str; - - $replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceLast('the', 'a'); - - // the quick brown fox jumps over a lazy dog - - -#### `replaceMatches` {.collection-method} - -The `replaceMatches` method replaces all portions of a string matching a pattern with the given replacement string: - - use Illuminate\Support\Str; - - $replaced = Str::of('(+1) 501-555-1000')->replaceMatches('/[^A-Za-z0-9]++/', '') - - // '15015551000' - -The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given pattern, allowing you to perform the replacement logic within the closure and return the replaced value: - - use Illuminate\Support\Str; - - $replaced = Str::of('123')->replaceMatches('/\d/', function (array $matches) { - return '['.$matches[0].']'; - }); - - // '[1][2][3]' - - -#### `replaceStart` {.collection-method} - -The `replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: - - use Illuminate\Support\Str; - - $replaced = Str::of('Hello World')->replaceStart('Hello', 'Laravel'); - - // Laravel World - - $replaced = Str::of('Hello World')->replaceStart('World', 'Laravel'); - - // Hello World - - -#### `replaceEnd` {.collection-method} - -The `replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: - - use Illuminate\Support\Str; - - $replaced = Str::of('Hello World')->replaceEnd('World', 'Laravel'); - - // Hello Laravel - - $replaced = Str::of('Hello World')->replaceEnd('Hello', 'Laravel'); - - // Hello World - - -#### `rtrim` {.collection-method} - -The `rtrim` method trims the right side of the given string: - - use Illuminate\Support\Str; - - $string = Str::of(' Laravel ')->rtrim(); - - // ' Laravel' - - $string = Str::of('/Laravel/')->rtrim('/'); - - // '/Laravel' - - -#### `scan` {.collection-method} - -The `scan` method parses input from a string into a collection according to a format supported by the [`sscanf` PHP function](https://www.php.net/manual/en/function.sscanf.php): - - use Illuminate\Support\Str; - - $collection = Str::of('filename.jpg')->scan('%[^.].%s'); - - // collect(['filename', 'jpg']) - - -#### `singular` {.collection-method} - -The `singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - - use Illuminate\Support\Str; - - $singular = Str::of('cars')->singular(); - - // car - - $singular = Str::of('children')->singular(); - - // child - - -#### `slug` {.collection-method} - -The `slug` method generates a URL friendly "slug" from the given string: - - use Illuminate\Support\Str; - - $slug = Str::of('Laravel Framework')->slug('-'); - - // laravel-framework - - -#### `snake` {.collection-method} - -The `snake` method converts the given string to `snake_case`: - - use Illuminate\Support\Str; - - $converted = Str::of('fooBar')->snake(); - - // foo_bar - - -#### `split` {.collection-method} - -The `split` method splits a string into a collection using a regular expression: - - use Illuminate\Support\Str; - - $segments = Str::of('one, two, three')->split('/[\s,]+/'); - - // collect(["one", "two", "three"]) - - -#### `squish` {.collection-method} - -The `squish` method removes all extraneous white space from a string, including extraneous white space between words: - - use Illuminate\Support\Str; - - $string = Str::of(' laravel framework ')->squish(); - - // laravel framework - - -#### `start` {.collection-method} - -The `start` method adds a single instance of the given value to a string if it does not already start with that value: - - use Illuminate\Support\Str; - - $adjusted = Str::of('this/string')->start('/'); - - // /this/string - - $adjusted = Str::of('/this/string')->start('/'); - - // /this/string - - -#### `startsWith` {.collection-method} - -The `startsWith` method determines if the given string begins with the given value: - - use Illuminate\Support\Str; - - $result = Str::of('This is my name')->startsWith('This'); - - // true - - -#### `studly` {.collection-method} - -The `studly` method converts the given string to `StudlyCase`: - - use Illuminate\Support\Str; - - $converted = Str::of('foo_bar')->studly(); - - // FooBar - - -#### `substr` {.collection-method} - -The `substr` method returns the portion of the string specified by the given start and length parameters: - - use Illuminate\Support\Str; - - $string = Str::of('Laravel Framework')->substr(8); - - // Framework - - $string = Str::of('Laravel Framework')->substr(8, 5); - - // Frame - - -#### `substrReplace` {.collection-method} - -The `substrReplace` method replaces text within a portion of a string, starting at the position specified by the second argument and replacing the number of characters specified by the third argument. Passing `0` to the method's third argument will insert the string at the specified position without replacing any of the existing characters in the string: - - use Illuminate\Support\Str; - - $string = Str::of('1300')->substrReplace(':', 2); - - // 13: - - $string = Str::of('The Framework')->substrReplace(' Laravel', 3, 0); - - // The Laravel Framework - - -#### `swap` {.collection-method} - -The `swap` method replaces multiple values in the string using PHP's `strtr` function: - - use Illuminate\Support\Str; - - $string = Str::of('Tacos are great!') - ->swap([ - 'Tacos' => 'Burritos', - 'great' => 'fantastic', - ]); - - // Burritos are fantastic! - - -#### `tap` {.collection-method} - -The `tap` method passes the string to the given closure, allowing you to examine and interact with the string while not affecting the string itself. The original string is returned by the `tap` method regardless of what is returned by the closure: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('Laravel') - ->append(' Framework') - ->tap(function (Stringable $string) { - dump('String after append: '.$string); - }) - ->upper(); - - // LARAVEL FRAMEWORK - - -#### `test` {.collection-method} - -The `test` method determines if a string matches the given regular expression pattern: - - use Illuminate\Support\Str; - - $result = Str::of('Laravel Framework')->test('/Laravel/'); - - // true - - -#### `title` {.collection-method} - -The `title` method converts the given string to `Title Case`: - - use Illuminate\Support\Str; - - $converted = Str::of('a nice title uses the correct case')->title(); - - // A Nice Title Uses The Correct Case - - -#### `trim` {.collection-method} - -The `trim` method trims the given string: - - use Illuminate\Support\Str; - - $string = Str::of(' Laravel ')->trim(); - - // 'Laravel' - - $string = Str::of('/Laravel/')->trim('/'); - - // 'Laravel' - - -#### `ucfirst` {.collection-method} - -The `ucfirst` method returns the given string with the first character capitalized: - - use Illuminate\Support\Str; - - $string = Str::of('foo bar')->ucfirst(); - - // Foo bar - - -#### `ucsplit` {.collection-method} - -The `ucsplit` method splits the given string into a collection by uppercase characters: - - use Illuminate\Support\Str; - - $string = Str::of('Foo Bar')->ucsplit(); - - // collect(['Foo', 'Bar']) - - -#### `upper` {.collection-method} - -The `upper` method converts the given string to uppercase: - - use Illuminate\Support\Str; - - $adjusted = Str::of('laravel')->upper(); - - // LARAVEL - - -#### `when` {.collection-method} - -The `when` method invokes the given closure if a given condition is `true`. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('Taylor') - ->when(true, function (Stringable $string) { - return $string->append(' Otwell'); - }); - - // 'Taylor Otwell' - -If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the condition parameter evaluates to `false`. - - -#### `whenContains` {.collection-method} - -The `whenContains` method invokes the given closure if the string contains the given value. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('tony stark') - ->whenContains('tony', function (Stringable $string) { - return $string->title(); - }); - - // 'Tony Stark' - -If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the string does not contain the given value. - -You may also pass an array of values to determine if the given string contains any of the values in the array: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('tony stark') - ->whenContains(['tony', 'hulk'], function (Stringable $string) { - return $string->title(); - }); - - // Tony Stark - - -#### `whenContainsAll` {.collection-method} - -The `whenContainsAll` method invokes the given closure if the string contains all of the given sub-strings. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('tony stark') - ->whenContainsAll(['tony', 'stark'], function (Stringable $string) { - return $string->title(); - }); - - // 'Tony Stark' - -If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the condition parameter evaluates to `false`. - - -#### `whenEmpty` {.collection-method} - -The `whenEmpty` method invokes the given closure if the string is empty. If the closure returns a value, that value will also be returned by the `whenEmpty` method. If the closure does not return a value, the fluent string instance will be returned: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of(' ')->whenEmpty(function (Stringable $string) { - return $string->trim()->prepend('Laravel'); - }); - - // 'Laravel' - - -#### `whenNotEmpty` {.collection-method} - -The `whenNotEmpty` method invokes the given closure if the string is not empty. If the closure returns a value, that value will also be returned by the `whenNotEmpty` method. If the closure does not return a value, the fluent string instance will be returned: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('Framework')->whenNotEmpty(function (Stringable $string) { - return $string->prepend('Laravel '); - }); - - // 'Laravel Framework' - - -#### `whenStartsWith` {.collection-method} - -The `whenStartsWith` method invokes the given closure if the string starts with the given sub-string. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('disney world')->whenStartsWith('disney', function (Stringable $string) { - return $string->title(); - }); - - // 'Disney World' - - -#### `whenEndsWith` {.collection-method} - -The `whenEndsWith` method invokes the given closure if the string ends with the given sub-string. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('disney world')->whenEndsWith('world', function (Stringable $string) { - return $string->title(); - }); - - // 'Disney World' - - -#### `whenExactly` {.collection-method} - -The `whenExactly` method invokes the given closure if the string exactly matches the given string. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('laravel')->whenExactly('laravel', function (Stringable $string) { - return $string->title(); - }); - - // 'Laravel' - - -#### `whenNotExactly` {.collection-method} - -The `whenNotExactly` method invokes the given closure if the string does not exactly match the given string. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('framework')->whenNotExactly('laravel', function (Stringable $string) { - return $string->title(); - }); - - // 'Framework' - - -#### `whenIs` {.collection-method} - -The `whenIs` method invokes the given closure if the string matches a given pattern. Asterisks may be used as wildcard values. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('foo/bar')->whenIs('foo/*', function (Stringable $string) { - return $string->append('/baz'); - }); - - // 'foo/bar/baz' - - -#### `whenIsAscii` {.collection-method} - -The `whenIsAscii` method invokes the given closure if the string is 7 bit ASCII. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('laravel')->whenIsAscii(function (Stringable $string) { - return $string->title(); - }); - - // 'Laravel' - - -#### `whenIsUlid` {.collection-method} - -The `whenIsUlid` method invokes the given closure if the string is a valid ULID. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - - $string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function (Stringable $string) { - return $string->substr(0, 8); - }); - - // '01gd6r36' - - -#### `whenIsUuid` {.collection-method} - -The `whenIsUuid` method invokes the given closure if the string is a valid UUID. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function (Stringable $string) { - return $string->substr(0, 8); - }); - - // 'a0a2a2d2' - - -#### `whenTest` {.collection-method} - -The `whenTest` method invokes the given closure if the string matches the given regular expression. The closure will receive the fluent string instance: - - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; - - $string = Str::of('laravel framework')->whenTest('/laravel/', function (Stringable $string) { - return $string->title(); - }); - - // 'Laravel Framework' - - -#### `wordCount` {.collection-method} - -The `wordCount` method returns the number of words that a string contains: - -```php -use Illuminate\Support\Str; - -Str::of('Hello, world!')->wordCount(); // 2 -``` - - -#### `words` {.collection-method} - -The `words` method limits the number of words in a string. If necessary, you may specify an additional string that will be appended to the truncated string: - - use Illuminate\Support\Str; - - $string = Str::of('Perfectly balanced, as all things should be.')->words(3, ' >>>'); - - // Perfectly balanced, as >>> - ## URLs diff --git a/strings.md b/strings.md new file mode 100644 index 00000000000..c2574503112 --- /dev/null +++ b/strings.md @@ -0,0 +1,2541 @@ +# Strings + +- [Introduction](#introduction) +- [Available Methods](#available-methods) + + +## Introduction + +Laravel includes a variety of functions for manipulating string values. Many of these functions are used by the framework itself; however, you are free to use them in your own applications if you find them convenient. + + +## Available Methods + + + + +### Strings + +
    + +[\__](#method-__) +[class_basename](#method-class-basename) +[e](#method-e) +[preg_replace_array](#method-preg-replace-array) +[Str::after](#method-str-after) +[Str::afterLast](#method-str-after-last) +[Str::ascii](#method-str-ascii) +[Str::before](#method-str-before) +[Str::beforeLast](#method-str-before-last) +[Str::between](#method-str-between) +[Str::betweenFirst](#method-str-between-first) +[Str::camel](#method-camel-case) +[Str::contains](#method-str-contains) +[Str::containsAll](#method-str-contains-all) +[Str::endsWith](#method-ends-with) +[Str::excerpt](#method-excerpt) +[Str::finish](#method-str-finish) +[Str::headline](#method-str-headline) +[Str::inlineMarkdown](#method-str-inline-markdown) +[Str::is](#method-str-is) +[Str::isAscii](#method-str-is-ascii) +[Str::isJson](#method-str-is-json) +[Str::isUlid](#method-str-is-ulid) +[Str::isUrl](#method-str-is-url) +[Str::isUuid](#method-str-is-uuid) +[Str::kebab](#method-kebab-case) +[Str::lcfirst](#method-str-lcfirst) +[Str::length](#method-str-length) +[Str::limit](#method-str-limit) +[Str::lower](#method-str-lower) +[Str::markdown](#method-str-markdown) +[Str::mask](#method-str-mask) +[Str::orderedUuid](#method-str-ordered-uuid) +[Str::padBoth](#method-str-padboth) +[Str::padLeft](#method-str-padleft) +[Str::padRight](#method-str-padright) +[Str::password](#method-str-password) +[Str::plural](#method-str-plural) +[Str::pluralStudly](#method-str-plural-studly) +[Str::random](#method-str-random) +[Str::remove](#method-str-remove) +[Str::repeat](#method-str-repeat) +[Str::replace](#method-str-replace) +[Str::replaceArray](#method-str-replace-array) +[Str::replaceFirst](#method-str-replace-first) +[Str::replaceLast](#method-str-replace-last) +[Str::replaceStart](#method-str-replace-start) +[Str::replaceEnd](#method-str-replace-end) +[Str::reverse](#method-str-reverse) +[Str::singular](#method-str-singular) +[Str::slug](#method-str-slug) +[Str::snake](#method-snake-case) +[Str::squish](#method-str-squish) +[Str::start](#method-str-start) +[Str::startsWith](#method-starts-with) +[Str::studly](#method-studly-case) +[Str::substr](#method-str-substr) +[Str::substrCount](#method-str-substrcount) +[Str::substrReplace](#method-str-substrreplace) +[Str::swap](#method-str-swap) +[Str::title](#method-title-case) +[Str::toHtmlString](#method-str-to-html-string) +[Str::ucfirst](#method-str-ucfirst) +[Str::ucsplit](#method-str-ucsplit) +[Str::upper](#method-str-upper) +[Str::ulid](#method-str-ulid) +[Str::uuid](#method-str-uuid) +[Str::wordCount](#method-str-word-count) +[Str::wordWrap](#method-str-word-wrap) +[Str::words](#method-str-words) +[Str::wrap](#method-str-wrap) +[str](#method-str) +[trans](#method-trans) +[trans_choice](#method-trans-choice) + +
    + + +### Fluent Strings + +
    + +[after](#method-fluent-str-after) +[afterLast](#method-fluent-str-after-last) +[append](#method-fluent-str-append) +[ascii](#method-fluent-str-ascii) +[basename](#method-fluent-str-basename) +[before](#method-fluent-str-before) +[beforeLast](#method-fluent-str-before-last) +[between](#method-fluent-str-between) +[betweenFirst](#method-fluent-str-between-first) +[camel](#method-fluent-str-camel) +[classBasename](#method-fluent-str-class-basename) +[contains](#method-fluent-str-contains) +[containsAll](#method-fluent-str-contains-all) +[dirname](#method-fluent-str-dirname) +[endsWith](#method-fluent-str-ends-with) +[excerpt](#method-fluent-str-excerpt) +[exactly](#method-fluent-str-exactly) +[explode](#method-fluent-str-explode) +[finish](#method-fluent-str-finish) +[headline](#method-fluent-str-headline) +[inlineMarkdown](#method-fluent-str-inline-markdown) +[is](#method-fluent-str-is) +[isAscii](#method-fluent-str-is-ascii) +[isEmpty](#method-fluent-str-is-empty) +[isNotEmpty](#method-fluent-str-is-not-empty) +[isJson](#method-fluent-str-is-json) +[isUlid](#method-fluent-str-is-ulid) +[isUrl](#method-fluent-str-is-url) +[isUuid](#method-fluent-str-is-uuid) +[kebab](#method-fluent-str-kebab) +[lcfirst](#method-fluent-str-lcfirst) +[length](#method-fluent-str-length) +[limit](#method-fluent-str-limit) +[lower](#method-fluent-str-lower) +[ltrim](#method-fluent-str-ltrim) +[markdown](#method-fluent-str-markdown) +[mask](#method-fluent-str-mask) +[match](#method-fluent-str-match) +[matchAll](#method-fluent-str-match-all) +[isMatch](#method-fluent-str-is-match) +[newLine](#method-fluent-str-new-line) +[padBoth](#method-fluent-str-padboth) +[padLeft](#method-fluent-str-padleft) +[padRight](#method-fluent-str-padright) +[pipe](#method-fluent-str-pipe) +[plural](#method-fluent-str-plural) +[prepend](#method-fluent-str-prepend) +[remove](#method-fluent-str-remove) +[repeat](#method-fluent-str-repeat) +[replace](#method-fluent-str-replace) +[replaceArray](#method-fluent-str-replace-array) +[replaceFirst](#method-fluent-str-replace-first) +[replaceLast](#method-fluent-str-replace-last) +[replaceMatches](#method-fluent-str-replace-matches) +[replaceStart](#method-fluent-str-replace-start) +[replaceEnd](#method-fluent-str-replace-end) +[rtrim](#method-fluent-str-rtrim) +[scan](#method-fluent-str-scan) +[singular](#method-fluent-str-singular) +[slug](#method-fluent-str-slug) +[snake](#method-fluent-str-snake) +[split](#method-fluent-str-split) +[squish](#method-fluent-str-squish) +[start](#method-fluent-str-start) +[startsWith](#method-fluent-str-starts-with) +[studly](#method-fluent-str-studly) +[substr](#method-fluent-str-substr) +[substrReplace](#method-fluent-str-substrreplace) +[swap](#method-fluent-str-swap) +[tap](#method-fluent-str-tap) +[test](#method-fluent-str-test) +[title](#method-fluent-str-title) +[trim](#method-fluent-str-trim) +[ucfirst](#method-fluent-str-ucfirst) +[ucsplit](#method-fluent-str-ucsplit) +[upper](#method-fluent-str-upper) +[when](#method-fluent-str-when) +[whenContains](#method-fluent-str-when-contains) +[whenContainsAll](#method-fluent-str-when-contains-all) +[whenEmpty](#method-fluent-str-when-empty) +[whenNotEmpty](#method-fluent-str-when-not-empty) +[whenStartsWith](#method-fluent-str-when-starts-with) +[whenEndsWith](#method-fluent-str-when-ends-with) +[whenExactly](#method-fluent-str-when-exactly) +[whenNotExactly](#method-fluent-str-when-not-exactly) +[whenIs](#method-fluent-str-when-is) +[whenIsAscii](#method-fluent-str-when-is-ascii) +[whenIsUlid](#method-fluent-str-when-is-ulid) +[whenIsUuid](#method-fluent-str-when-is-uuid) +[whenTest](#method-fluent-str-when-test) +[wordCount](#method-fluent-str-word-count) +[words](#method-fluent-str-words) + +
    + + +## Strings + + +#### `__()` {.collection-method} + +The `__` function translates the given translation string or translation key using your [language files](/docs/{{version}}/localization): + + echo __('Welcome to our application'); + + echo __('messages.welcome'); + +If the specified translation string or key does not exist, the `__` function will return the given value. So, using the example above, the `__` function would return `messages.welcome` if that translation key does not exist. + + +#### `class_basename()` {.collection-method} + +The `class_basename` function returns the class name of the given class with the class's namespace removed: + + $class = class_basename('Foo\Bar\Baz'); + + // Baz + + +#### `e()` {.collection-method} + +The `e` function runs PHP's `htmlspecialchars` function with the `double_encode` option set to `true` by default: + + echo e('foo'); + + // <html>foo</html> + + +#### `preg_replace_array()` {.collection-method} + +The `preg_replace_array` function replaces a given pattern in the string sequentially using an array: + + $string = 'The event will take place between :start and :end'; + + $replaced = preg_replace_array('/:[a-z_]+/', ['8:30', '9:00'], $string); + + // The event will take place between 8:30 and 9:00 + + +#### `Str::after()` {.collection-method} + +The `Str::after` method returns everything after the given value in a string. The entire string will be returned if the value does not exist within the string: + + use Illuminate\Support\Str; + + $slice = Str::after('This is my name', 'This is'); + + // ' my name' + + +#### `Str::afterLast()` {.collection-method} + +The `Str::afterLast` method returns everything after the last occurrence of the given value in a string. The entire string will be returned if the value does not exist within the string: + + use Illuminate\Support\Str; + + $slice = Str::afterLast('App\Http\Controllers\Controller', '\\'); + + // 'Controller' + + +#### `Str::ascii()` {.collection-method} + +The `Str::ascii` method will attempt to transliterate the string into an ASCII value: + + use Illuminate\Support\Str; + + $slice = Str::ascii('û'); + + // 'u' + + +#### `Str::before()` {.collection-method} + +The `Str::before` method returns everything before the given value in a string: + + use Illuminate\Support\Str; + + $slice = Str::before('This is my name', 'my name'); + + // 'This is ' + + +#### `Str::beforeLast()` {.collection-method} + +The `Str::beforeLast` method returns everything before the last occurrence of the given value in a string: + + use Illuminate\Support\Str; + + $slice = Str::beforeLast('This is my name', 'is'); + + // 'This ' + + +#### `Str::between()` {.collection-method} + +The `Str::between` method returns the portion of a string between two values: + + use Illuminate\Support\Str; + + $slice = Str::between('This is my name', 'This', 'name'); + + // ' is my ' + + +#### `Str::betweenFirst()` {.collection-method} + +The `Str::betweenFirst` method returns the smallest possible portion of a string between two values: + + use Illuminate\Support\Str; + + $slice = Str::betweenFirst('[a] bc [d]', '[', ']'); + + // 'a' + + +#### `Str::camel()` {.collection-method} + +The `Str::camel` method converts the given string to `camelCase`: + + use Illuminate\Support\Str; + + $converted = Str::camel('foo_bar'); + + // fooBar + + +#### `Str::contains()` {.collection-method} + +The `Str::contains` method determines if the given string contains the given value. This method is case sensitive: + + use Illuminate\Support\Str; + + $contains = Str::contains('This is my name', 'my'); + + // true + +You may also pass an array of values to determine if the given string contains any of the values in the array: + + use Illuminate\Support\Str; + + $contains = Str::contains('This is my name', ['my', 'foo']); + + // true + + +#### `Str::containsAll()` {.collection-method} + +The `Str::containsAll` method determines if the given string contains all of the values in a given array: + + use Illuminate\Support\Str; + + $containsAll = Str::containsAll('This is my name', ['my', 'name']); + + // true + + +#### `Str::endsWith()` {.collection-method} + +The `Str::endsWith` method determines if the given string ends with the given value: + + use Illuminate\Support\Str; + + $result = Str::endsWith('This is my name', 'name'); + + // true + + +You may also pass an array of values to determine if the given string ends with any of the values in the array: + + use Illuminate\Support\Str; + + $result = Str::endsWith('This is my name', ['name', 'foo']); + + // true + + $result = Str::endsWith('This is my name', ['this', 'foo']); + + // false + + +#### `Str::excerpt()` {.collection-method} + +The `Str::excerpt` method extracts an excerpt from a given string that matches the first instance of a phrase within that string: + + use Illuminate\Support\Str; + + $excerpt = Str::excerpt('This is my name', 'my', [ + 'radius' => 3 + ]); + + // '...is my na...' + +The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. + +In addition, you may use the `omission` option to define the string that will be prepended and appended to the truncated string: + + use Illuminate\Support\Str; + + $excerpt = Str::excerpt('This is my name', 'name', [ + 'radius' => 3, + 'omission' => '(...) ' + ]); + + // '(...) my name' + + +#### `Str::finish()` {.collection-method} + +The `Str::finish` method adds a single instance of the given value to a string if it does not already end with that value: + + use Illuminate\Support\Str; + + $adjusted = Str::finish('this/string', '/'); + + // this/string/ + + $adjusted = Str::finish('this/string/', '/'); + + // this/string/ + + +#### `Str::headline()` {.collection-method} + +The `Str::headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: + + use Illuminate\Support\Str; + + $headline = Str::headline('steve_jobs'); + + // Steve Jobs + + $headline = Str::headline('EmailNotificationSent'); + + // Email Notification Sent + + +#### `Str::inlineMarkdown()` {.collection-method} + +The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: + + use Illuminate\Support\Str; + + $html = Str::inlineMarkdown('**Laravel**'); + + // Laravel + + +#### `Str::is()` {.collection-method} + +The `Str::is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values: + + use Illuminate\Support\Str; + + $matches = Str::is('foo*', 'foobar'); + + // true + + $matches = Str::is('baz*', 'foobar'); + + // false + + +#### `Str::isAscii()` {.collection-method} + +The `Str::isAscii` method determines if a given string is 7 bit ASCII: + + use Illuminate\Support\Str; + + $isAscii = Str::isAscii('Taylor'); + + // true + + $isAscii = Str::isAscii('ü'); + + // false + + +#### `Str::isJson()` {.collection-method} + +The `Str::isJson` method determines if the given string is valid JSON: + + use Illuminate\Support\Str; + + $result = Str::isJson('[1,2,3]'); + + // true + + $result = Str::isJson('{"first": "John", "last": "Doe"}'); + + // true + + $result = Str::isJson('{first: "John", last: "Doe"}'); + + // false + + +#### `Str::isUrl()` {.collection-method} + +The `Str::isUrl` method determines if the given string is a valid URL: + + use Illuminate\Support\Str; + + $isUrl = Str::isUrl('http://example.com/'); + + // true + + $isUrl = Str::isUrl('laravel'); + + // false + + +#### `Str::isUlid()` {.collection-method} + +The `Str::isUlid` method determines if the given string is a valid ULID: + + use Illuminate\Support\Str; + + $isUlid = Str::isUlid('01gd6r360bp37zj17nxb55yv40'); + + // true + + $isUlid = Str::isUlid('laravel'); + + // false + + +#### `Str::isUuid()` {.collection-method} + +The `Str::isUuid` method determines if the given string is a valid UUID: + + use Illuminate\Support\Str; + + $isUuid = Str::isUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de'); + + // true + + $isUuid = Str::isUuid('laravel'); + + // false + + +#### `Str::kebab()` {.collection-method} + +The `Str::kebab` method converts the given string to `kebab-case`: + + use Illuminate\Support\Str; + + $converted = Str::kebab('fooBar'); + + // foo-bar + + +#### `Str::lcfirst()` {.collection-method} + +The `Str::lcfirst` method returns the given string with the first character lowercased: + + use Illuminate\Support\Str; + + $string = Str::lcfirst('Foo Bar'); + + // foo Bar + + +#### `Str::length()` {.collection-method} + +The `Str::length` method returns the length of the given string: + + use Illuminate\Support\Str; + + $length = Str::length('Laravel'); + + // 7 + + +#### `Str::limit()` {.collection-method} + +The `Str::limit` method truncates the given string to the specified length: + + use Illuminate\Support\Str; + + $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20); + + // The quick brown fox... + +You may pass a third argument to the method to change the string that will be appended to the end of the truncated string: + + use Illuminate\Support\Str; + + $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20, ' (...)'); + + // The quick brown fox (...) + + +#### `Str::lower()` {.collection-method} + +The `Str::lower` method converts the given string to lowercase: + + use Illuminate\Support\Str; + + $converted = Str::lower('LARAVEL'); + + // laravel + + +#### `Str::markdown()` {.collection-method} + +The `Str::markdown` method converts GitHub flavored Markdown into HTML using [CommonMark](https://commonmark.thephpleague.com/): + + use Illuminate\Support\Str; + + $html = Str::markdown('# Laravel'); + + //

    Laravel

    + + $html = Str::markdown('# Taylor Otwell', [ + 'html_input' => 'strip', + ]); + + //

    Taylor Otwell

    + + +#### `Str::mask()` {.collection-method} + +The `Str::mask` method masks a portion of a string with a repeated character, and may be used to obfuscate segments of strings such as email addresses and phone numbers: + + use Illuminate\Support\Str; + + $string = Str::mask('taylor@example.com', '*', 3); + + // tay*************** + +If needed, you provide a negative number as the third argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: + + $string = Str::mask('taylor@example.com', '*', -15, 3); + + // tay***@example.com + + +#### `Str::orderedUuid()` {.collection-method} + +The `Str::orderedUuid` method generates a "timestamp first" UUID that may be efficiently stored in an indexed database column. Each UUID that is generated using this method will be sorted after UUIDs previously generated using the method: + + use Illuminate\Support\Str; + + return (string) Str::orderedUuid(); + + +#### `Str::padBoth()` {.collection-method} + +The `Str::padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches a desired length: + + use Illuminate\Support\Str; + + $padded = Str::padBoth('James', 10, '_'); + + // '__James___' + + $padded = Str::padBoth('James', 10); + + // ' James ' + + +#### `Str::padLeft()` {.collection-method} + +The `Str::padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches a desired length: + + use Illuminate\Support\Str; + + $padded = Str::padLeft('James', 10, '-='); + + // '-=-=-James' + + $padded = Str::padLeft('James', 10); + + // ' James' + + +#### `Str::padRight()` {.collection-method} + +The `Str::padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches a desired length: + + use Illuminate\Support\Str; + + $padded = Str::padRight('James', 10, '-'); + + // 'James-----' + + $padded = Str::padRight('James', 10); + + // 'James ' + + +#### `Str::password()` {.collection-method} + +The `Str::password` method may be used to generate a secure, random password of a given length. The password will consist of a combination of letters, numbers, symbols, and spaces. By default, passwords are 32 characters long: + + use Illuminate\Support\Str; + + $password = Str::password(); + + // 'EbJo2vE-AS:U,$%_gkrV4n,q~1xy/-_4' + + $password = Str::password(12); + + // 'qwuar>#V|i]N' + + +#### `Str::plural()` {.collection-method} + +The `Str::plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): + + use Illuminate\Support\Str; + + $plural = Str::plural('car'); + + // cars + + $plural = Str::plural('child'); + + // children + +You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: + + use Illuminate\Support\Str; + + $plural = Str::plural('child', 2); + + // children + + $singular = Str::plural('child', 1); + + // child + + +#### `Str::pluralStudly()` {.collection-method} + +The `Str::pluralStudly` method converts a singular word string formatted in studly caps case to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): + + use Illuminate\Support\Str; + + $plural = Str::pluralStudly('VerifiedHuman'); + + // VerifiedHumans + + $plural = Str::pluralStudly('UserFeedback'); + + // UserFeedback + +You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: + + use Illuminate\Support\Str; + + $plural = Str::pluralStudly('VerifiedHuman', 2); + + // VerifiedHumans + + $singular = Str::pluralStudly('VerifiedHuman', 1); + + // VerifiedHuman + + +#### `Str::random()` {.collection-method} + +The `Str::random` method generates a random string of the specified length. This function uses PHP's `random_bytes` function: + + use Illuminate\Support\Str; + + $random = Str::random(40); + + +#### `Str::remove()` {.collection-method} + +The `Str::remove` method removes the given value or array of values from the string: + + use Illuminate\Support\Str; + + $string = 'Peter Piper picked a peck of pickled peppers.'; + + $removed = Str::remove('e', $string); + + // Ptr Pipr pickd a pck of pickld ppprs. + +You may also pass `false` as a third argument to the `remove` method to ignore case when removing strings. + + +#### `Str::repeat()` {.collection-method} + +The `Str::repeat` method repeats the given string: + +```php +use Illuminate\Support\Str; + +$string = 'a'; + +$repeat = Str::repeat($string, 5); + +// aaaaa +``` + + +#### `Str::replace()` {.collection-method} + +The `Str::replace` method replaces a given string within the string: + + use Illuminate\Support\Str; + + $string = 'Laravel 8.x'; + + $replaced = Str::replace('8.x', '9.x', $string); + + // Laravel 9.x + +The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: + + Str::replace('Framework', 'Laravel', caseSensitive: false); + + +#### `Str::replaceArray()` {.collection-method} + +The `Str::replaceArray` method replaces a given value in the string sequentially using an array: + + use Illuminate\Support\Str; + + $string = 'The event will take place between ? and ?'; + + $replaced = Str::replaceArray('?', ['8:30', '9:00'], $string); + + // The event will take place between 8:30 and 9:00 + + +#### `Str::replaceFirst()` {.collection-method} + +The `Str::replaceFirst` method replaces the first occurrence of a given value in a string: + + use Illuminate\Support\Str; + + $replaced = Str::replaceFirst('the', 'a', 'the quick brown fox jumps over the lazy dog'); + + // a quick brown fox jumps over the lazy dog + + +#### `Str::replaceLast()` {.collection-method} + +The `Str::replaceLast` method replaces the last occurrence of a given value in a string: + + use Illuminate\Support\Str; + + $replaced = Str::replaceLast('the', 'a', 'the quick brown fox jumps over the lazy dog'); + + // the quick brown fox jumps over a lazy dog + + + +#### `Str::replaceStart()` {.collection-method} + +The `Str::replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: + + use Illuminate\Support\Str; + + $replaced = Str::replaceStart('Hello', 'Laravel', 'Hello World'); + + // Laravel World + + $replaced = Str::replaceStart('World', 'Laravel', 'Hello World'); + + // Hello World + + +#### `Str::replaceEnd()` {.collection-method} + +The `Str::replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: + + use Illuminate\Support\Str; + + $replaced = Str::replaceEnd('World', 'Laravel', 'Hello World'); + + // Hello Laravel + + $replaced = Str::replaceEnd('Hello', 'Laravel', 'Hello World'); + + // Hello World + + +#### `Str::reverse()` {.collection-method} + +The `Str::reverse` method reverses the given string: + + use Illuminate\Support\Str; + + $reversed = Str::reverse('Hello World'); + + // dlroW olleH + + +#### `Str::singular()` {.collection-method} + +The `Str::singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): + + use Illuminate\Support\Str; + + $singular = Str::singular('cars'); + + // car + + $singular = Str::singular('children'); + + // child + + +#### `Str::slug()` {.collection-method} + +The `Str::slug` method generates a URL friendly "slug" from the given string: + + use Illuminate\Support\Str; + + $slug = Str::slug('Laravel 5 Framework', '-'); + + // laravel-5-framework + + +#### `Str::snake()` {.collection-method} + +The `Str::snake` method converts the given string to `snake_case`: + + use Illuminate\Support\Str; + + $converted = Str::snake('fooBar'); + + // foo_bar + + $converted = Str::snake('fooBar', '-'); + + // foo-bar + + +#### `Str::squish()` {.collection-method} + +The `Str::squish` method removes all extraneous white space from a string, including extraneous white space between words: + + use Illuminate\Support\Str; + + $string = Str::squish(' laravel framework '); + + // laravel framework + + +#### `Str::start()` {.collection-method} + +The `Str::start` method adds a single instance of the given value to a string if it does not already start with that value: + + use Illuminate\Support\Str; + + $adjusted = Str::start('this/string', '/'); + + // /this/string + + $adjusted = Str::start('/this/string', '/'); + + // /this/string + + +#### `Str::startsWith()` {.collection-method} + +The `Str::startsWith` method determines if the given string begins with the given value: + + use Illuminate\Support\Str; + + $result = Str::startsWith('This is my name', 'This'); + + // true + +If an array of possible values is passed, the `startsWith` method will return `true` if the string begins with any of the given values: + + $result = Str::startsWith('This is my name', ['This', 'That', 'There']); + + // true + + +#### `Str::studly()` {.collection-method} + +The `Str::studly` method converts the given string to `StudlyCase`: + + use Illuminate\Support\Str; + + $converted = Str::studly('foo_bar'); + + // FooBar + + +#### `Str::substr()` {.collection-method} + +The `Str::substr` method returns the portion of string specified by the start and length parameters: + + use Illuminate\Support\Str; + + $converted = Str::substr('The Laravel Framework', 4, 7); + + // Laravel + + +#### `Str::substrCount()` {.collection-method} + +The `Str::substrCount` method returns the number of occurrences of a given value in the given string: + + use Illuminate\Support\Str; + + $count = Str::substrCount('If you like ice cream, you will like snow cones.', 'like'); + + // 2 + + +#### `Str::substrReplace()` {.collection-method} + +The `Str::substrReplace` method replaces text within a portion of a string, starting at the position specified by the third argument and replacing the number of characters specified by the fourth argument. Passing `0` to the method's fourth argument will insert the string at the specified position without replacing any of the existing characters in the string: + + use Illuminate\Support\Str; + + $result = Str::substrReplace('1300', ':', 2); + // 13: + + $result = Str::substrReplace('1300', ':', 2, 0); + // 13:00 + + +#### `Str::swap()` {.collection-method} + +The `Str::swap` method replaces multiple values in the given string using PHP's `strtr` function: + + use Illuminate\Support\Str; + + $string = Str::swap([ + 'Tacos' => 'Burritos', + 'great' => 'fantastic', + ], 'Tacos are great!'); + + // Burritos are fantastic! + + +#### `Str::title()` {.collection-method} + +The `Str::title` method converts the given string to `Title Case`: + + use Illuminate\Support\Str; + + $converted = Str::title('a nice title uses the correct case'); + + // A Nice Title Uses The Correct Case + + +#### `Str::toHtmlString()` {.collection-method} + +The `Str::toHtmlString` method converts the string instance to an instance of `Illuminate\Support\HtmlString`, which may be displayed in Blade templates: + + use Illuminate\Support\Str; + + $htmlString = Str::of('Nuno Maduro')->toHtmlString(); + + +#### `Str::ucfirst()` {.collection-method} + +The `Str::ucfirst` method returns the given string with the first character capitalized: + + use Illuminate\Support\Str; + + $string = Str::ucfirst('foo bar'); + + // Foo bar + + +#### `Str::ucsplit()` {.collection-method} + +The `Str::ucsplit` method splits the given string into an array by uppercase characters: + + use Illuminate\Support\Str; + + $segments = Str::ucsplit('FooBar'); + + // [0 => 'Foo', 1 => 'Bar'] + + +#### `Str::upper()` {.collection-method} + +The `Str::upper` method converts the given string to uppercase: + + use Illuminate\Support\Str; + + $string = Str::upper('laravel'); + + // LARAVEL + + +#### `Str::ulid()` {.collection-method} + +The `Str::ulid` method generates a ULID, which is a compact, time-ordered unique identifier: + + use Illuminate\Support\Str; + + return (string) Str::ulid(); + + // 01gd6r360bp37zj17nxb55yv40 + +If you would like to retrieve a `Illuminate\Support\Carbon` date instance representing the date and time that a given ULID was created, you may use the `createFromId` method provided by Laravel's Carbon integration: + +```php +use Illuminate\Support\Carbon; +use Illuminate\Support\Str; + +$date = Carbon::createFromId((string) Str::ulid()); +``` + + +#### `Str::uuid()` {.collection-method} + +The `Str::uuid` method generates a UUID (version 4): + + use Illuminate\Support\Str; + + return (string) Str::uuid(); + + +#### `Str::wordCount()` {.collection-method} + +The `Str::wordCount` method returns the number of words that a string contains: + +```php +use Illuminate\Support\Str; + +Str::wordCount('Hello, world!'); // 2 +``` + + +#### `Str::wordWrap()` {.collection-method} + +The `Str::wordWrap` method wraps a string to a given number of characters: + + use Illuminate\Support\Str; + + $text = "The quick brown fox jumped over the lazy dog." + + Str::wordWrap($text, characters: 20, break: "
    \n"); + + /* + The quick brown fox
    + jumped over the lazy
    + dog. + */ + + +#### `Str::words()` {.collection-method} + +The `Str::words` method limits the number of words in a string. An additional string may be passed to this method via its third argument to specify which string should be appended to the end of the truncated string: + + use Illuminate\Support\Str; + + return Str::words('Perfectly balanced, as all things should be.', 3, ' >>>'); + + // Perfectly balanced, as >>> + + +#### `Str::wrap()` {.collection-method} + +The `Str::wrap` method wraps the given string with an additional string or pair of strings: + + use Illuminate\Support\Str; + + Str::wrap('Laravel', '"'); + + // "Laravel" + + Str::wrap('is', before: 'This ', after: ' Laravel!'); + + // This is Laravel! + + +#### `str()` {.collection-method} + +The `str` function returns a new `Illuminate\Support\Stringable` instance of the given string. This function is equivalent to the `Str::of` method: + + $string = str('Taylor')->append(' Otwell'); + + // 'Taylor Otwell' + +If no argument is provided to the `str` function, the function returns an instance of `Illuminate\Support\Str`: + + $snake = str()->snake('FooBar'); + + // 'foo_bar' + + +#### `trans()` {.collection-method} + +The `trans` function translates the given translation key using your [language files](/docs/{{version}}/localization): + + echo trans('messages.welcome'); + +If the specified translation key does not exist, the `trans` function will return the given key. So, using the example above, the `trans` function would return `messages.welcome` if the translation key does not exist. + + +#### `trans_choice()` {.collection-method} + +The `trans_choice` function translates the given translation key with inflection: + + echo trans_choice('messages.notifications', $unreadCount); + +If the specified translation key does not exist, the `trans_choice` function will return the given key. So, using the example above, the `trans_choice` function would return `messages.notifications` if the translation key does not exist. + + +## Fluent Strings + +Fluent strings provide a more fluent, object-oriented interface for working with string values, allowing you to chain multiple string operations together using a more readable syntax compared to traditional string operations. + + +#### `after` {.collection-method} + +The `after` method returns everything after the given value in a string. The entire string will be returned if the value does not exist within the string: + + use Illuminate\Support\Str; + + $slice = Str::of('This is my name')->after('This is'); + + // ' my name' + + +#### `afterLast` {.collection-method} + +The `afterLast` method returns everything after the last occurrence of the given value in a string. The entire string will be returned if the value does not exist within the string: + + use Illuminate\Support\Str; + + $slice = Str::of('App\Http\Controllers\Controller')->afterLast('\\'); + + // 'Controller' + + +#### `append` {.collection-method} + +The `append` method appends the given values to the string: + + use Illuminate\Support\Str; + + $string = Str::of('Taylor')->append(' Otwell'); + + // 'Taylor Otwell' + + +#### `ascii` {.collection-method} + +The `ascii` method will attempt to transliterate the string into an ASCII value: + + use Illuminate\Support\Str; + + $string = Str::of('ü')->ascii(); + + // 'u' + + +#### `basename` {.collection-method} + +The `basename` method will return the trailing name component of the given string: + + use Illuminate\Support\Str; + + $string = Str::of('/foo/bar/baz')->basename(); + + // 'baz' + +If needed, you may provide an "extension" that will be removed from the trailing component: + + use Illuminate\Support\Str; + + $string = Str::of('/foo/bar/baz.jpg')->basename('.jpg'); + + // 'baz' + + +#### `before` {.collection-method} + +The `before` method returns everything before the given value in a string: + + use Illuminate\Support\Str; + + $slice = Str::of('This is my name')->before('my name'); + + // 'This is ' + + +#### `beforeLast` {.collection-method} + +The `beforeLast` method returns everything before the last occurrence of the given value in a string: + + use Illuminate\Support\Str; + + $slice = Str::of('This is my name')->beforeLast('is'); + + // 'This ' + + +#### `between` {.collection-method} + +The `between` method returns the portion of a string between two values: + + use Illuminate\Support\Str; + + $converted = Str::of('This is my name')->between('This', 'name'); + + // ' is my ' + + +#### `betweenFirst` {.collection-method} + +The `betweenFirst` method returns the smallest possible portion of a string between two values: + + use Illuminate\Support\Str; + + $converted = Str::of('[a] bc [d]')->betweenFirst('[', ']'); + + // 'a' + + +#### `camel` {.collection-method} + +The `camel` method converts the given string to `camelCase`: + + use Illuminate\Support\Str; + + $converted = Str::of('foo_bar')->camel(); + + // fooBar + + +#### `classBasename` {.collection-method} + +The `classBasename` method returns the class name of the given class with the class's namespace removed: + + use Illuminate\Support\Str; + + $class = Str::of('Foo\Bar\Baz')->classBasename(); + + // Baz + + +#### `contains` {.collection-method} + +The `contains` method determines if the given string contains the given value. This method is case sensitive: + + use Illuminate\Support\Str; + + $contains = Str::of('This is my name')->contains('my'); + + // true + +You may also pass an array of values to determine if the given string contains any of the values in the array: + + use Illuminate\Support\Str; + + $contains = Str::of('This is my name')->contains(['my', 'foo']); + + // true + + +#### `containsAll` {.collection-method} + +The `containsAll` method determines if the given string contains all of the values in the given array: + + use Illuminate\Support\Str; + + $containsAll = Str::of('This is my name')->containsAll(['my', 'name']); + + // true + + +#### `dirname` {.collection-method} + +The `dirname` method returns the parent directory portion of the given string: + + use Illuminate\Support\Str; + + $string = Str::of('/foo/bar/baz')->dirname(); + + // '/foo/bar' + +If necessary, you may specify how many directory levels you wish to trim from the string: + + use Illuminate\Support\Str; + + $string = Str::of('/foo/bar/baz')->dirname(2); + + // '/foo' + + +#### `excerpt` {.collection-method} + +The `excerpt` method extracts an excerpt from the string that matches the first instance of a phrase within that string: + + use Illuminate\Support\Str; + + $excerpt = Str::of('This is my name')->excerpt('my', [ + 'radius' => 3 + ]); + + // '...is my na...' + +The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. + +In addition, you may use the `omission` option to change the string that will be prepended and appended to the truncated string: + + use Illuminate\Support\Str; + + $excerpt = Str::of('This is my name')->excerpt('name', [ + 'radius' => 3, + 'omission' => '(...) ' + ]); + + // '(...) my name' + + +#### `endsWith` {.collection-method} + +The `endsWith` method determines if the given string ends with the given value: + + use Illuminate\Support\Str; + + $result = Str::of('This is my name')->endsWith('name'); + + // true + +You may also pass an array of values to determine if the given string ends with any of the values in the array: + + use Illuminate\Support\Str; + + $result = Str::of('This is my name')->endsWith(['name', 'foo']); + + // true + + $result = Str::of('This is my name')->endsWith(['this', 'foo']); + + // false + + +#### `exactly` {.collection-method} + +The `exactly` method determines if the given string is an exact match with another string: + + use Illuminate\Support\Str; + + $result = Str::of('Laravel')->exactly('Laravel'); + + // true + + +#### `explode` {.collection-method} + +The `explode` method splits the string by the given delimiter and returns a collection containing each section of the split string: + + use Illuminate\Support\Str; + + $collection = Str::of('foo bar baz')->explode(' '); + + // collect(['foo', 'bar', 'baz']) + + +#### `finish` {.collection-method} + +The `finish` method adds a single instance of the given value to a string if it does not already end with that value: + + use Illuminate\Support\Str; + + $adjusted = Str::of('this/string')->finish('/'); + + // this/string/ + + $adjusted = Str::of('this/string/')->finish('/'); + + // this/string/ + + +#### `headline` {.collection-method} + +The `headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: + + use Illuminate\Support\Str; + + $headline = Str::of('taylor_otwell')->headline(); + + // Taylor Otwell + + $headline = Str::of('EmailNotificationSent')->headline(); + + // Email Notification Sent + + +#### `inlineMarkdown` {.collection-method} + +The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: + + use Illuminate\Support\Str; + + $html = Str::of('**Laravel**')->inlineMarkdown(); + + // Laravel + + +#### `is` {.collection-method} + +The `is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values + + use Illuminate\Support\Str; + + $matches = Str::of('foobar')->is('foo*'); + + // true + + $matches = Str::of('foobar')->is('baz*'); + + // false + + +#### `isAscii` {.collection-method} + +The `isAscii` method determines if a given string is an ASCII string: + + use Illuminate\Support\Str; + + $result = Str::of('Taylor')->isAscii(); + + // true + + $result = Str::of('ü')->isAscii(); + + // false + + +#### `isEmpty` {.collection-method} + +The `isEmpty` method determines if the given string is empty: + + use Illuminate\Support\Str; + + $result = Str::of(' ')->trim()->isEmpty(); + + // true + + $result = Str::of('Laravel')->trim()->isEmpty(); + + // false + + +#### `isNotEmpty` {.collection-method} + +The `isNotEmpty` method determines if the given string is not empty: + + + use Illuminate\Support\Str; + + $result = Str::of(' ')->trim()->isNotEmpty(); + + // false + + $result = Str::of('Laravel')->trim()->isNotEmpty(); + + // true + + +#### `isJson` {.collection-method} + +The `isJson` method determines if a given string is valid JSON: + + use Illuminate\Support\Str; + + $result = Str::of('[1,2,3]')->isJson(); + + // true + + $result = Str::of('{"first": "John", "last": "Doe"}')->isJson(); + + // true + + $result = Str::of('{first: "John", last: "Doe"}')->isJson(); + + // false + + +#### `isUlid` {.collection-method} + +The `isUlid` method determines if a given string is a ULID: + + use Illuminate\Support\Str; + + $result = Str::of('01gd6r360bp37zj17nxb55yv40')->isUlid(); + + // true + + $result = Str::of('Taylor')->isUlid(); + + // false + + +#### `isUrl` {.collection-method} + +The `isUrl` method determines if a given string is a URL: + + use Illuminate\Support\Str; + + $result = Str::of('http://example.com/')->isUrl(); + + // true + + $result = Str::of('Taylor')->isUrl(); + + // false + + +#### `isUuid` {.collection-method} + +The `isUuid` method determines if a given string is a UUID: + + use Illuminate\Support\Str; + + $result = Str::of('5ace9ab9-e9cf-4ec6-a19d-5881212a452c')->isUuid(); + + // true + + $result = Str::of('Taylor')->isUuid(); + + // false + + +#### `kebab` {.collection-method} + +The `kebab` method converts the given string to `kebab-case`: + + use Illuminate\Support\Str; + + $converted = Str::of('fooBar')->kebab(); + + // foo-bar + + +#### `lcfirst` {.collection-method} + +The `lcfirst` method returns the given string with the first character lowercased: + + use Illuminate\Support\Str; + + $string = Str::of('Foo Bar')->lcfirst(); + + // foo Bar + + + +#### `length` {.collection-method} + +The `length` method returns the length of the given string: + + use Illuminate\Support\Str; + + $length = Str::of('Laravel')->length(); + + // 7 + + +#### `limit` {.collection-method} + +The `limit` method truncates the given string to the specified length: + + use Illuminate\Support\Str; + + $truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20); + + // The quick brown fox... + +You may also pass a second argument to change the string that will be appended to the end of the truncated string: + + use Illuminate\Support\Str; + + $truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20, ' (...)'); + + // The quick brown fox (...) + + +#### `lower` {.collection-method} + +The `lower` method converts the given string to lowercase: + + use Illuminate\Support\Str; + + $result = Str::of('LARAVEL')->lower(); + + // 'laravel' + + +#### `ltrim` {.collection-method} + +The `ltrim` method trims the left side of the string: + + use Illuminate\Support\Str; + + $string = Str::of(' Laravel ')->ltrim(); + + // 'Laravel ' + + $string = Str::of('/Laravel/')->ltrim('/'); + + // 'Laravel/' + + +#### `markdown` {.collection-method} + +The `markdown` method converts GitHub flavored Markdown into HTML: + + use Illuminate\Support\Str; + + $html = Str::of('# Laravel')->markdown(); + + //

    Laravel

    + + $html = Str::of('# Taylor Otwell')->markdown([ + 'html_input' => 'strip', + ]); + + //

    Taylor Otwell

    + + +#### `mask` {.collection-method} + +The `mask` method masks a portion of a string with a repeated character, and may be used to obfuscate segments of strings such as email addresses and phone numbers: + + use Illuminate\Support\Str; + + $string = Str::of('taylor@example.com')->mask('*', 3); + + // tay*************** + +If needed, you may provide negative numbers as the third or fourth argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: + + $string = Str::of('taylor@example.com')->mask('*', -15, 3); + + // tay***@example.com + + $string = Str::of('taylor@example.com')->mask('*', 4, -4); + + // tayl**********.com + + +#### `match` {.collection-method} + +The `match` method will return the portion of a string that matches a given regular expression pattern: + + use Illuminate\Support\Str; + + $result = Str::of('foo bar')->match('/bar/'); + + // 'bar' + + $result = Str::of('foo bar')->match('/foo (.*)/'); + + // 'bar' + + +#### `matchAll` {.collection-method} + +The `matchAll` method will return a collection containing the portions of a string that match a given regular expression pattern: + + use Illuminate\Support\Str; + + $result = Str::of('bar foo bar')->matchAll('/bar/'); + + // collect(['bar', 'bar']) + +If you specify a matching group within the expression, Laravel will return a collection of that group's matches: + + use Illuminate\Support\Str; + + $result = Str::of('bar fun bar fly')->matchAll('/f(\w*)/'); + + // collect(['un', 'ly']); + +If no matches are found, an empty collection will be returned. + + +#### `isMatch` {.collection-method} + +The `isMatch` method will return `true` if the string matches a given regular expression: + + use Illuminate\Support\Str; + + $result = Str::of('foo bar')->isMatch('/foo (.*)/'); + + // true + + $result = Str::of('laravel')->isMatch('/foo (.*)/'); + + // false + + +#### `newLine` {.collection-method} + +The `newLine` method appends an "end of line" character to a string: + + use Illuminate\Support\Str; + + $padded = Str::of('Laravel')->newLine()->append('Framework'); + + // 'Laravel + // Framework' + + +#### `padBoth` {.collection-method} + +The `padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches the desired length: + + use Illuminate\Support\Str; + + $padded = Str::of('James')->padBoth(10, '_'); + + // '__James___' + + $padded = Str::of('James')->padBoth(10); + + // ' James ' + + +#### `padLeft` {.collection-method} + +The `padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches the desired length: + + use Illuminate\Support\Str; + + $padded = Str::of('James')->padLeft(10, '-='); + + // '-=-=-James' + + $padded = Str::of('James')->padLeft(10); + + // ' James' + + +#### `padRight` {.collection-method} + +The `padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches the desired length: + + use Illuminate\Support\Str; + + $padded = Str::of('James')->padRight(10, '-'); + + // 'James-----' + + $padded = Str::of('James')->padRight(10); + + // 'James ' + + +#### `pipe` {.collection-method} + +The `pipe` method allows you to transform the string by passing its current value to the given callable: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $hash = Str::of('Laravel')->pipe('md5')->prepend('Checksum: '); + + // 'Checksum: a5c95b86291ea299fcbe64458ed12702' + + $closure = Str::of('foo')->pipe(function (Stringable $str) { + return 'bar'; + }); + + // 'bar' + + +#### `plural` {.collection-method} + +The `plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): + + use Illuminate\Support\Str; + + $plural = Str::of('car')->plural(); + + // cars + + $plural = Str::of('child')->plural(); + + // children + +You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: + + use Illuminate\Support\Str; + + $plural = Str::of('child')->plural(2); + + // children + + $plural = Str::of('child')->plural(1); + + // child + + +#### `prepend` {.collection-method} + +The `prepend` method prepends the given values onto the string: + + use Illuminate\Support\Str; + + $string = Str::of('Framework')->prepend('Laravel '); + + // Laravel Framework + + +#### `remove` {.collection-method} + +The `remove` method removes the given value or array of values from the string: + + use Illuminate\Support\Str; + + $string = Str::of('Arkansas is quite beautiful!')->remove('quite'); + + // Arkansas is beautiful! + +You may also pass `false` as a second parameter to ignore case when removing strings. + + +#### `repeat` {.collection-method} + +The `repeat` method repeats the given string: + +```php +use Illuminate\Support\Str; + +$repeated = Str::of('a')->repeat(5); + +// aaaaa +``` + + +#### `replace` {.collection-method} + +The `replace` method replaces a given string within the string: + + use Illuminate\Support\Str; + + $replaced = Str::of('Laravel 6.x')->replace('6.x', '7.x'); + + // Laravel 7.x + +The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: + + $replaced = Str::of('macOS 13.x')->replace( + 'macOS', 'iOS', caseSensitive: false + ); + + +#### `replaceArray` {.collection-method} + +The `replaceArray` method replaces a given value in the string sequentially using an array: + + use Illuminate\Support\Str; + + $string = 'The event will take place between ? and ?'; + + $replaced = Str::of($string)->replaceArray('?', ['8:30', '9:00']); + + // The event will take place between 8:30 and 9:00 + + +#### `replaceFirst` {.collection-method} + +The `replaceFirst` method replaces the first occurrence of a given value in a string: + + use Illuminate\Support\Str; + + $replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceFirst('the', 'a'); + + // a quick brown fox jumps over the lazy dog + + +#### `replaceLast` {.collection-method} + +The `replaceLast` method replaces the last occurrence of a given value in a string: + + use Illuminate\Support\Str; + + $replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceLast('the', 'a'); + + // the quick brown fox jumps over a lazy dog + + +#### `replaceMatches` {.collection-method} + +The `replaceMatches` method replaces all portions of a string matching a pattern with the given replacement string: + + use Illuminate\Support\Str; + + $replaced = Str::of('(+1) 501-555-1000')->replaceMatches('/[^A-Za-z0-9]++/', '') + + // '15015551000' + +The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given pattern, allowing you to perform the replacement logic within the closure and return the replaced value: + + use Illuminate\Support\Str; + + $replaced = Str::of('123')->replaceMatches('/\d/', function (array $matches) { + return '['.$matches[0].']'; + }); + + // '[1][2][3]' + + +#### `replaceStart` {.collection-method} + +The `replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: + + use Illuminate\Support\Str; + + $replaced = Str::of('Hello World')->replaceStart('Hello', 'Laravel'); + + // Laravel World + + $replaced = Str::of('Hello World')->replaceStart('World', 'Laravel'); + + // Hello World + + +#### `replaceEnd` {.collection-method} + +The `replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: + + use Illuminate\Support\Str; + + $replaced = Str::of('Hello World')->replaceEnd('World', 'Laravel'); + + // Hello Laravel + + $replaced = Str::of('Hello World')->replaceEnd('Hello', 'Laravel'); + + // Hello World + + +#### `rtrim` {.collection-method} + +The `rtrim` method trims the right side of the given string: + + use Illuminate\Support\Str; + + $string = Str::of(' Laravel ')->rtrim(); + + // ' Laravel' + + $string = Str::of('/Laravel/')->rtrim('/'); + + // '/Laravel' + + +#### `scan` {.collection-method} + +The `scan` method parses input from a string into a collection according to a format supported by the [`sscanf` PHP function](https://www.php.net/manual/en/function.sscanf.php): + + use Illuminate\Support\Str; + + $collection = Str::of('filename.jpg')->scan('%[^.].%s'); + + // collect(['filename', 'jpg']) + + +#### `singular` {.collection-method} + +The `singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): + + use Illuminate\Support\Str; + + $singular = Str::of('cars')->singular(); + + // car + + $singular = Str::of('children')->singular(); + + // child + + +#### `slug` {.collection-method} + +The `slug` method generates a URL friendly "slug" from the given string: + + use Illuminate\Support\Str; + + $slug = Str::of('Laravel Framework')->slug('-'); + + // laravel-framework + + +#### `snake` {.collection-method} + +The `snake` method converts the given string to `snake_case`: + + use Illuminate\Support\Str; + + $converted = Str::of('fooBar')->snake(); + + // foo_bar + + +#### `split` {.collection-method} + +The `split` method splits a string into a collection using a regular expression: + + use Illuminate\Support\Str; + + $segments = Str::of('one, two, three')->split('/[\s,]+/'); + + // collect(["one", "two", "three"]) + + +#### `squish` {.collection-method} + +The `squish` method removes all extraneous white space from a string, including extraneous white space between words: + + use Illuminate\Support\Str; + + $string = Str::of(' laravel framework ')->squish(); + + // laravel framework + + +#### `start` {.collection-method} + +The `start` method adds a single instance of the given value to a string if it does not already start with that value: + + use Illuminate\Support\Str; + + $adjusted = Str::of('this/string')->start('/'); + + // /this/string + + $adjusted = Str::of('/this/string')->start('/'); + + // /this/string + + +#### `startsWith` {.collection-method} + +The `startsWith` method determines if the given string begins with the given value: + + use Illuminate\Support\Str; + + $result = Str::of('This is my name')->startsWith('This'); + + // true + + +#### `studly` {.collection-method} + +The `studly` method converts the given string to `StudlyCase`: + + use Illuminate\Support\Str; + + $converted = Str::of('foo_bar')->studly(); + + // FooBar + + +#### `substr` {.collection-method} + +The `substr` method returns the portion of the string specified by the given start and length parameters: + + use Illuminate\Support\Str; + + $string = Str::of('Laravel Framework')->substr(8); + + // Framework + + $string = Str::of('Laravel Framework')->substr(8, 5); + + // Frame + + +#### `substrReplace` {.collection-method} + +The `substrReplace` method replaces text within a portion of a string, starting at the position specified by the second argument and replacing the number of characters specified by the third argument. Passing `0` to the method's third argument will insert the string at the specified position without replacing any of the existing characters in the string: + + use Illuminate\Support\Str; + + $string = Str::of('1300')->substrReplace(':', 2); + + // 13: + + $string = Str::of('The Framework')->substrReplace(' Laravel', 3, 0); + + // The Laravel Framework + + +#### `swap` {.collection-method} + +The `swap` method replaces multiple values in the string using PHP's `strtr` function: + + use Illuminate\Support\Str; + + $string = Str::of('Tacos are great!') + ->swap([ + 'Tacos' => 'Burritos', + 'great' => 'fantastic', + ]); + + // Burritos are fantastic! + + +#### `tap` {.collection-method} + +The `tap` method passes the string to the given closure, allowing you to examine and interact with the string while not affecting the string itself. The original string is returned by the `tap` method regardless of what is returned by the closure: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('Laravel') + ->append(' Framework') + ->tap(function (Stringable $string) { + dump('String after append: '.$string); + }) + ->upper(); + + // LARAVEL FRAMEWORK + + +#### `test` {.collection-method} + +The `test` method determines if a string matches the given regular expression pattern: + + use Illuminate\Support\Str; + + $result = Str::of('Laravel Framework')->test('/Laravel/'); + + // true + + +#### `title` {.collection-method} + +The `title` method converts the given string to `Title Case`: + + use Illuminate\Support\Str; + + $converted = Str::of('a nice title uses the correct case')->title(); + + // A Nice Title Uses The Correct Case + + +#### `trim` {.collection-method} + +The `trim` method trims the given string: + + use Illuminate\Support\Str; + + $string = Str::of(' Laravel ')->trim(); + + // 'Laravel' + + $string = Str::of('/Laravel/')->trim('/'); + + // 'Laravel' + + +#### `ucfirst` {.collection-method} + +The `ucfirst` method returns the given string with the first character capitalized: + + use Illuminate\Support\Str; + + $string = Str::of('foo bar')->ucfirst(); + + // Foo bar + + +#### `ucsplit` {.collection-method} + +The `ucsplit` method splits the given string into a collection by uppercase characters: + + use Illuminate\Support\Str; + + $string = Str::of('Foo Bar')->ucsplit(); + + // collect(['Foo', 'Bar']) + + +#### `upper` {.collection-method} + +The `upper` method converts the given string to uppercase: + + use Illuminate\Support\Str; + + $adjusted = Str::of('laravel')->upper(); + + // LARAVEL + + +#### `when` {.collection-method} + +The `when` method invokes the given closure if a given condition is `true`. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('Taylor') + ->when(true, function (Stringable $string) { + return $string->append(' Otwell'); + }); + + // 'Taylor Otwell' + +If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the condition parameter evaluates to `false`. + + +#### `whenContains` {.collection-method} + +The `whenContains` method invokes the given closure if the string contains the given value. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('tony stark') + ->whenContains('tony', function (Stringable $string) { + return $string->title(); + }); + + // 'Tony Stark' + +If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the string does not contain the given value. + +You may also pass an array of values to determine if the given string contains any of the values in the array: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('tony stark') + ->whenContains(['tony', 'hulk'], function (Stringable $string) { + return $string->title(); + }); + + // Tony Stark + + +#### `whenContainsAll` {.collection-method} + +The `whenContainsAll` method invokes the given closure if the string contains all of the given sub-strings. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('tony stark') + ->whenContainsAll(['tony', 'stark'], function (Stringable $string) { + return $string->title(); + }); + + // 'Tony Stark' + +If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the condition parameter evaluates to `false`. + + +#### `whenEmpty` {.collection-method} + +The `whenEmpty` method invokes the given closure if the string is empty. If the closure returns a value, that value will also be returned by the `whenEmpty` method. If the closure does not return a value, the fluent string instance will be returned: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of(' ')->whenEmpty(function (Stringable $string) { + return $string->trim()->prepend('Laravel'); + }); + + // 'Laravel' + + +#### `whenNotEmpty` {.collection-method} + +The `whenNotEmpty` method invokes the given closure if the string is not empty. If the closure returns a value, that value will also be returned by the `whenNotEmpty` method. If the closure does not return a value, the fluent string instance will be returned: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('Framework')->whenNotEmpty(function (Stringable $string) { + return $string->prepend('Laravel '); + }); + + // 'Laravel Framework' + + +#### `whenStartsWith` {.collection-method} + +The `whenStartsWith` method invokes the given closure if the string starts with the given sub-string. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('disney world')->whenStartsWith('disney', function (Stringable $string) { + return $string->title(); + }); + + // 'Disney World' + + +#### `whenEndsWith` {.collection-method} + +The `whenEndsWith` method invokes the given closure if the string ends with the given sub-string. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('disney world')->whenEndsWith('world', function (Stringable $string) { + return $string->title(); + }); + + // 'Disney World' + + +#### `whenExactly` {.collection-method} + +The `whenExactly` method invokes the given closure if the string exactly matches the given string. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('laravel')->whenExactly('laravel', function (Stringable $string) { + return $string->title(); + }); + + // 'Laravel' + + +#### `whenNotExactly` {.collection-method} + +The `whenNotExactly` method invokes the given closure if the string does not exactly match the given string. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('framework')->whenNotExactly('laravel', function (Stringable $string) { + return $string->title(); + }); + + // 'Framework' + + +#### `whenIs` {.collection-method} + +The `whenIs` method invokes the given closure if the string matches a given pattern. Asterisks may be used as wildcard values. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('foo/bar')->whenIs('foo/*', function (Stringable $string) { + return $string->append('/baz'); + }); + + // 'foo/bar/baz' + + +#### `whenIsAscii` {.collection-method} + +The `whenIsAscii` method invokes the given closure if the string is 7 bit ASCII. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('laravel')->whenIsAscii(function (Stringable $string) { + return $string->title(); + }); + + // 'Laravel' + + +#### `whenIsUlid` {.collection-method} + +The `whenIsUlid` method invokes the given closure if the string is a valid ULID. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + + $string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function (Stringable $string) { + return $string->substr(0, 8); + }); + + // '01gd6r36' + + +#### `whenIsUuid` {.collection-method} + +The `whenIsUuid` method invokes the given closure if the string is a valid UUID. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function (Stringable $string) { + return $string->substr(0, 8); + }); + + // 'a0a2a2d2' + + +#### `whenTest` {.collection-method} + +The `whenTest` method invokes the given closure if the string matches the given regular expression. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + use Illuminate\Support\Stringable; + + $string = Str::of('laravel framework')->whenTest('/laravel/', function (Stringable $string) { + return $string->title(); + }); + + // 'Laravel Framework' + + +#### `wordCount` {.collection-method} + +The `wordCount` method returns the number of words that a string contains: + +```php +use Illuminate\Support\Str; + +Str::of('Hello, world!')->wordCount(); // 2 +``` + + +#### `words` {.collection-method} + +The `words` method limits the number of words in a string. If necessary, you may specify an additional string that will be appended to the truncated string: + + use Illuminate\Support\Str; + + $string = Str::of('Perfectly balanced, as all things should be.')->words(3, ' >>>'); + + // Perfectly balanced, as >>> From 1609eb7f8d5935243a7809d19b10c349b9d8d53d Mon Sep 17 00:00:00 2001 From: Peter Hauke <90506472+peterhauke@users.noreply.github.com> Date: Tue, 12 Sep 2023 13:09:52 +0200 Subject: [PATCH 1103/2609] Fix typo (#9022) * Fix typo Both "an `App" and "a `App" appear in this documentation and most probably "an `App" is the correct version, so this PR is for consistency. * Fix typo See #9022 * Fix typo See #9022 * Fix typo See #9022 --- authentication.md | 2 +- authorization.md | 2 +- broadcasting.md | 4 ++-- eloquent-factories.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/authentication.md b/authentication.md index 7c7717f3373..02f00461bce 100644 --- a/authentication.md +++ b/authentication.md @@ -707,7 +707,7 @@ Now that we have explored each of the methods on the `UserProvider`, let's take This interface is simple. The `getAuthIdentifierName` method should return the name of the "primary key" field of the user and the `getAuthIdentifier` method should return the "primary key" of the user. When using a MySQL back-end, this would likely be the auto-incrementing primary key assigned to the user record. The `getAuthPassword` method should return the user's hashed password. -This interface allows the authentication system to work with any "user" class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes a `App\Models\User` class in the `app/Models` directory which implements this interface. +This interface allows the authentication system to work with any "user" class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes an `App\Models\User` class in the `app/Models` directory which implements this interface. ## Events diff --git a/authorization.md b/authorization.md index 1c7eb9ed902..186e1fcb1bf 100644 --- a/authorization.md +++ b/authorization.md @@ -264,7 +264,7 @@ If the action is not authorized or if no user is currently authenticated, Larave ### 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 `App\Models\Post` model and a corresponding `App\Policies\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 an `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. 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: diff --git a/broadcasting.md b/broadcasting.md index cfd777c2441..3e7b80f496b 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -43,7 +43,7 @@ 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. -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. +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 an `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. 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. @@ -1018,7 +1018,7 @@ If you plan to explicitly return a channel instance from your model's `broadcast return [new Channel($this->user)]; ``` -If you need to determine the channel name of a model, you may call the `broadcastChannel` method on any model instance. For example, this method returns the string `App.Models.User.1` for a `App\Models\User` model with an `id` of `1`: +If you need to determine the channel name of a model, you may call the `broadcastChannel` method on any model instance. For example, this method returns the string `App.Models.User.1` for an `App\Models\User` model with an `id` of `1`: ```php $user->broadcastChannel() diff --git a/eloquent-factories.md b/eloquent-factories.md index f2f715fe34d..f42cbdf446b 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -442,7 +442,7 @@ For convenience, you may use Laravel's magic factory relationship methods to def ### Polymorphic Relationships -[Polymorphic relationships](/docs/{{version}}/eloquent-relationships#polymorphic-relationships) may also be created using factories. Polymorphic "morph many" relationships are created in the same way as typical "has many" relationships. For example, if a `App\Models\Post` model has a `morphMany` relationship with a `App\Models\Comment` model: +[Polymorphic relationships](/docs/{{version}}/eloquent-relationships#polymorphic-relationships) may also be created using factories. Polymorphic "morph many" relationships are created in the same way as typical "has many" relationships. For example, if an `App\Models\Post` model has a `morphMany` relationship with an `App\Models\Comment` model: use App\Models\Post; From c3a2cb2a8fcfcb4eec41c4f148046a9dbdeaa504 Mon Sep 17 00:00:00 2001 From: Peter Hauke <90506472+peterhauke@users.noreply.github.com> Date: Tue, 12 Sep 2023 17:55:05 +0200 Subject: [PATCH 1104/2609] Fix typo (#9023) Both "an `App" and "a `App" appear in this documentation and most probably "an `App" is the correct version, so this PR is for consistency. See #9022 Sorry @taylorotwell , I thought I found them all, but I missed this one. --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index 186e1fcb1bf..dd98b989ef8 100644 --- a/authorization.md +++ b/authorization.md @@ -675,7 +675,7 @@ Laravel includes a middleware that can authorize actions before the incoming req // 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. +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), an `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. For convenience, you may also attach the `can` middleware to your route using the `can` method: From 9b546921226e9a9adacb74b802262291c19a93fe Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 12 Sep 2023 16:56:48 +0100 Subject: [PATCH 1105/2609] Adds `make:view` command documentation (#9013) --- views.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/views.md b/views.md index b73ea69dcdc..9a968cd36ad 100644 --- a/views.md +++ b/views.md @@ -48,7 +48,13 @@ Our Breeze and Jetstream [starter kits](/docs/{{version}}/starter-kits) give you ## Creating & Rendering Views -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. +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 +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: From 17bd1d424939f3abf09ca3ee933fd64ce4fd2594 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Sep 2023 11:08:27 -0500 Subject: [PATCH 1106/2609] truncation hooks --- dusk.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dusk.md b/dusk.md index 719f6e67a2a..5e50844430e 100644 --- a/dusk.md +++ b/dusk.md @@ -222,6 +222,24 @@ To specify the database connections that should have their tables truncated, you */ protected $connectionsToTruncate = ['mysql']; +If you would like to execute code before or after database truncation is performed, you may define `beforeTruncatingDatabase` or `afterTruncatingDatabase` methods on your test class: + + /** + * Perform any work that should take place before the database has started truncating. + */ + protected function beforeTruncatingDatabase(): void + { + // + } + + /** + * Perform any work that should take place after the database has finished truncating. + */ + protected function afterTruncatingDatabase(): void + { + // + } + ### Running Tests From fca85ee87aefb3ceb3bdcd45e944471febe206bb Mon Sep 17 00:00:00 2001 From: Dmitri Chebotarev Date: Tue, 12 Sep 2023 17:40:00 +0100 Subject: [PATCH 1107/2609] fixed legacy PHP syntax (#9024) --- routing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing.md b/routing.md index 1a050fbd615..71a83935072 100644 --- a/routing.md +++ b/routing.md @@ -197,11 +197,11 @@ If your route has dependencies that you would like the Laravel service container Occasionally you may need to specify a route parameter that may not always be present in the URI. You may do so by placing a `?` mark after the parameter name. Make sure to give the route's corresponding variable a default value: - Route::get('/user/{name?}', function (string $name = null) { + Route::get('/user/{name?}', function (?string $name = null) { return $name; }); - Route::get('/user/{name?}', function (string $name = 'John') { + Route::get('/user/{name?}', function (?string $name = 'John') { return $name; }); From 63dde39470588ab1ad39cea9a592f0838ca5ad47 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Sep 2023 15:00:02 -0500 Subject: [PATCH 1108/2609] undocument cache tags due to complexity of implementation --- cache.md | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/cache.md b/cache.md index 205d79c611e..1ad23095199 100644 --- a/cache.md +++ b/cache.md @@ -9,11 +9,6 @@ - [Storing Items In The Cache](#storing-items-in-the-cache) - [Removing Items From The Cache](#removing-items-from-the-cache) - [The Cache Helper](#the-cache-helper) -- [Cache Tags](#cache-tags) - - [Storing Tagged Cache Items](#storing-tagged-cache-items) - - [Accessing Tagged Cache Items](#accessing-tagged-cache-items) - - [Removing Tagged Cache Items](#removing-tagged-cache-items) - - [Pruning Stale Cache Tags](#pruning-stale-cache-tags) - [Atomic Locks](#atomic-locks) - [Driver Prerequisites](#lock-driver-prerequisites) - [Managing Locks](#managing-locks) @@ -267,51 +262,6 @@ When the `cache` function is called without any arguments, it returns an instanc > **Note** > 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 - -> **Warning** -> Cache tags are not supported when using the `file`, `dynamodb`, or `database` cache drivers. Furthermore, when using multiple tags with caches that are stored "forever", performance will be best with a driver such as `memcached`, which automatically purges stale records. - - -### 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` a value into the cache: - - Cache::tags(['people', 'artists'])->put('John', $john, $seconds); - - Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds); - - -### Accessing Tagged Cache Items - -Items stored via tags may not be accessed without also providing the tags that were used to store the value. To retrieve a tagged cache item, pass the same ordered list of tags to the `tags` method and then call the `get` method with the key you wish to retrieve: - - $john = Cache::tags(['people', 'artists'])->get('John'); - - $anne = Cache::tags(['people', 'authors'])->get('Anne'); - - -### Removing Tagged Cache Items - -You may flush all items that are assigned a tag or list of tags. For example, this statement would remove all caches tagged with either `people`, `authors`, or both. So, both `Anne` and `John` would be removed from the cache: - - Cache::tags(['people', 'authors'])->flush(); - -In contrast, this statement would remove only cached values tagged with `authors`, so `Anne` would be removed, but not `John`: - - Cache::tags('authors')->flush(); - - -### Pruning Stale Cache Tags - -> **Warning** -> Pruning stale cache tags is only necessary when using Redis as your application's cache driver. - -In order to properly prune stale cache tag entries when using the Redis cache driver, Laravel's `cache:prune-stale-tags` Artisan command should be [scheduled](/docs/{{version}}/scheduling) in your application's `App\Console\Kernel` class: - - $schedule->command('cache:prune-stale-tags')->hourly(); - ## Atomic Locks From f655b3c520b0d8a428ad3b0c152acfd92c6d222b Mon Sep 17 00:00:00 2001 From: Peter Hauke <90506472+peterhauke@users.noreply.github.com> Date: Wed, 13 Sep 2023 16:32:23 +0200 Subject: [PATCH 1109/2609] Change `e` helper to `e` function (#9027) Searching for: `e` helper in the current 10.x docs points back to this location, however searching for: `e` function finds the `e` function in the Strings section In the Strings section, `e()` is referred to as the `e` function. --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index ccee85afe02..5c9633b2b5b 100644 --- a/blade.md +++ b/blade.md @@ -88,7 +88,7 @@ The current UNIX timestamp is {{ time() }}. ### 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`: +By default, Blade (and the Laravel `e` function) 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`: Date: Wed, 13 Sep 2023 16:34:27 +0200 Subject: [PATCH 1110/2609] Note about inline attachments rendered as data urls in browser (#9025) * Note about inline attachments rendered as data urls in browser * Update mail.md --------- Co-authored-by: Taylor Otwell --- mail.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/mail.md b/mail.md index e9aff18de05..1ae40cda5bd 100644 --- a/mail.md +++ b/mail.md @@ -919,9 +919,6 @@ When designing a mailable's template, it is convenient to quickly preview the re return new App\Mail\InvoicePaid($invoice); }); -> **Warning** -> [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [Mailpit](https://github.com/axllent/mailpit) or [HELO](https://usehelo.com). - ## Localizing Mailables From c5ff3ca4797cb5752f4439845d97b584b1fcea96 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 14 Sep 2023 00:44:46 +1000 Subject: [PATCH 1111/2609] Document deduplicating exceptions (#9026) * Document deduplicating exceptions * formatting * formatting --------- Co-authored-by: Taylor Otwell --- errors.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/errors.md b/errors.md index 6c356ff8ae4..288a19f030d 100644 --- a/errors.md +++ b/errors.md @@ -117,6 +117,42 @@ Sometimes you may need to report an exception but continue handling the current } } + +#### Deduplicating Reported Exceptions + +If you are using the `report` function throughout your application, you may occasionally report the same exception multiple times, creating duplicate entries in your logs. + +If you would like to ensure that a single instance of an exception is only ever reported once, you may call the exception handler's `dontReportDuplicates` method. Typically, this method should be invoked from the `boot` method of your application's `AppServiceProvider`: + +```php +use Illuminate\Contracts\Debug\ExceptionHandler; + +/** + * Bootstrap any application services. + */ +public function boot(ExceptionHandler $exceptionHandler): void +{ + $exceptionHandler->dontReportDuplicates(); +} +``` + +Now, when the `report` helper is called with the same instance of an exception, only the first call will be reported: + +```php +$original = new RuntimeException('Whoops!'); + +report($original); // reported + +try { + throw $original; +} catch (Throwable $caught) { + report($caught); // ignored +} + +report($original); // ignored +report($caught); // ignored +``` + ### Exception Log Levels From f73fc3bdaeafa776b755ba5e5c488cc7ce50e9e5 Mon Sep 17 00:00:00 2001 From: Salvatore Scalzi Date: Thu, 14 Sep 2023 15:01:15 +0200 Subject: [PATCH 1112/2609] [Docs] Prompts - missing comma (#9029) * Missing comma * fix curly bracket --- prompts.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/prompts.md b/prompts.md index 06e03d08dba..d7b9fbf5d33 100644 --- a/prompts.md +++ b/prompts.md @@ -265,7 +265,7 @@ Unlike other prompt functions, the `select` function doesn't accept the `require ```php $role = select( - label: 'What role should the user have?' + label: 'What role should the user have?', options: [ 'member' => 'Member', 'contributor' => 'Contributor', @@ -274,8 +274,7 @@ $role = select( validate: fn (string $value) => $value === 'owner' && User::where('role', 'owner')->exists() ? 'An owner already exists.' - : null - } + : null ); ``` @@ -352,7 +351,7 @@ You may pass a closure to the `validate` argument if you need to present an opti ``` $permissions = select( - label: 'What permissions should the user have?' + label: 'What permissions should the user have?', options: [ 'read' => 'Read', 'create' => 'Create', From 1322e1a84223b173f4c878f5157d3cd3cc7a8638 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Sat, 16 Sep 2023 08:37:27 +1000 Subject: [PATCH 1113/2609] Include hint example with each prompt (#9033) --- prompts.md | 58 +++++++++++++++++++++--------------------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/prompts.md b/prompts.md index d7b9fbf5d33..2e23a8e8de5 100644 --- a/prompts.md +++ b/prompts.md @@ -11,7 +11,6 @@ - [Suggest](#suggest) - [Search](#search) - [Informational Messages](#informational-messages) -- [Hint Text](#hint-text) - [Terminal Considerations](#terminal-considerations) - [Unsupported Environments & Fallbacks](#fallbacks) @@ -52,13 +51,14 @@ use function Laravel\Prompts\text; $name = text('What is your name?'); ``` -You may also include placeholder text and a default value: +You may also include placeholder text, a default value, and an informational hint: ```php $name = text( label: 'What is your name?', placeholder: 'E.g. Taylor Otwell', - default: $user?->name + default: $user?->name, + hint: 'This will be displayed on your profile.' ); ``` @@ -112,12 +112,13 @@ use function Laravel\Prompts\password; $password = password('What is your password?'); ``` -You may also include placeholder text: +You may also include placeholder text and an informational hint: ```php $password = password( label: 'What is your password?', - placeholder: 'Minimum 8 characters...' + placeholder: 'password', + hint: 'Minimum 8 characters.' ); ``` @@ -170,22 +171,15 @@ use function Laravel\Prompts\confirm; $confirmed = confirm('Do you accept the terms?'); ``` -By default, the "Yes" answer will be pre-selected. However, you may configure the default to be "No" by passing `false` to the `default` argument: +You may also include a default value, customized wording for the "Yes" and "No" labels, and an informational hint: ```php $confirmed = confirm( label: 'Do you accept the terms?', default: false, -); -``` - -You may also customize the wording used for the "Yes" and "No" labels by passing the `yes` and `no` arguments: - -```php -$confirmed = confirm( - label: 'Do you accept the terms?', yes: 'I accept', - no: 'I decline' + no: 'I decline', + hint: 'The terms must be accepted to continue.' ); ``` @@ -224,13 +218,14 @@ $role = select( ); ``` -You may also specify the default choice by passing the `default` argument: +You may also specify the default choice and an informational hint: ```php $role = select( label: 'What role should the user have?', options: ['Member', 'Contributor', 'Owner'], - default: 'Owner' + default: 'Owner', + hint: 'The role may be changed at any time.' ); ``` @@ -274,7 +269,7 @@ $role = select( validate: fn (string $value) => $value === 'owner' && User::where('role', 'owner')->exists() ? 'An owner already exists.' - : null + : null ); ``` @@ -294,7 +289,7 @@ $permissions = multiselect( ); ``` -You may also specify options that should be pre-selected by passing an array to the `default` argument: +You may also specify default choices and an informational hint: ```php use function Laravel\Prompts\multiselect; @@ -302,7 +297,8 @@ use function Laravel\Prompts\multiselect; $permissions = multiselect( label: 'What permissions should be assigned?', options: ['Read', 'Create', 'Update', 'Delete'], - default: ['Read', 'Create'] + default: ['Read', 'Create'], + hint: 'Permissions may be updated at any time.' ); ``` @@ -387,14 +383,15 @@ $name = suggest( ) ``` -You may also include placeholder text and a default value: +You may also include placeholder text, a default value, and an informational hint: ```php $name = suggest( label: 'What is your name?', options: ['Taylor', 'Dayle'], placeholder: 'E.g. Taylor', - default: $user?->name + default: $user?->name, + hint: 'This will be displayed on your profile.' ); ``` @@ -458,7 +455,7 @@ $id = search( The closure will receive the text that has been typed by the user so far and must return an array of options. If you return an associative array then the selected option's key will be returned, otherwise its value will be returned instead. -You may also include placeholder text: +You may also include placeholder text and an informational hint: ```php $id = search( @@ -466,7 +463,8 @@ $id = search( placeholder: 'E.g. Taylor Otwell', options: fn (string $value) => strlen($value) > 0 ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() - : [] + : [], + hint: 'The user will receive an email immediately.' ); ``` @@ -516,18 +514,6 @@ use function Laravel\Prompts\info; info('Package installed successfully.'); ``` - -### Hint Text - -All prompt functions also support "hint text". This text will be displayed underneath the prompt to give the user information or instructions regarding the prompt: - -```php -$email = text( - label: 'What is your email address?', - hint: 'We will never share your email address with anyone.', -); -``` - ### Terminal Considerations From bceec752841d686d8b593ef04c80b83007c75778 Mon Sep 17 00:00:00 2001 From: Manel Gavalda Date: Mon, 18 Sep 2023 16:42:45 +0200 Subject: [PATCH 1114/2609] [10.x] Add `laravel/folio` to contributions page (#9035) Add github link to `laravel/folio` --- contributions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributions.md b/contributions.md index ac9a71b4633..073342fbd7d 100644 --- a/contributions.md +++ b/contributions.md @@ -34,6 +34,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Cashier Paddle](https://github.com/laravel/cashier-paddle) - [Laravel Echo](https://github.com/laravel/echo) - [Laravel Envoy](https://github.com/laravel/envoy) +- [Laravel Folio](https://github.com/laravel/folio) - [Laravel Framework](https://github.com/laravel/framework) - [Laravel Homestead](https://github.com/laravel/homestead) - [Laravel Homestead Build Scripts](https://github.com/laravel/settler) From 4aebf835583ddf42ba7918b3fc50e78fb65954f0 Mon Sep 17 00:00:00 2001 From: Hossain Mohammad Shahidullah Jaber <55241455+JaberWiki@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:43:14 +0600 Subject: [PATCH 1115/2609] typo: correct article in helpers.md (#9036) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 959966c204e..28ebc43dba7 100644 --- a/helpers.md +++ b/helpers.md @@ -1427,7 +1427,7 @@ The `decrypt` function [decrypts](/docs/{{version}}/encryption) the given value. #### `dd()` {.collection-method} -The `dd` function dumps the given variables and ends execution of the script: +The `dd` function dumps the given variables and ends the execution of the script: dd($value); From a2cf776692b78089c6e06464fd2bcfad31604f85 Mon Sep 17 00:00:00 2001 From: Manel Gavalda Date: Mon, 18 Sep 2023 16:53:34 +0200 Subject: [PATCH 1116/2609] [10.x] Update contributions.md to reflect laravel prompts (#9034) Add github link to laravel/prompts --- contributions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributions.md b/contributions.md index 073342fbd7d..6df192f1585 100644 --- a/contributions.md +++ b/contributions.md @@ -43,6 +43,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Passport](https://github.com/laravel/passport) - [Laravel Pennant](https://github.com/laravel/pennant) - [Laravel Pint](https://github.com/laravel/pint) +- [Laravel Prompts](https://github.com/laravel/prompts) - [Laravel Sail](https://github.com/laravel/sail) - [Laravel Sanctum](https://github.com/laravel/sanctum) - [Laravel Scout](https://github.com/laravel/scout) From a59ca91891943d18c72e617e00d02a51c107dea3 Mon Sep 17 00:00:00 2001 From: Manel Gavalda Date: Tue, 19 Sep 2023 17:33:40 +0200 Subject: [PATCH 1117/2609] [10.x] Prompts: Adds `spin` function documentation (#9030) * [10.x] Prompts: Adds `spin`function documentation This PR adds a new section for the `spin` function. * Update prompts.md Move menu item * Make it clearer that the message is optional * Update function formatting * Fix Facade name * Remove returning of variable * Update description * Remove word custom * Add note regarding `pcntl` requirement for animated spinner. * Remove sample import for consistency with other examples * Update prompts.md --------- Co-authored-by: Jess Archer Co-authored-by: Taylor Otwell --- prompts.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/prompts.md b/prompts.md index 2e23a8e8de5..500f1ab5dcc 100644 --- a/prompts.md +++ b/prompts.md @@ -11,6 +11,7 @@ - [Suggest](#suggest) - [Search](#search) - [Informational Messages](#informational-messages) +- [Spin](#spin) - [Terminal Considerations](#terminal-considerations) - [Unsupported Environments & Fallbacks](#fallbacks) @@ -514,6 +515,23 @@ use function Laravel\Prompts\info; info('Package installed successfully.'); ``` + +### Spin + +The `spin` function displays a spinner along with an optional message while executing a specified callback. It serves to indicate ongoing processes and returns the callback's results upon completion: + +```php +use function Laravel\Prompts\spin; + +$response = spin( + fn () => Http::get('http://example.com/'), + 'Fetching response...' +); +``` + +> **Warning** +> The `spin` function requires the `pcntl` PHP extension to animate the spinner. When this extension is not available, a static version of the spinner will appear instead. + ### Terminal Considerations From f092fa093dec2651ab0d3aa549aa5e3a266e5490 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 20 Sep 2023 09:16:27 +1000 Subject: [PATCH 1118/2609] Document `table` prompt (#9032) --- prompts.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/prompts.md b/prompts.md index 500f1ab5dcc..266f1725ee1 100644 --- a/prompts.md +++ b/prompts.md @@ -11,6 +11,7 @@ - [Suggest](#suggest) - [Search](#search) - [Informational Messages](#informational-messages) +- [Tables](#tables) - [Spin](#spin) - [Terminal Considerations](#terminal-considerations) - [Unsupported Environments & Fallbacks](#fallbacks) @@ -515,6 +516,20 @@ use function Laravel\Prompts\info; info('Package installed successfully.'); ``` + +### Tables + +The `table` function makes it easy to display multiple rows and columns of data. All you need to do is provide the column names and the data for the table: + +```php +use function Laravel\Prompts\table; + +table( + ['Name', 'Email'], + User::all(['name', 'email']) +); +``` + ### Spin From 247e4ad016b465b50573894f28a8d35bebdd0e63 Mon Sep 17 00:00:00 2001 From: Kevin Lin Date: Wed, 20 Sep 2023 21:59:53 +0800 Subject: [PATCH 1119/2609] Update frontend.md (#9040) Update to Inertia v1 syntax. Reference: [Upgrade Guide](https://inertiajs.com/upgrade-guide) --- frontend.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend.md b/frontend.md index 0e5c0a41999..411a5bdad0b 100644 --- a/frontend.md +++ b/frontend.md @@ -149,7 +149,7 @@ An Inertia page corresponds to a Vue or React component, typically stored within ```vue From 666d4042e2e49d2f9bac6f235b482b88d177d119 Mon Sep 17 00:00:00 2001 From: clem Date: Wed, 20 Sep 2023 21:15:20 +0700 Subject: [PATCH 1120/2609] Fix notification custom queue connection example (#9038) * Fix notification custom queue connection example * formatting --------- Co-authored-by: Taylor Otwell --- notifications.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/notifications.md b/notifications.md index 350d504ddb9..526f2be6d7d 100644 --- a/notifications.md +++ b/notifications.md @@ -197,14 +197,28 @@ Alternatively, you may define a `withDelay` method on the notification class its #### 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: +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 call the `onConnection` method from your notification's constructor: - /** - * The name of the queue connection to use when queueing the notification. - * - * @var string - */ - public $connection = 'redis'; + onConnection('redis'); + } + } Or, if you would like to specify a specific queue connection that should be used for each notification channel supported by the notification, you may define a `viaConnections` method on your notification. This method should return an array of channel name / queue connection name pairs: From 9a1cfe18e4c3d3021a9e30bd7d56546122cd5758 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Wed, 20 Sep 2023 11:54:03 -0400 Subject: [PATCH 1121/2609] Remove trailing quotation (#9041) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 80e73c480fe..6ef06ad115e 100644 --- a/vite.md +++ b/vite.md @@ -246,7 +246,7 @@ use Illuminate\Support\Facades\Vite; {{-- ... --}} ``` -> **Warning** +> [!WARNING] > You should only use the `Js::from` method 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. @@ -359,7 +359,7 @@ In addition to conditional statements, Blade provides simple directives for work @endwhile ``` -> **Note** +> [!NOTE] > While iterating through a `foreach` loop, 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 skip the current iteration or end the loop using the `@continue` and `@break` directives: @@ -523,7 +523,7 @@ In addition, the `@required` directive may be used to indicate if a given elemen ### Including Subviews -> **Note** +> [!NOTE] > 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. 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: @@ -564,7 +564,7 @@ To include the first view that exists from a given array of views, you may use t @includeFirst(['custom.admin', 'admin'], ['status' => 'complete']) ``` -> **Warning** +> [!WARNING] > 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. @@ -584,7 +584,7 @@ You may also pass a fourth argument to the `@each` directive. This argument dete @each('view.name', $jobs, 'job', 'view.empty') ``` -> **Warning** +> [!WARNING] > 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. @@ -944,7 +944,7 @@ All of the attributes that are not part of the component's constructor will auto
    ``` -> **Warning** +> [!WARNING] > Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled. @@ -991,7 +991,7 @@ If you need to merge other attributes onto your component, you can chain the `me ``` -> **Note** +> [!NOTE] > If you need to conditionally compile classes on other HTML elements that shouldn't receive merged attributes, you can use the [`@class` directive](#conditional-classes). @@ -1243,7 +1243,7 @@ Sometimes you may need to render a component but not know which component should ### Manually Registering Components -> **Warning** +> [!WARNING] > 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. When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. @@ -1393,7 +1393,7 @@ Because the `color` prop was only passed into the parent (``), it won't
  • ``` -> **Warning** +> [!WARNING] > The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. @@ -1552,7 +1552,7 @@ When defining a child view, use the `@extends` Blade directive to specify which 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. -> **Note** +> [!NOTE] > 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: @@ -1794,7 +1794,7 @@ As you can see, we will chain the `format` method onto whatever expression is pa format('m/d/Y H:i'); ?> -> **Warning** +> [!WARNING] > After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command. diff --git a/broadcasting.md b/broadcasting.md index f62b0569609..51360d1b90c 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -54,7 +54,7 @@ The core concepts behind broadcasting are simple: clients connect to named chann By default, Laravel includes two server-side broadcasting drivers for you to choose from: [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.com). However, community driven packages such as [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) and [soketi](https://docs.soketi.app/) provide additional broadcasting drivers that do not require commercial broadcasting providers. -> **Note** +> [!NOTE] > Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). @@ -115,7 +115,7 @@ The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) and [ ### Ably -> **Note** +> [!NOTE] > The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). If you plan to broadcast your events using [Ably](https://ably.com), you should install the Ably PHP SDK using the Composer package manager: @@ -185,7 +185,7 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run build ``` -> **Note** +> [!NOTE] > To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). @@ -211,7 +211,7 @@ window.Echo = new Echo({ ### Ably -> **Note** +> [!NOTE] > The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). [Laravel Echo](https://github.com/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. @@ -250,7 +250,7 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run dev ``` -> **Note** +> [!NOTE] > To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). @@ -260,7 +260,7 @@ Laravel's event broadcasting allows you to broadcast your server-side Laravel ev 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. -> **Note** +> [!NOTE] > If you would like to explore open source alternatives to Pusher, check out the [open source alternatives](#open-source-alternatives). @@ -522,7 +522,7 @@ If your queue connection's `after_commit` configuration option is set to `false` use SerializesModels; } -> **Note** +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -614,7 +614,7 @@ Just like HTTP routes, channel routes may also take advantage of implicit and ex return $user->id === $order->user_id; }); -> **Warning** +> [!WARNING] > 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. @@ -669,7 +669,7 @@ Finally, you may place the authorization logic for your channel in the channel c } } -> **Note** +> [!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. @@ -701,7 +701,7 @@ axios.post('/task', task) 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. -> **Warning** +> [!WARNING] > Your event must use the `Illuminate\Broadcasting\InteractsWithSockets` trait in order to call the `toOthers` method. @@ -902,7 +902,7 @@ Echo.join(`chat.${roomId}`) ## Model Broadcasting -> **Warning** +> [!WARNING] > Before reading the following documentation about model broadcasting, we recommend you become familiar with the general concepts of Laravel's model broadcasting services as well as how to manually create and listen to broadcast events. It is common to broadcast events when your application's [Eloquent models](/docs/{{version}}/eloquent) are created, updated, or deleted. Of course, this can easily be accomplished by manually [defining custom events for Eloquent model state changes](/docs/{{version}}/eloquent#events) and marking those events with the `ShouldBroadcast` interface. @@ -1091,7 +1091,7 @@ Echo.private(`App.Models.User.${this.user.id}`) ## Client Events -> **Note** +> [!NOTE] > When using [Pusher Channels](https://pusher.com/channels), you must enable the "Client Events" option in the "App Settings" section of your [application dashboard](https://dashboard.pusher.com/) in order to send client events. Sometimes you may wish to broadcast an event to other connected clients without hitting your Laravel application at all. This can be particularly useful for things like "typing" notifications, where you want to alert users of your application that another user is typing a message on a given screen. diff --git a/cache.md b/cache.md index e64a3e17875..95db89b7a3d 100644 --- a/cache.md +++ b/cache.md @@ -46,7 +46,7 @@ When using the `database` cache driver, you will need to set up a table to conta $table->integer('expiration'); }); -> **Note** +> [!NOTE] > You may also use the `php artisan cache:table` Artisan command to generate a migration with the proper schema. @@ -217,7 +217,7 @@ The `forever` method may be used to store an item in the cache permanently. Sinc Cache::forever('key', 'value'); -> **Note** +> [!NOTE] > If you are using the Memcached driver, items that are stored "forever" may be removed when the cache reaches its size limit. @@ -237,7 +237,7 @@ You may clear the entire cache using the `flush` method: Cache::flush(); -> **Warning** +> [!WARNING] > 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. @@ -259,13 +259,13 @@ When the `cache` function is called without any arguments, it returns an instanc return DB::table('users')->get(); }); -> **Note** +> [!NOTE] > 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). ## Atomic Locks -> **Warning** +> [!WARNING] > To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. @@ -282,7 +282,7 @@ When using the `database` cache driver, you will need to setup a table to contai $table->integer('expiration'); }); -> **Note** +> [!NOTE] > If you used the `cache:table` Artisan command to create the database driver's cache table, the migration created by that command already includes a definition for the `cache_locks` table. @@ -385,7 +385,7 @@ We just need to implement each of these methods using a MongoDB connection. For return Cache::repository(new MongoStore); }); -> **Note** +> [!NOTE] > If you're wondering where to put your custom cache driver code, you could create an `Extensions` namespace within your `app` directory. However, keep in mind that Laravel does not have a rigid application structure and you are free to organize your application according to your preferences. diff --git a/cashier-paddle.md b/cashier-paddle.md index 4971fa2cf0e..381016b9be8 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -50,7 +50,7 @@ ## Introduction -> **Warning** +> [!WARNING] > This documentation is for Cashier Paddle 2.x's integration with Paddle Billing. If you're still using Paddle Classic, you should use [Cashier Paddle 1.x](https://github.com/laravel/cashier-paddle/tree/1.x). [Laravel Cashier Paddle](https://github.com/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: swapping subscriptions, subscription "quantities", subscription pausing, cancelation grace periods, and more. @@ -83,7 +83,7 @@ Then, you should run your application's database migrations. The Cashier migrati php artisan migrate ``` -> **Warning** +> [!WARNING] > To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks). @@ -163,7 +163,7 @@ You can specify a locale to be used when formatting money values for display on CASHIER_CURRENCY_LOCALE=nl_BE ``` -> **Warning** +> [!WARNING] > In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. @@ -231,7 +231,7 @@ By default, this will display the widget using Paddle's default styling. You can The Paddle checkout widget is asynchronous. Once the user creates a subscription within the widget, Paddle will send your application a webhook so that you may properly update the subscription state in your application's database. Therefore, it's important that you properly [set up webhooks](#handling-paddle-webhooks) to accommodate for state changes from Paddle. -> **Warning** +> [!WARNING] > 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. @@ -598,7 +598,7 @@ If you would like subscriptions to still be considered valid when they are `past Cashier::keepPastDueSubscriptionsActive(); } -> **Warning** +> [!WARNING] > 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. @@ -764,7 +764,7 @@ You may remove prices from subscriptions using the `swap` method and omitting th $user->subscription()->swap(['price_original' => 2]); -> **Warning** +> [!WARNING] > You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. @@ -821,7 +821,7 @@ To resume a paused subscription, you may invoke the `resume` method on the subsc $user->subscription()->resume(); -> **Warning** +> [!WARNING] > A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. @@ -847,7 +847,7 @@ To stop a subscription on its grace period from canceling, you may invoke the `s $user->subscription()->stopCancelation(); -> **Warning** +> [!WARNING] > Paddle's subscriptions cannot be resumed after cancelation. If your customer wishes to resume their subscription, they will have to create a new subscription. @@ -869,7 +869,7 @@ If you would like to offer trial periods to your customers while still collectin When your application receives the `subscription_created` event, Cashier 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. -> **Warning** +> [!WARNING] > If the customer's subscription is not canceled 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 are equivalent: @@ -973,7 +973,7 @@ To ensure your application can handle Paddle webhooks, be sure to [configure the - Subscription Paused - Subscription Canceled -> **Warning** +> [!WARNING] > Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. @@ -1120,7 +1120,7 @@ The example above refunds specific line items in a transaction. If you want to r For more information on refunds, please consult [Paddle's refund documentation](https://developer.paddle.com/build/transactions/create-transaction-adjustments). -> **Warning** +> [!WARNING] > Refunds must always be approved by Paddle before fully processing. @@ -1135,7 +1135,7 @@ Just like refunding, you can also credit transactions. Crediting transactions wi For more info, [see Paddle's documentation on crediting](https://developer.paddle.com/build/transactions/create-transaction-adjustments). -> **Warning** +> [!WARNING] > Credits can only be applied for manually-collected transactions. Automatically-collected transactions are credited by Paddle themselves. diff --git a/collections.md b/collections.md index 904b37da112..d81b0194484 100644 --- a/collections.md +++ b/collections.md @@ -31,7 +31,7 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle $collection = collect([1, 2, 3]); -> **Note** +> [!NOTE] > The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. @@ -374,7 +374,7 @@ The `collect` method is primarily useful for converting [lazy collections](#lazy // [1, 2, 3] -> **Note** +> [!NOTE] > The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. @@ -467,7 +467,7 @@ The `containsOneItem` method determines whether the collection contains a single This method has the same signature as the [`contains`](#method-contains) method; however, all values are compared using "strict" comparisons. -> **Note** +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). @@ -578,7 +578,7 @@ The `diff` method compares the collection against another collection or a plain // [1, 3, 5] -> **Note** +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). @@ -798,7 +798,7 @@ Primitive types such as `string`, `int`, `float`, `bool`, and `array` may also b return $collection->ensure('int'); -> **Warning** +> [!WARNING] > The `ensure` method does not guarantee that elements of different types will not be added to the collection at a later time. @@ -837,7 +837,7 @@ The `except` method returns all items in the collection except for those with th For the inverse of `except`, see the [only](#method-only) method. -> **Note** +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). @@ -1020,7 +1020,7 @@ The `forget` method removes an item from the collection by its key: // ['framework' => 'laravel'] -> **Warning** +> [!WARNING] > Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. @@ -1223,7 +1223,7 @@ The `intersect` method removes any values from the original collection that are // [0 => 'Desk', 2 => 'Chair'] -> **Note** +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). @@ -1412,7 +1412,7 @@ The `map` method iterates through the collection and passes each value to the gi // [2, 4, 6, 8, 10] -> **Warning** +> [!WARNING] > Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. @@ -1669,7 +1669,7 @@ The `only` method returns the items in the collection with the specified keys: For the inverse of `only`, see the [except](#method-except) method. -> **Note** +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). @@ -2220,7 +2220,7 @@ You may also pass a simple value to the `skipUntil` method to skip all items unt // [3, 4] -> **Warning** +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. @@ -2238,7 +2238,7 @@ The `skipWhile` method skips over items from the collection while the given call // [4] -> **Warning** +> [!WARNING] > If the callback never returns `false`, the `skipWhile` method will return an empty collection. @@ -2347,7 +2347,7 @@ The `sort` method sorts the collection. The sorted collection keeps the original If your sorting needs are more advanced, you may pass a callback to `sort` with your own algorithm. Refer to the PHP documentation on [`uasort`](https://secure.php.net/manual/en/function.uasort.php#refsect1-function.uasort-parameters), which is what the collection's `sort` method calls utilizes internally. -> **Note** +> [!NOTE] > If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. @@ -2691,7 +2691,7 @@ You may also pass a simple value to the `takeUntil` method to get the items unti // [1, 2] -> **Warning** +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. @@ -2709,7 +2709,7 @@ The `takeWhile` method returns items in the collection until the given callback // [1, 2] -> **Warning** +> [!WARNING] > If the callback never returns `false`, the `takeWhile` method will return all items in the collection. @@ -2754,7 +2754,7 @@ The `toArray` method converts the collection into a plain PHP `array`. If the co ] */ -> **Warning** +> [!WARNING] > `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw array underlying the collection, use the [`all`](#method-all) method instead. @@ -2783,7 +2783,7 @@ The `transform` method iterates over the collection and calls the given callback // [2, 4, 6, 8, 10] -> **Warning** +> [!WARNING] > Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. @@ -2887,7 +2887,7 @@ Finally, you may also pass your own closure to the `unique` method to specify wh The `unique` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`uniqueStrict`](#method-uniquestrict) method to filter using "strict" comparisons. -> **Note** +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). @@ -3396,7 +3396,7 @@ Likewise, we can use the `sum` higher order message to gather the total number o ### Introduction -> **Warning** +> [!WARNING] > Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). To supplement the already powerful `Collection` class, the `LazyCollection` class leverages PHP's [generators](https://www.php.net/manual/en/language.generators.overview.php) to allow you to work with very large datasets while keeping memory usage low. @@ -3587,7 +3587,7 @@ Almost all methods available on the `Collection` class are also available on the
    -> **Warning** +> [!WARNING] > Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. diff --git a/configuration.md b/configuration.md index 68276acfd1b..b2760786433 100644 --- a/configuration.md +++ b/configuration.md @@ -50,7 +50,7 @@ Laravel's default `.env` file contains some common configuration values that may If you are developing with a team, you may wish to continue including a `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. -> **Note** +> [!NOTE] > Any variable in your `.env` file can be overridden by external environment variables such as server-level or system-level environment variables. @@ -115,7 +115,7 @@ You may also pass arguments to the `environment` method to determine if the envi // The environment is either local OR staging... } -> **Note** +> [!NOTE] > The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. @@ -138,7 +138,7 @@ Running the `env:encrypt` command will encrypt your `.env` file and place the en php artisan env:encrypt --key=3UVsEgGVK36XN82KKeyLFMhvosbZN1aF ``` -> **Note** +> [!NOTE] > The length of the key provided should match the key length required by the encryption cipher being used. By default, Laravel will use the `AES-256-CBC` cipher which requires a 32 character key. You are free to use any cipher supported by Laravel's [encrypter](/docs/{{version}}/encryption) by passing the `--cipher` option when invoking the command. If your application has multiple environment files, such as `.env` and `.env.staging`, you may specify the environment file that should be encrypted by providing the environment name via the `--env` option: @@ -213,7 +213,7 @@ The `config:clear` command may be used to purge the cached configuration: php artisan config:clear ``` -> **Warning** +> [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. @@ -221,7 +221,7 @@ php artisan config:clear The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. -> **Warning** +> [!WARNING] > For local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** @@ -270,7 +270,7 @@ php artisan down --with-secret When accessing this hidden route, you will then be redirected to the `/` route of the application. Once the cookie has been issued to your browser, you will be able to browse the application normally as if it was not in maintenance mode. -> **Note** +> [!NOTE] > Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?` or `&`. @@ -302,7 +302,7 @@ To disable maintenance mode, use the `up` command: php artisan up ``` -> **Note** +> [!NOTE] > You may customize the default maintenance mode template by defining your own template at `resources/views/errors/503.blade.php`. diff --git a/container.md b/container.md index 7b329ad3c0c..fbe5fa2c496 100644 --- a/container.md +++ b/container.md @@ -134,7 +134,7 @@ $this->app->bindIf(Transistor::class, function (Application $app) { }); ``` -> **Note** +> [!NOTE] > There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection. diff --git a/controllers.md b/controllers.md index bac7ea6eb96..28e87f27b8d 100644 --- a/controllers.md +++ b/controllers.md @@ -63,7 +63,7 @@ Once you have written a controller class and method, you may define a route to t 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. -> **Note** +> [!NOTE] > Controllers are not **required** to extend a base class. However, you will not have access to convenient features such as the `middleware` and `authorize` methods. @@ -98,7 +98,7 @@ You may generate an invokable controller by using the `--invokable` option of th php artisan make:controller ProvisionServer --invokable ``` -> **Note** +> [!NOTE] > Controller stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). @@ -372,7 +372,7 @@ If you need to add additional routes to a resource controller beyond the default Route::get('/photos/popular', [PhotoController::class, 'popular']); Route::resource('photos', PhotoController::class); -> **Note** +> [!NOTE] > Remember to keep your controllers focused. If you find yourself routinely needing methods outside of the typical set of resource actions, consider splitting your controller into two, smaller controllers. diff --git a/csrf.md b/csrf.md index 3ae54385b8b..19aa3f04788 100644 --- a/csrf.md +++ b/csrf.md @@ -94,7 +94,7 @@ Typically, you should place these kinds of routes outside of the `web` middlewar ]; } -> **Note** +> [!NOTE] > For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). @@ -123,5 +123,5 @@ Laravel stores the current CSRF token in an encrypted `XSRF-TOKEN` cookie that i This cookie is primarily sent as a developer convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the `X-XSRF-TOKEN` header on same-origin requests. -> **Note** +> [!NOTE] > By default, the `resources/js/bootstrap.js` file includes the Axios HTTP library which will automatically send the `X-XSRF-TOKEN` header for you. diff --git a/database.md b/database.md index e3971d7307d..0a5664abfca 100644 --- a/database.md +++ b/database.md @@ -224,7 +224,7 @@ Sometimes you may want to execute an SQL statement without binding any values. Y DB::unprepared('update users set votes = 100 where name = "Dries"'); -> **Warning** +> [!WARNING] > Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement. @@ -363,7 +363,7 @@ Lastly, you can commit a transaction via the `commit` method: DB::commit(); -> **Note** +> [!NOTE] > The `DB` facade's transaction methods control the transactions for both the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent). diff --git a/deployment.md b/deployment.md index b4e6e80b9bd..cbbfb822744 100644 --- a/deployment.md +++ b/deployment.md @@ -99,7 +99,7 @@ When deploying to production, make sure that you are optimizing Composer's class composer install --optimize-autoloader --no-dev ``` -> **Note** +> [!NOTE] > In addition to optimizing the autoloader, you should always be sure to include a `composer.lock` file in your project's source control repository. Your project's dependencies can be installed much faster when a `composer.lock` file is present. @@ -113,7 +113,7 @@ php artisan config:cache This command will combine all of Laravel's configuration files into a single, cached file, which greatly reduces the number of trips the framework must make to the filesystem when loading your configuration values. -> **Warning** +> [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function for `.env` variables will return `null`. @@ -152,7 +152,7 @@ This command precompiles all your Blade views so they are not compiled on demand The debug option in your config/app.php configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your application's `.env` file. -> **Warning** +> [!WARNING] > **In your production environment, this value should always be `false`. If the `APP_DEBUG` variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** @@ -165,7 +165,7 @@ If you aren't quite ready to manage your own server configuration or aren't comf Laravel Forge can create servers on various infrastructure providers such as DigitalOcean, Linode, AWS, and more. In addition, Forge installs and manages all of the tools needed to build robust Laravel applications, such as Nginx, MySQL, Redis, Memcached, Beanstalk, and more. -> **Note** +> [!NOTE] > Want a full guide to deploying with Laravel Forge? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com/deploying) and the Forge [video series available on Laracasts](https://laracasts.com/series/learn-laravel-forge-2022-edition). diff --git a/dusk.md b/dusk.md index 9dabaf0871d..1ee5ba5be73 100644 --- a/dusk.md +++ b/dusk.md @@ -64,7 +64,7 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome composer require --dev laravel/dusk ``` -> **Warning** +> [!WARNING] > If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operating system: @@ -75,7 +75,7 @@ php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. -> **Note** +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -97,7 +97,7 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> **Warning** +> [!WARNING] > Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. @@ -167,7 +167,7 @@ The `DatabaseMigrations` trait will run your database migrations before each tes use DatabaseMigrations; } -> **Warning** +> [!WARNING] > SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. @@ -261,7 +261,7 @@ The `dusk` command accepts any argument that is normally accepted by the PHPUnit php artisan dusk --group=foo ``` -> **Note** +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -455,7 +455,7 @@ Often, you will be testing pages that require authentication. You can use Dusk's ->visit('/home'); }); -> **Warning** +> [!WARNING] > After using the `loginAs` method, the user session will be maintained for all tests within the file. @@ -652,7 +652,7 @@ The `attach` method may be used to attach a file to a `file` input element. Like $browser->attach('photo', __DIR__.'/photos/mountains.png'); -> **Warning** +> [!WARNING] > The attach function requires the `Zip` PHP extension to be installed and enabled on your server. @@ -683,7 +683,7 @@ You may use the `seeLink` method to determine if a link with the given display t // ... } -> **Warning** +> [!WARNING] > These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. @@ -697,7 +697,7 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" $browser->keys('.app', ['{command}', 'j']); -> **Note** +> [!NOTE] > All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). @@ -1997,7 +1997,7 @@ Once the component has been defined, we can easily select a date within the date ## Continuous Integration -> **Warning** +> [!WARNING] > Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. diff --git a/eloquent-factories.md b/eloquent-factories.md index 5c5de4c4fdd..4b5885cdb87 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -52,7 +52,7 @@ As you can see, in their most basic form, factories are classes that extend Lara Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing and seeding. -> **Note** +> [!NOTE] > You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. @@ -218,7 +218,7 @@ Alternatively, the `state` method may be called directly on the factory instance 'name' => 'Abigail Otwell', ])->make(); -> **Note** +> [!NOTE] > [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. diff --git a/eloquent-mutators.md b/eloquent-mutators.md index b915dc5c04a..d53e1e940e9 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -62,7 +62,7 @@ As you can see, the original value of the column is passed to the accessor, allo $firstName = $user->first_name; -> **Note** +> [!NOTE] > If you would like these computed values to be added to the array / JSON representations of your model, [you will need to append them](/docs/{{version}}/eloquent-serialization#appending-values-to-json). @@ -261,7 +261,7 @@ If you need to add a new, temporary cast at runtime, you may use the `mergeCasts 'options' => 'object', ]); -> **Warning** +> [!WARNING] > Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship or assign a cast to the model's primary key. @@ -624,7 +624,7 @@ When casting to value objects, any changes made to the value object will automat $user->save(); -> **Note** +> [!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. diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 182e00c22d6..fb63b801882 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -348,7 +348,7 @@ public function largestOrder(): HasOne } ``` -> **Warning** +> [!WARNING] > Because PostgreSQL does not support executing the `MAX` function against UUID columns, it is not currently possible to use one-of-many relationships in combination with PostgreSQL UUID columns. @@ -682,7 +682,7 @@ If you would like your intermediate table to have `created_at` and `updated_at` return $this->belongsToMany(Role::class)->withTimestamps(); -> **Warning** +> [!WARNING] > Intermediate tables that utilize Eloquent's automatically maintained timestamps are required to have both `created_at` and `updated_at` timestamp columns. @@ -781,7 +781,7 @@ When defining the `RoleUser` model, you should extend the `Illuminate\Database\E // ... } -> **Warning** +> [!WARNING] > Pivot models may not use the `SoftDeletes` trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model. @@ -1049,7 +1049,7 @@ public function bestImage(): MorphOne } ``` -> **Note** +> [!NOTE] > It is possible to construct more advanced "one of many" relationships. For more information, please consult the [has one of many documentation](#advanced-has-one-of-many-relationships). @@ -1077,7 +1077,7 @@ Many-to-many polymorphic relations are slightly more complicated than "morph one taggable_id - integer taggable_type - string -> **Note** +> [!NOTE] > Before diving into polymorphic many-to-many relationships, you may benefit from reading the documentation on typical [many-to-many relationships](#many-to-many). @@ -1189,7 +1189,7 @@ You may determine the morph alias of a given model at runtime using the model's $class = Relation::getMorphedModel($alias); -> **Warning** +> [!WARNING] > When adding a "morph map" to your existing application, every morphable `*_type` column value in your database that still contains a fully-qualified class will need to be converted to its "map" name. @@ -1206,7 +1206,7 @@ The `resolveRelationUsing` method accepts the desired relationship name as its f return $orderModel->belongsTo(Customer::class, 'customer_id'); }); -> **Warning** +> [!WARNING] > When defining dynamic relationships, always provide explicit key name arguments to the Eloquent relationship methods. @@ -1330,7 +1330,7 @@ If you need even more power, you may use the `whereHas` and `orWhereHas` methods $query->where('content', 'like', 'code%'); }, '>=', 10)->get(); -> **Warning** +> [!WARNING] > Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. @@ -1672,7 +1672,7 @@ You may not always need every column from the relationships you are retrieving. $books = Book::with('author:id,name,book_id')->get(); -> **Warning** +> [!WARNING] > When using this feature, you should always include the `id` column and any relevant foreign key columns in the list of columns you wish to retrieve. @@ -1739,7 +1739,7 @@ In this example, Eloquent will only eager load posts where the post's `title` co $query->orderBy('created_at', 'desc'); }])->get(); -> **Warning** +> [!WARNING] > The `limit` and `take` query builder methods may not be used when constraining eager loads. @@ -1952,7 +1952,7 @@ The `createQuietly` and `createManyQuietly` methods may be used to create a mode You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods to [create and update models on relationships](/docs/{{version}}/eloquent#upserts). -> **Note** +> [!NOTE] > Before using the `create` method, be sure to review the [mass assignment](/docs/{{version}}/eloquent#mass-assignment) documentation. @@ -2087,5 +2087,5 @@ For example, when a `Comment` model is updated, you may want to automatically "t } } -> **Warning** +> [!WARNING] > Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. diff --git a/eloquent-resources.md b/eloquent-resources.md index 2e4d7d191f2..05d38e812e0 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -44,7 +44,7 @@ php artisan make:resource UserCollection ## Concept Overview -> **Note** +> [!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: @@ -196,7 +196,7 @@ For example, `UserCollection` will attempt to map the given user instances into ## Writing Resources -> **Note** +> [!NOTE] > If you have not read the [concept overview](#concept-overview), you are highly encouraged to do so before proceeding with this documentation. Resources only need to transform a given model into an array. So, each resource contains a `toArray` method which translates your model's attributes into an API friendly array that can be returned from your application's routes or controllers: @@ -261,7 +261,7 @@ If you would like to include related resources in your response, you may add the ]; } -> **Note** +> [!NOTE] > If you would like to include relationships only when they have already been loaded, check out the documentation on [conditional relationships](#conditional-relationships). @@ -380,7 +380,7 @@ If you would like to disable the wrapping of the outermost resource, you should } } -> **Warning** +> [!WARNING] > The `withoutWrapping` method only affects the outermost response and will not remove `data` keys that you manually add to your own resource collections. @@ -577,7 +577,7 @@ Sometimes you may have several attributes that should only be included in the re Again, if the given condition is `false`, these attributes will be removed from the resource response before it is sent to the client. -> **Warning** +> [!WARNING] > The `mergeWhen` method should not be used within arrays that mix string and numeric keys. Furthermore, it should not be used within arrays with numeric keys that are not ordered sequentially. diff --git a/eloquent-serialization.md b/eloquent-serialization.md index c1526922e4b..4192403d37e 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -13,7 +13,7 @@ When building APIs using Laravel, you will often need to convert your models and relationships to arrays or JSON. Eloquent includes convenient methods for making these conversions, as well as controlling which attributes are included in the serialized representation of your models. -> **Note** +> [!NOTE] > For an even more robust way of handling Eloquent model and collection JSON serialization, check out the documentation on [Eloquent API resources](/docs/{{version}}/eloquent-resources). @@ -91,7 +91,7 @@ Sometimes you may wish to limit the attributes, such as passwords, that are incl protected $hidden = ['password']; } -> **Note** +> [!NOTE] > To hide relationships, add the relationship's method name to your Eloquent model's `$hidden` property. Alternatively, you may use the `visible` property to define an "allow list" of attributes that should be included in your model's array and JSON representation. All attributes that are not present in the `$visible` array will be hidden when the model is converted to an array or JSON: diff --git a/eloquent.md b/eloquent.md index ca0f0505702..a7a62eee992 100644 --- a/eloquent.md +++ b/eloquent.md @@ -43,7 +43,7 @@ Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Eloquent, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well. -> **Note** +> [!NOTE] > Before getting started, be sure to configure a database connection in your application's `config/database.php` configuration file. For more information on configuring your database, check out [the database configuration documentation](/docs/{{version}}/database#configuration). #### Laravel Bootcamp @@ -405,7 +405,7 @@ The Eloquent `all` method will return all of the results in the model's table. H ->take(10) ->get(); -> **Note** +> [!NOTE] > Since Eloquent models are query builders, you should review all of the methods provided by Laravel's [query builder](/docs/{{version}}/queries). You may use any of these methods when writing your Eloquent queries. @@ -511,7 +511,7 @@ Similar to the `lazy` method, the `cursor` method may be used to significantly r The `cursor` method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor. -> **Warning** +> [!WARNING] > Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality: @@ -726,7 +726,7 @@ Updates can also be performed against models that match a given query. In this e The `update` method expects an array of column and value pairs representing the columns that should be updated. The `update` method returns the number of affected rows. -> **Warning** +> [!WARNING] > When issuing a mass update via Eloquent, the `saving`, `saved`, `updating`, and `updated` model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. @@ -895,7 +895,7 @@ If you would like to perform multiple "upserts" in a single query, then you shou ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] ], ['departure', 'destination'], ['price']); -> **Warning** +> [!WARNING] > All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. @@ -926,7 +926,7 @@ In the example above, we are retrieving the model from the database before calli Flight::destroy(collect([1, 2, 3])); -> **Warning** +> [!WARNING] > The `destroy` method loads each model individually and calls the `delete` method so that the `deleting` and `deleted` events are properly dispatched for each model. @@ -936,7 +936,7 @@ Of course, you may build an Eloquent query to delete all models matching your qu $deleted = Flight::where('active', 0)->delete(); -> **Warning** +> [!WARNING] > When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement. @@ -956,7 +956,7 @@ In addition to actually removing records from your database, Eloquent can also " use SoftDeletes; } -> **Note** +> [!NOTE] > The `SoftDeletes` trait will automatically cast the `deleted_at` attribute to a `DateTime` / `Carbon` instance for you. You should also add the `deleted_at` column to your database table. The Laravel [schema builder](/docs/{{version}}/migrations) contains a helper method to create this column: @@ -1099,7 +1099,7 @@ You may test your `prunable` query by executing the `model:prune` command with t php artisan model:prune --pretend ``` -> **Warning** +> [!WARNING] > Soft deleting models will be permanently deleted (`forceDelete`) if they match the prunable query. @@ -1204,7 +1204,7 @@ Writing a global scope is simple. First, use the `make:scope` command to generat } } -> **Note** +> [!NOTE] > If your global scope is adding columns to the select clause of the query, you should use the `addSelect` method instead of `select`. This will prevent the unintentional replacement of the query's existing select clause. @@ -1383,7 +1383,7 @@ The `is` and `isNot` methods are also available when using the `belongsTo`, `has ## Events -> **Note** +> [!NOTE] > Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `trashed`, `forceDeleting`, `forceDeleted`, `restoring`, `restored`, and `replicating`. @@ -1418,7 +1418,7 @@ To start listening to model events, define a `$dispatchesEvents` property on you After defining and mapping your Eloquent events, you may use [event listeners](/docs/{{version}}/events#defining-listeners) to handle the events. -> **Warning** +> [!WARNING] > When issuing a mass update or delete query via Eloquent, the `saved`, `updated`, `deleting`, and `deleted` model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing mass updates or deletes. @@ -1543,7 +1543,7 @@ Alternatively, you may list your observers within an `$observers` property of yo User::class => [UserObserver::class], ]; -> **Note** +> [!NOTE] > There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. diff --git a/errors.md b/errors.md index 6f37e336a40..3a8baabeb29 100644 --- a/errors.md +++ b/errors.md @@ -56,7 +56,7 @@ When you register a custom exception reporting callback using the `reportable` m return false; }); -> **Note** +> [!NOTE] > To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). @@ -314,7 +314,7 @@ If your exception contains custom reporting logic that is only necessary when ce return false; } -> **Note** +> [!NOTE] > You may type-hint any required dependencies of the `report` method and they will automatically be injected into the method by Laravel's [service container](/docs/{{version}}/container). diff --git a/events.md b/events.md index a350dbaa1c6..68240d8f066 100644 --- a/events.md +++ b/events.md @@ -46,7 +46,7 @@ The `App\Providers\EventServiceProvider` included with your Laravel application ], ]; -> **Note** +> [!NOTE] > The `event:list` command may be used to display a list of all events and listeners registered by your application. @@ -244,7 +244,7 @@ Next, let's take a look at the listener for our example event. Event listeners r } } -> **Note** +> [!NOTE] > Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. @@ -414,7 +414,7 @@ If your queue connection's `after_commit` configuration option is set to `false` use InteractsWithQueue; } -> **Note** +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -529,7 +529,7 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th OrderShipped::dispatchUnless($condition, $order); -> **Note** +> [!NOTE] > When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) make it a cinch. @@ -729,7 +729,7 @@ If you would simply like to assert that an event listener is listening to a give SendShipmentNotification::class ); -> **Warning** +> [!WARNING] > 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. diff --git a/filesystem.md b/filesystem.md index 9ed0102a9bd..6370658687e 100644 --- a/filesystem.md +++ b/filesystem.md @@ -37,7 +37,7 @@ Laravel's filesystem configuration file is located at `config/filesystems.php`. The `local` driver interacts with files stored locally on the server running the Laravel application while the `s3` driver is used to write to Amazon's S3 cloud storage service. -> **Note** +> [!NOTE] > You may configure as many disks as you like and may even have multiple disks that use the same driver. @@ -202,7 +202,7 @@ In order for Laravel's Flysystem integration to generate proper URLs when using AWS_URL=http://localhost:9000/local ``` -> **Warning** +> [!WARNING] > Generating temporary storage URLs via the `temporaryUrl` method is not supported when using MinIO. @@ -277,7 +277,7 @@ You may use the `url` method to get the URL for a given file. If you are using t When using the `local` driver, all files that should be publicly accessible should be placed in the `storage/app/public` directory. Furthermore, you should [create a symbolic link](#the-public-disk) at `public/storage` which points to the `storage/app/public` directory. -> **Warning** +> [!WARNING] > When using the `local` driver, the return value of `url` is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs. @@ -347,7 +347,7 @@ If you need to customize how temporary URLs are created for a specific storage d #### Temporary Upload URLs -> **Warning** +> [!WARNING] > The ability to generate temporary upload URLs is only supported by the `s3` driver. If you need to generate a temporary URL that can be used to upload a file directly from your client-side application, you may use the `temporaryUploadUrl` method. This method accepts a path and a `DateTime` instance specifying when the URL should expire. The `temporaryUploadUrl` method returns an associative array which may be destructured into the upload URL and the headers that should be included with the upload request: @@ -498,7 +498,7 @@ You may also use the `putFileAs` method on the `Storage` facade, which will perf 'avatars', $request->file('avatar'), $request->user()->id ); -> **Warning** +> [!WARNING] > Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. @@ -675,7 +675,7 @@ The `Storage` facade's `fake` method allows you to easily generate a fake disk t 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. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). -> **Warning** +> [!WARNING] > The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). diff --git a/fortify.md b/fortify.md index c5523dd0179..6b61a4a180c 100644 --- a/fortify.md +++ b/fortify.md @@ -32,7 +32,7 @@ Since Fortify does not provide its own user interface, it is meant to be paired with your own user interface which makes requests to the routes it registers. We will discuss exactly how to make requests to these routes in the remainder of this documentation. -> **Note** +> [!NOTE] > Remember, Fortify is a package that is meant to give you a head start implementing Laravel's authentication features. **You are not required to use it.** You are always free to manually interact with Laravel's authentication services by following the documentation available in the [authentication](/docs/{{version}}/authentication), [password reset](/docs/{{version}}/passwords), and [email verification](/docs/{{version}}/verification) documentation. diff --git a/frontend.md b/frontend.md index 829aca84277..638a5b468b5 100644 --- a/frontend.md +++ b/frontend.md @@ -191,5 +191,5 @@ By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. V The fastest way to get started with Laravel and Vite is by beginning your application's development using [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), our simplest starter kit that jump-starts your application by providing frontend and backend authentication scaffolding. -> **Note** +> [!NOTE] > For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). diff --git a/hashing.md b/hashing.md index c59df003af3..2c0320f201a 100644 --- a/hashing.md +++ b/hashing.md @@ -72,7 +72,7 @@ If you are using the Argon2 algorithm, the `make` method allows you to manage th 'threads' => 2, ]); -> **Note** +> [!NOTE] > For more information on these options, please refer to the [official PHP documentation regarding Argon hashing](https://secure.php.net/manual/en/function.password-hash.php). diff --git a/helpers.md b/helpers.md index d3c3c282202..cbd6f55aa6d 100644 --- a/helpers.md +++ b/helpers.md @@ -1268,7 +1268,7 @@ The `lang_path` function returns the fully qualified path to your application's $path = lang_path('en/messages.php'); -> **Note** +> [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -1614,7 +1614,7 @@ The `env` function retrieves the value of an [environment variable](/docs/{{vers $env = env('APP_ENV', 'production'); -> **Warning** +> [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. diff --git a/homestead.md b/homestead.md index 4abf1cda49a..e27086e85e8 100644 --- a/homestead.md +++ b/homestead.md @@ -42,7 +42,7 @@ Laravel strives to make the entire PHP development experience delightful, includ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, MySQL, PostgreSQL, Redis, Memcached, Node, and all of the other software you need to develop amazing Laravel applications. -> **Warning** +> [!WARNING] > If you are using Windows, you may need to enable hardware virtualization (VT-x). It can usually be enabled via your BIOS. If you are using Hyper-V on a UEFI system you may additionally need to disable Hyper-V in order to access VT-x. @@ -189,7 +189,7 @@ The `provider` key in your `Homestead.yaml` file indicates which Vagrant provide provider: virtualbox -> **Warning** +> [!WARNING] > If you are using Apple Silicon the Parallels provider is required. @@ -203,7 +203,7 @@ folders: to: /home/vagrant/project1 ``` -> **Warning** +> [!WARNING] > Windows users should not use the `~/` path syntax and instead should use the full path to their project, such as `C:\Users\user\Code\project1`. You should always map individual applications to their own folder mapping instead of mapping a single large directory that contains all of your applications. When you map a folder, the virtual machine must keep track of all disk IO for *every* file in the folder. You may experience reduced performance if you have a large number of files in a folder: @@ -216,7 +216,7 @@ folders: to: /home/vagrant/project2 ``` -> **Warning** +> [!WARNING] > You should never mount `.` (the current directory) when using Homestead. This causes Vagrant to not map the current folder to `/vagrant` and will break optional features and cause unexpected results while provisioning. To enable [NFS](https://developer.hashicorp.com/vagrant/docs/synced-folders/nfs), you may add a `type` option to your folder mapping: @@ -228,7 +228,7 @@ folders: type: "nfs" ``` -> **Warning** +> [!WARNING] > When using NFS on Windows, you should consider installing the [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd) plug-in. This plug-in will maintain the correct user / group permissions for files and directories within the Homestead virtual machine. You may also pass any options supported by Vagrant's [Synced Folders](https://developer.hashicorp.com/vagrant/docs/synced-folders/basic_usage) by listing them under the `options` key: @@ -256,7 +256,7 @@ sites: If you change the `sites` property after provisioning the Homestead virtual machine, you should execute the `vagrant reload --provision` command in your terminal to update the Nginx configuration on the virtual machine. -> **Warning** +> [!WARNING] > Homestead scripts are built to be as idempotent as possible. However, if you are experiencing issues while provisioning you should destroy and rebuild the machine by executing the `vagrant destroy && vagrant up` command. @@ -372,7 +372,7 @@ features: You may specify a supported version of Elasticsearch, which must be an exact version number (major.minor.patch). The default installation will create a cluster named 'homestead'. You should never give Elasticsearch more than half of the operating system's memory, so make sure your Homestead virtual machine has at least twice the Elasticsearch allocation. -> **Note** +> [!NOTE] > Check out the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current) to learn how to customize your configuration. @@ -470,7 +470,7 @@ sites: to: /home/vagrant/project2/public ``` -> **Warning** +> [!WARNING] > You should ensure that you have configured a [folder mapping](#configuring-shared-folders) for the project's directory before adding the site. If Vagrant is not automatically managing your "hosts" file, you may need to add the new site to that file as well. On macOS and Linux, this file is located at `/etc/hosts`. On Windows, it is located at `C:\Windows\System32\drivers\etc\hosts`: @@ -608,7 +608,7 @@ php82 A `homestead` database is configured for both MySQL and PostgreSQL out of the box. To connect to your MySQL or PostgreSQL database from your host machine's database client, you should connect to `127.0.0.1` on port `33060` (MySQL) or `54320` (PostgreSQL). The username and password for both databases is `homestead` / `secret`. -> **Warning** +> [!WARNING] > You should only use these non-standard ports when connecting to the databases from your host machine. You will use the default 3306 and 5432 ports in your Laravel application's `database` configuration file since Laravel is running _within_ the virtual machine. @@ -725,7 +725,7 @@ share homestead.test -region=eu -subdomain=laravel If you need to share content over HTTPS rather than HTTP, using the `sshare` command instead of `share` will enable you to do so. -> **Warning** +> [!WARNING] > Remember, Vagrant is inherently insecure and you are exposing your virtual machine to the Internet when running the `share` command. @@ -738,7 +738,7 @@ Homestead includes support for step debugging using [Xdebug](https://xdebug.org) By default, Xdebug is already running and ready to accept connections. If you need to enable Xdebug on the CLI, execute the `sudo phpenmod xdebug` command within your Homestead virtual machine. Next, follow your IDE's instructions to enable debugging. Finally, configure your browser to trigger Xdebug with an extension or [bookmarklet](https://www.jetbrains.com/phpstorm/marklets/). -> **Warning** +> [!WARNING] > Xdebug causes PHP to run significantly slower. To disable Xdebug, run `sudo phpdismod xdebug` within your Homestead virtual machine and restart the FPM service. diff --git a/horizon.md b/horizon.md index a8858f3e35c..0f6d7669a84 100644 --- a/horizon.md +++ b/horizon.md @@ -18,7 +18,7 @@ ## Introduction -> **Note** +> [!NOTE] > Before digging into Laravel Horizon, you should familiarize yourself with Laravel's base [queue services](/docs/{{version}}/queues). Horizon augments Laravel's queue with additional features that may be confusing if you are not already familiar with the basic queue features offered by Laravel. [Laravel Horizon](https://github.com/laravel/horizon) provides a beautiful dashboard and code-driven configuration for your Laravel powered [Redis queues](/docs/{{version}}/queues). Horizon allows you to easily monitor key metrics of your queue system such as job throughput, runtime, and job failures. @@ -30,7 +30,7 @@ When using Horizon, all of your queue worker configuration is stored in a single ## Installation -> **Warning** +> [!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: @@ -50,7 +50,7 @@ php artisan horizon:install After publishing Horizon's assets, its primary configuration file will be located at `config/horizon.php`. This configuration file allows you to configure the queue worker options for your application. Each configuration option includes a description of its purpose, so be sure to thoroughly explore this file. -> **Warning** +> [!WARNING] > Horizon uses a Redis connection named `horizon` internally. This Redis connection name is reserved and should not be assigned to another Redis connection in the `database.php` configuration file or as the value of the `use` option in the `horizon.php` configuration file. @@ -76,7 +76,7 @@ After installation, the primary Horizon configuration option that you should fam When you start Horizon, it will use the worker process configuration options for the environment that your application is running on. Typically, the environment is determined by the value of the `APP_ENV` [environment variable](/docs/{{version}}/configuration#determining-the-current-environment). For example, the default `local` Horizon environment is configured to start three worker processes and automatically balance the number of worker processes assigned to each queue. The default `production` environment is configured to start a maximum of 10 worker processes and automatically balance the number of worker processes assigned to each queue. -> **Warning** +> [!WARNING] > You should ensure that the `environments` portion of your `horizon` configuration file contains an entry for each [environment](/docs/{{version}}/configuration#environment-configuration) on which you plan to run Horizon. @@ -260,7 +260,7 @@ Supervisor is a process monitor for the Linux operating system and will automati sudo apt-get install supervisor ``` -> **Note** +> [!NOTE] > 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. @@ -282,7 +282,7 @@ stopwaitsecs=3600 When defining your Supervisor configuration, 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. -> **Warning** +> [!WARNING] > While the examples above are valid for Ubuntu based servers, the location and file extension expected of Supervisor configuration files may vary between other server operating systems. Please consult your server's documentation for more information. @@ -298,7 +298,7 @@ sudo supervisorctl update sudo supervisorctl start horizon ``` -> **Note** +> [!NOTE] > For more information on running Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html). @@ -386,7 +386,7 @@ When retrieving the tags for a queued event listener, Horizon will automatically ## Notifications -> **Warning** +> [!WARNING] > When configuring Horizon to send Slack or SMS notifications, you should review the [prerequisites for the relevant notification channel](/docs/{{version}}/notifications). If you would like to be notified when one of your queues has a long wait time, you may use the `Horizon::routeMailNotificationsTo`, `Horizon::routeSlackNotificationsTo`, and `Horizon::routeSmsNotificationsTo` methods. You may call these methods from the `boot` method of your application's `App\Providers\HorizonServiceProvider`: diff --git a/http-client.md b/http-client.md index 2bbf7466227..058ef9e6b88 100644 --- a/http-client.md +++ b/http-client.md @@ -260,7 +260,7 @@ If all of the requests fail, an instance of `Illuminate\Http\Client\RequestExcep $response = Http::retry(3, 100, throw: false)->post(/* ... */); -> **Warning** +> [!WARNING] > If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. diff --git a/http-tests.md b/http-tests.md index e3ae098f3c2..8d577bd52d6 100644 --- a/http-tests.md +++ b/http-tests.md @@ -73,7 +73,7 @@ Instead of returning an `Illuminate\Http\Response` instance, test request method In general, each of your tests should only make one request to your application. Unexpected behavior may occur if multiple requests are executed within a single test method. -> **Note** +> [!NOTE] > For convenience, the CSRF middleware is automatically disabled when running tests. @@ -275,7 +275,7 @@ In addition, JSON response data may be accessed as array variables on the respon $this->assertTrue($response['created']); -> **Note** +> [!NOTE] > The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. @@ -885,7 +885,7 @@ Assert that the response has no JSON validation errors for the given keys: $response->assertJsonMissingValidationErrors($keys); -> **Note** +> [!NOTE] > The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. @@ -993,7 +993,7 @@ Assert that the response has the given JSON validation errors for the given keys $response->assertJsonValidationErrors(array $data, $responseKey = 'errors'); -> **Note** +> [!NOTE] > The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. @@ -1211,7 +1211,7 @@ Or, you may assert that a given field has a particular validation error message: 'name' => 'The given name was invalid.' ]); -> **Note** +> [!NOTE] > The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. @@ -1235,7 +1235,7 @@ Assert that the session has no validation errors for the given keys: $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); -> **Note** +> [!NOTE] > The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. diff --git a/installation.md b/installation.md index f460f6249b5..12829b01a95 100644 --- a/installation.md +++ b/installation.md @@ -26,7 +26,7 @@ Laravel strives to provide an amazing developer experience while providing power Whether you are new to PHP web frameworks or have years of experience, Laravel is a framework that can grow with you. We'll help you take your first steps as a web developer or give you a boost as you take your expertise to the next level. We can't wait to see what you build. -> **Note** +> [!NOTE] > New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. @@ -79,7 +79,7 @@ php artisan serve Once you have started the Artisan development server, your application will be accessible in your web browser at [http://localhost:8000](http://localhost:8000). Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). -> **Note** +> [!NOTE] > If you would like a head start when developing your Laravel application, 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. @@ -96,7 +96,7 @@ Since many of Laravel's configuration option values may vary depending on whethe Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. -> **Note** +> [!NOTE] > For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). @@ -104,7 +104,7 @@ Your `.env` file should not be committed to your application's source control, s Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with a MySQL database and will access the database at `127.0.0.1`. -> **Note** +> [!NOTE] > If you are developing on macOS and need to install MySQL, Postgres, or Redis locally, consider using [DBngin](https://dbngin.com/). If you do not want to install MySQL or Postgres on your local machine, you can always use a [SQLite](https://www.sqlite.org/index.html) database. SQLite is a small, fast, self-contained database engine. To get started, update your `.env` configuration file to use Laravel's `sqlite` database driver. You may remove the other database configuration options: @@ -141,7 +141,7 @@ Docker is a tool for running applications and services in small, light-weight "c Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. -> **Note** +> [!NOTE] > Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. @@ -167,7 +167,7 @@ cd example-app Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> **Note** +> [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). @@ -175,7 +175,7 @@ Once the application's Docker containers have been started, you can access the a Before we create a new Laravel application on your Windows machine, make sure to install [Docker Desktop](https://www.docker.com/products/docker-desktop). Next, you should ensure that Windows Subsystem for Linux 2 (WSL2) is installed and enabled. WSL allows you to run Linux binary executables natively on Windows 10. Information on how to install and enable WSL2 can be found within Microsoft's [developer environment documentation](https://docs.microsoft.com/en-us/windows/wsl/install-win10). -> **Note** +> [!NOTE] > After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). Next, you are ready to create your first Laravel project. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: @@ -198,7 +198,7 @@ cd example-app Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> **Note** +> [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). #### Developing Within WSL2 @@ -238,7 +238,7 @@ cd example-app Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> **Note** +> [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). @@ -283,7 +283,7 @@ Now that you have created your Laravel project, you may be wondering what to lea How you want to use Laravel will also dictate the next steps on your journey. There are a variety of ways to use Laravel, and we'll explore two primary use cases for the framework below. -> **Note** +> [!NOTE] > New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. @@ -295,7 +295,7 @@ If this is how you plan to use Laravel, you may want to check out our documentat If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Vite](/docs/{{version}}/vite). -> **Note** +> [!NOTE] > If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). @@ -305,5 +305,5 @@ Laravel may also serve as an API backend to a JavaScript single-page application If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [Laravel Sanctum](/docs/{{version}}/sanctum), and the [Eloquent ORM](/docs/{{version}}/eloquent). -> **Note** +> [!NOTE] > Need a head start scaffolding your Laravel backend and Next.js frontend? Laravel Breeze offers an [API stack](/docs/{{version}}/starter-kits#breeze-and-next) as well as a [Next.js frontend implementation](https://github.com/laravel/breeze-next) so you can get started in minutes. diff --git a/localization.md b/localization.md index f7b60d186d4..d42dc3f248d 100644 --- a/localization.md +++ b/localization.md @@ -16,7 +16,7 @@ ## Introduction -> **Note** +> [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. Laravel's localization features provide a convenient way to retrieve strings in various languages, allowing you to easily support multiple languages within your application. @@ -99,7 +99,7 @@ You may instruct Laravel's "pluralizer", which is used by Eloquent and other por // ... } -> **Warning** +> [!WARNING] > If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). @@ -126,7 +126,7 @@ All language files return an array of keyed strings. For example: 'welcome' => 'Welcome to our application!', ]; -> **Warning** +> [!WARNING] > For languages that differ by territory, you should name the language directories according to the ISO 15897. For example, "en_GB" should be used for British English rather than "en-gb". diff --git a/logging.md b/logging.md index f75e5f3a038..02dac3790f1 100644 --- a/logging.md +++ b/logging.md @@ -66,7 +66,7 @@ Name | Description
    -> **Note** +> [!NOTE] > Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. @@ -350,7 +350,7 @@ Once you have configured the `tap` option on your channel, you're ready to defin } } -> **Note** +> [!NOTE] > All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected. @@ -461,7 +461,7 @@ Laravel Pail is a package that allows you to easily dive into your Laravel appli ### Installation -> **Warning** +> [!WARNING] > Laravel Pail requires [PHP 8.2+](https://php.net/releases/) and the [PCNTL](https://www.php.net/manual/en/book.pcntl.php) extension. To get started, install Pail into your project using the Composer package manager: diff --git a/mail.md b/mail.md index c01f1a56760..9ee73b47a6c 100644 --- a/mail.md +++ b/mail.md @@ -284,7 +284,7 @@ Within a mailable class's `content` method, you may define the `view`, or which ); } -> **Note** +> [!NOTE] > You may wish to create a `resources/views/emails` directory to house all of your email templates; however, you are free to place them wherever you wish within your `resources/views` directory. @@ -518,7 +518,7 @@ Embedding inline images into your emails is typically cumbersome; however, Larav ``` -> **Warning** +> [!WARNING] > The `$message` variable is not available in plain-text message templates since plain-text messages do not utilize inline attachments. @@ -714,7 +714,7 @@ Thanks,
    ``` -> **Note** +> [!NOTE] > Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. @@ -917,7 +917,7 @@ Alternatively, you may call the `afterCommit` method from your mailable's constr } } -> **Note** +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). diff --git a/middleware.md b/middleware.md index c13cae91cb5..20f99bd7665 100644 --- a/middleware.md +++ b/middleware.md @@ -57,7 +57,7 @@ As you can see, if the given `token` does not match our secret token, the middle It's best to envision middleware as a series of "layers" HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely. -> **Note** +> [!NOTE] > All middleware are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a middleware's constructor. @@ -220,7 +220,7 @@ Middleware groups may be assigned to routes and controller actions using the sam // ... }); -> **Note** +> [!NOTE] > Out of the box, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `App\Providers\RouteServiceProvider`. diff --git a/migrations.md b/migrations.md index 3ad655f3e91..230ec7604ae 100644 --- a/migrations.md +++ b/migrations.md @@ -44,7 +44,7 @@ Laravel will use the name of the migration to attempt to guess the name of the t If you would like to specify a custom path for the generated migration, you may use the `--path` option when executing the `make:migration` command. The given path should be relative to your application's base path. -> **Note** +> [!NOTE] > Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). @@ -70,7 +70,7 @@ php artisan schema:dump --database=testing --prune You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure. -> **Warning** +> [!WARNING] > Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. Schema dumps may not be restored to in-memory SQLite databases. @@ -161,7 +161,7 @@ When the `isolated` option is provided, Laravel will acquire an atomic lock usin php artisan migrate --isolated ``` -> **Warning** +> [!WARNING] > To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. @@ -241,7 +241,7 @@ By default, the `migrate:fresh` command only drops tables from the default datab php artisan migrate:fresh --database=admin ``` -> **Warning** +> [!WARNING] > The `migrate:fresh` command will drop all database tables regardless of their prefix. This command should be used with caution when developing on a database that is shared with other applications. @@ -1011,7 +1011,7 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi } }; -> **Warning** +> [!WARNING] > Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. @@ -1059,7 +1059,7 @@ use Illuminate\Database\DBAL\TimestampType; ], ``` -> **Warning** +> [!WARNING] > When using the `doctrine/dbal` package, the following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `double`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `tinyText`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, `ulid`, and `uuid`. @@ -1184,7 +1184,7 @@ To rename an index, you may use the `renameIndex` method provided by the schema $table->renameIndex('from', 'to') -> **Warning** +> [!WARNING] > If your application is utilizing an SQLite database, you must install the `doctrine/dbal` package via the Composer package manager before the `renameIndex` method may be used. @@ -1282,7 +1282,7 @@ You may enable or disable foreign key constraints within your migrations by usin // Constraints disabled within this closure... }); -> **Warning** +> [!WARNING] > SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. In addition, SQLite only supports foreign keys upon creation of the table and [not when tables are altered](https://www.sqlite.org/omitted.html). diff --git a/mix.md b/mix.md index 803a3181ceb..91cf5c66506 100644 --- a/mix.md +++ b/mix.md @@ -16,5 +16,5 @@ mix.js('resources/js/app.js', 'public/js') 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. -> **Note** +> [!NOTE] > Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [official Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [Vite migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). diff --git a/mocking.md b/mocking.md index be8ee7a522d..dfa180d4673 100644 --- a/mocking.md +++ b/mocking.md @@ -110,7 +110,7 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, } } -> **Warning** +> [!WARNING] > 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. diff --git a/notifications.md b/notifications.md index 9f03828c155..6089e52f9c9 100644 --- a/notifications.md +++ b/notifications.md @@ -95,7 +95,7 @@ The `notify` method that is provided by this trait expects to receive a notifica $user->notify(new InvoicePaid($invoice)); -> **Note** +> [!NOTE] > Remember, you may use the `Notifiable` trait on any of your models. You are not limited to only including it on your `User` model. @@ -116,7 +116,7 @@ You can also send notifications immediately using the `sendNow` method. This met Every notification class has a `via` method that determines on which channels the notification will be delivered. Notifications may be sent on the `mail`, `database`, `broadcast`, `vonage`, and `slack` channels. -> **Note** +> [!NOTE] > If you would like to use other delivery channels such as Telegram or Pusher, check out the community driven [Laravel Notification Channels website](http://laravel-notification-channels.com). The `via` method receives a `$notifiable` instance, which will be an instance of the class to which the notification is being sent. You may use `$notifiable` to determine which channels the notification should be delivered on: @@ -134,7 +134,7 @@ The `via` method receives a `$notifiable` instance, which will be an instance of ### Queueing Notifications -> **Warning** +> [!WARNING] > Before queueing notifications you should configure your queue and [start a worker](/docs/{{version}}/queues#running-the-queue-worker). 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: @@ -287,7 +287,7 @@ Alternatively, you may call the `afterCommit` method from your notification's co } } -> **Note** +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -350,14 +350,14 @@ The `MailMessage` class contains a few simple methods to help you build transact ->line('Thank you for using our application!'); } -> **Note** +> [!NOTE] > 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 beautiful, responsive HTML email template with a plain-text counterpart. Here is an example of an email generated by the `mail` channel: -> **Note** +> [!NOTE] > 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. @@ -517,7 +517,7 @@ To add attachments to an email notification, use the `attach` method while build ->attach('/path/to/file'); } -> **Note** +> [!NOTE] > The `attach` method offered by notification mail messages also accepts [attachable objects](/docs/{{version}}/mail#attachable-objects). Please consult the comprehensive [attachable object documentation](/docs/{{version}}/mail#attachable-objects) to learn more. When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: @@ -809,7 +809,7 @@ php artisan notifications:table php artisan migrate ``` -> **Note** +> [!NOTE] > If your notifiable models are using [UUID or ULID primary keys](/docs/{{version}}/eloquent#uuid-and-ulid-keys), you should replace the `morphs` method with [`uuidMorphs`](/docs/{{version}}/migrations#column-method-uuidMorphs) or [`ulidMorphs`](/docs/{{version}}/migrations#column-method-ulidMorphs) in the notification table migration. @@ -866,7 +866,7 @@ If you want to retrieve only the "unread" notifications, you may use the `unread echo $notification->type; } -> **Note** +> [!NOTE] > 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. @@ -1267,7 +1267,7 @@ For instance, returning `#support-channel` from the `routeNotificationForSlack` ### Notifying External Slack Workspaces -> **Note** +> [!NOTE] > Before sending notifications to external Slack workspaces, your Slack App must be [distributed](#slack-app-distribution). Of course, you will often want to send notifications to the Slack workspaces owned by your application's users. To do so, you will first need to obtain a Slack OAuth token for the user. Thankfully, [Laravel Socialite](/docs/{{version}}/socialite) includes a Slack driver that will allow you to easily authenticate your application's users with Slack and [obtain a bot token](/docs/{{version}}/socialite#slack-bot-scopes). @@ -1464,7 +1464,7 @@ When a notification is sent, the `Illuminate\Notifications\Events\NotificationSe ], ]; -> **Note** +> [!NOTE] > After registering listeners in your `EventServiceProvider`, use the `event:generate` Artisan command to quickly generate listener classes. Within an event listener, you may access the `notifiable`, `notification`, `channel`, and `response` properties on the event to learn more about the notification recipient or the notification itself: diff --git a/octane.md b/octane.md index 70fb7730f8e..361b7b31bfb 100644 --- a/octane.md +++ b/octane.md @@ -47,13 +47,13 @@ php artisan octane:install ## Server Prerequisites -> **Warning** +> [!WARNING] > Laravel Octane requires [PHP 8.1+](https://php.net/releases/). ### FrankenPHP -> **Warning** +> [!WARNING] > FrankenPHP's Octane integration is in beta and should be used with caution in production. [FrankenPHP](https://frankenphp.dev) is a PHP application server, written in Go, that supports modern web features like early hints and Zstandard compression. When you install Octane and choose FrankenPHP as your server, Octane will automatically download and install the FrankenPHP binary for you. @@ -149,7 +149,7 @@ Using Laravel Octane with Open Swoole grants the same functionality provided by #### Swoole via Laravel Sail -> **Warning** +> [!WARNING] > Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. Alternatively, you may develop your Swoole based Octane application using [Laravel Sail](/docs/{{version}}/sail), the official Docker based development environment for Laravel. Laravel Sail includes the Swoole extension by default. However, you will still need to adjust the `docker-compose.yml` file used by Sail. @@ -206,7 +206,7 @@ By default, applications running via Octane generate links prefixed with `http:/ ### Serving Your Application via Nginx -> **Note** +> [!NOTE] > If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). In production environments, you should serve your Octane application behind a traditional web server such as Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. @@ -427,7 +427,7 @@ $service->method($request->input('name')); The global `request` helper will always return the request the application is currently handling and is therefore safe to use within your application. -> **Warning** +> [!WARNING] > It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. @@ -498,7 +498,7 @@ While building your application, you should take special care to avoid creating ## Concurrent Tasks -> **Warning** +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's `concurrently` method. You may combine this method with PHP array destructuring to retrieve the results of each operation: @@ -525,7 +525,7 @@ When invoking the `concurrently` method, you should not provide more than 1024 t ## Ticks and Intervals -> **Warning** +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may register "tick" operations that will be executed every specified number of seconds. You may register "tick" callbacks via the `tick` method. The first argument provided to the `tick` method should be a string that represents the name of the ticker. The second argument should be a callable that will be invoked at the specified interval. @@ -548,7 +548,7 @@ Octane::tick('simple-ticker', fn () => ray('Ticking...')) ## The Octane Cache -> **Warning** +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. Therefore, this cache driver is an excellent choice for applications that need extreme read / write speeds from their caching layer. @@ -559,7 +559,7 @@ This cache driver is powered by [Swoole tables](https://www.swoole.co.uk/docs/mo Cache::store('octane')->put('framework', 'Laravel', 30); ``` -> **Note** +> [!NOTE] > The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. @@ -578,7 +578,7 @@ Cache::store('octane')->interval('random', function () { ## Tables -> **Warning** +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may define and interact with your own arbitrary [Swoole tables](https://www.swoole.co.uk/docs/modules/swoole-table). Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server. However, the data within them will be lost when the server is restarted. @@ -607,5 +607,5 @@ Octane::table('example')->set('uuid', [ return Octane::table('example')->get('uuid'); ``` -> **Warning** +> [!WARNING] > The column types supported by Swoole tables are: `string`, `int`, and `float`. diff --git a/packages.md b/packages.md index cd53afe3405..5d6a5ca0ab0 100644 --- a/packages.md +++ b/packages.md @@ -106,7 +106,7 @@ Now, when users of your package execute Laravel's `vendor:publish` command, your $value = config('courier.option'); -> **Warning** +> [!WARNING] > You should not define closures in your configuration files. They can not be serialized correctly when users execute the `config:cache` Artisan command. @@ -126,7 +126,7 @@ The `mergeConfigFrom` method accepts the path to your package's configuration fi ); } -> **Warning** +> [!WARNING] > This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. diff --git a/pagination.md b/pagination.md index 8912aa4247d..d55a67fa6c2 100644 --- a/pagination.md +++ b/pagination.md @@ -125,7 +125,7 @@ You may create a cursor based paginator instance via the `cursorPaginate` method Once you have retrieved a cursor paginator instance, you may [display the pagination results](#displaying-pagination-results) as you typically would when using the `paginate` and `simplePaginate` methods. For more information on the instance methods offered by the cursor paginator, please consult the [cursor paginator instance method documentation](#cursor-paginator-instance-methods). -> **Warning** +> [!WARNING] > Your query must contain an "order by" clause in order to take advantage of cursor pagination. In addition, the columns that the query are ordered by must belong to the table you are paginating. @@ -162,7 +162,7 @@ The `Paginator` and `CursorPaginator` classes do not need to know the total numb In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, the `CursorPaginator` corresponds to the `cursorPaginate` method, and the `LengthAwarePaginator` corresponds to the `paginate` method. -> **Warning** +> [!WARNING] > 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. diff --git a/passport.md b/passport.md index 2faec0d86fc..2f94e86e491 100644 --- a/passport.md +++ b/passport.md @@ -49,7 +49,7 @@ [Laravel Passport](https://github.com/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. -> **Warning** +> [!WARNING] > 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. @@ -80,7 +80,7 @@ Next, you should execute the `passport:install` Artisan command. This command wi php artisan passport:install ``` -> **Note** +> [!NOTE] > If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). After running the `passport:install` 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. If your model is already using the `Laravel\Sanctum\HasApiTokens` trait, you may remove that trait: @@ -205,7 +205,7 @@ By default, Passport issues long-lived access tokens that expire after one year. Passport::personalAccessTokensExpireIn(now()->addMonths(6)); } -> **Warning** +> [!WARNING] > 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). @@ -399,7 +399,7 @@ If the `prompt` value is `none`, Passport will always throw an authentication er If no `prompt` value is provided, the user will be prompted for authorization only if they have not previously authorized access to the consuming application for the requested scopes. -> **Note** +> [!NOTE] > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. @@ -462,7 +462,7 @@ If the user approves the authorization request, they will be redirected back to 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. -> **Note** +> [!NOTE] > Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. @@ -653,7 +653,7 @@ If the state parameter matches, the consumer should issue a `POST` request to yo ## Password Grant Tokens -> **Warning** +> [!WARNING] > We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). 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. @@ -685,7 +685,7 @@ Once you have created a password grant client, you may request an access token b return $response->json(); -> **Note** +> [!NOTE] > Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. @@ -765,7 +765,7 @@ When authenticating using the password grant, Passport will use the `password` a ## Implicit Grant Tokens -> **Warning** +> [!WARNING] > We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). 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 application's `App\Providers\AuthServiceProvider` class: @@ -797,7 +797,7 @@ Once the grant has been enabled, developers may use their client ID to request a return redirect('http://passport-app.test/oauth/authorize?'.$query); }); -> **Note** +> [!NOTE] > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. @@ -852,7 +852,7 @@ To retrieve a token using this grant type, make a request to the `oauth/token` e Sometimes, your users may want to issue access tokens to themselves without going through the typical authorization code redirect flow. Allowing users to issue tokens to themselves via your application's UI can be useful for allowing users to experiment with your API or may serve as a simpler approach to issuing access tokens in general. -> **Note** +> [!NOTE] > If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. @@ -958,7 +958,7 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add // ... })->middleware('auth:api'); -> **Warning** +> [!WARNING] > If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. @@ -982,7 +982,7 @@ The following route will utilize the `api-customers` guard, which uses the `cust // ... })->middleware('auth:api-customers'); -> **Note** +> [!NOTE] > For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). @@ -1037,7 +1037,7 @@ If a client does not request any specific scopes, you may configure your Passpor 'place-orders', ]); -> **Note** +> [!NOTE] > Passport's default scopes do not apply to personal access tokens that are generated by the user. @@ -1138,7 +1138,7 @@ Typically, if you want to consume your API from your JavaScript application, you \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ], -> **Warning** +> [!WARNING] > You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. 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: @@ -1166,7 +1166,7 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo When using this method of authentication, you will need to ensure a valid CSRF token header is included in your requests. The default Laravel JavaScript scaffolding includes an Axios instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. -> **Note** +> [!NOTE] > If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. diff --git a/passwords.md b/passwords.md index 4e0709d7aad..26e762606ef 100644 --- a/passwords.md +++ b/passwords.md @@ -15,7 +15,7 @@ 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. -> **Note** +> [!NOTE] > 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. @@ -86,12 +86,12 @@ Before moving on, let's examine this route in more detail. First, the request's 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 `lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. -> **Note** +> [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. You may be 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). -> **Note** +> [!NOTE] > 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). diff --git a/pennant.md b/pennant.md index 871410f4e54..e0f73439176 100644 --- a/pennant.md +++ b/pennant.md @@ -141,7 +141,7 @@ class NewApi } ``` -> **Note** Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. +> [!NOTE] Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. ## Checking Features @@ -200,7 +200,7 @@ Feature::allAreInactive(['new-api', 'site-redesign']); Feature::someAreInactive(['new-api', 'site-redesign']); ``` -> **Note** +> [!NOTE] > When using Pennant outside of an HTTP context, such as in an Artisan command or a queued job, you should typically [explicitly specify the feature's scope](#specifying-the-scope). Alternatively, you may define a [default scope](#default-scope) that accounts for both authenticated HTTP contexts and unauthenticated contexts. @@ -583,7 +583,7 @@ Pennant's included Blade directive also makes it easy to conditionally render co @endfeature ``` -> **Note** When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. +> [!NOTE] When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. When calling the [conditional `when`](#conditional-execution) method, the feature's rich value will be provided to the first closure: @@ -751,7 +751,7 @@ Alternatively, you may deactivate the feature for all users: Feature::deactivateForEveryone('new-api'); ``` -> **Note** This will only update the resolved feature values that have been stored by Pennant's storage driver. You will also need to update the feature definition in your application. +> [!NOTE] This will only update the resolved feature values that have been stored by Pennant's storage driver. You will also need to update the feature definition in your application. ### Purging Features @@ -873,7 +873,7 @@ class RedisFeatureDriver implements Driver Now, we just need to implement each of these methods using a Redis connection. For an example of how to implement each of these methods, take a look at the `Laravel\Pennant\Drivers\DatabaseDriver` in the [Pennant source code](https://github.com/laravel/pennant/blob/1.x/src/Drivers/DatabaseDriver.php) -> **Note** +> [!NOTE] > Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `RedisFeatureDriver`. diff --git a/precognition.md b/precognition.md index ecde87a0cf1..35a340e09ff 100644 --- a/precognition.md +++ b/precognition.md @@ -133,7 +133,7 @@ You may also determine if an input has passed or failed validation by passing th ``` -> **Warning** +> [!WARNING] > A form input will only appear as valid or invalid once it has changed and a validation response has been received. If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's `forgetError` function to achieve this: @@ -175,7 +175,7 @@ You may determine if a form submission request is in-flight by inspecting the fo ### Using Vue and Inertia -> **Note** +> [!NOTE] > If you would like a head start when developing your Laravel application with Vue 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 Vue and Inertia, be sure to review our general documentation on [using Precognition with Vue](#using-vue). When using Vue with Inertia, you will need to install the Inertia compatible Precognition library via NPM: @@ -305,7 +305,7 @@ You may also determine if an input has passed or failed validation by passing th {form.invalid('email') && } ``` -> **Warning** +> [!WARNING] > A form input will only appear as valid or invalid once it has changed and a validation response has been received. If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's `forgetError` function to achieve this: @@ -351,7 +351,7 @@ You may determine if a form submission request is in-flight by inspecting the fo ### Using React and Inertia -> **Note** +> [!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: @@ -498,7 +498,7 @@ You may also determine if an input has passed or failed validation by passing th ``` -> **Warning** +> [!WARNING] > A form input will only appear as valid or invalid once it has changed and a validation response has been received. You may determine if a form submission request is in-flight by inspecting the form's `processing` property: @@ -571,7 +571,7 @@ window.axios.defaults.headers.common['Authorization'] = authToken; client.use(window.axios) ``` -> **Warning** +> [!WARNING] > The Inertia flavored Precognition libraries will only use the configured Axios instance for validation requests. Form submissions will always be sent by Inertia. diff --git a/prompts.md b/prompts.md index b6e51f733c4..d17be47da33 100644 --- a/prompts.md +++ b/prompts.md @@ -27,7 +27,7 @@ Laravel Prompts is perfect for accepting user input in your [Artisan console commands](/docs/{{version}}/artisan#writing-commands), but it may also be used in any command-line PHP project. -> **Note** +> [!NOTE] > Laravel Prompts supports macOS, Linux, and Windows with WSL. For more information, please see our documentation on [unsupported environments & fallbacks](#fallbacks). @@ -649,7 +649,7 @@ $response = spin( ); ``` -> **Warning** +> [!WARNING] > The `spin` function requires the `pcntl` PHP extension to animate the spinner. When this extension is not available, a static version of the spinner will appear instead. @@ -724,7 +724,7 @@ Laravel Prompts supports macOS, Linux, and Windows with WSL. Due to limitations For this reason, Laravel Prompts supports falling back to an alternative implementation such as the [Symfony Console Question Helper](https://symfony.com/doc/current/components/console/helpers/questionhelper.html). -> **Note** +> [!NOTE] > When using Laravel Prompts with the Laravel framework, fallbacks for each prompt have been configured for you and will be automatically enabled in unsupported environments. diff --git a/providers.md b/providers.md index 311d1c188de..d44fb77393d 100644 --- a/providers.md +++ b/providers.md @@ -18,7 +18,7 @@ If you open the `config/app.php` file included with Laravel, you will see a `pro In this overview, you will learn how to write your own service providers and register them with your Laravel application. -> **Note** +> [!NOTE] > 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). diff --git a/pulse.md b/pulse.md index e9935b91cff..6554742fcc6 100644 --- a/pulse.md +++ b/pulse.md @@ -32,7 +32,7 @@ For in-depth debugging of individual events, check out [Laravel Telescope](/docs ## Installation -> **Warning** +> [!WARNING] > Pulse's first-party storage implementation currently requires a MySQL or PostgreSQL database. If you are using a different database engine, you will need a separate MySQL or PostgreSQL database for your Pulse data. Since Pulse is currently in beta, you may need to adjust your application's `composer.json` file to allow beta package releases to be installed: @@ -62,7 +62,7 @@ php artisan migrate Once Pulse's database migrations have been run, you may access the Pulse dashboard via the `/pulse` route. -> **Note** +> [!NOTE] > If you do not want to store Pulse data in your application's primary database, you may [specify a dedicated database connection](#using-a-different-database). @@ -165,7 +165,7 @@ public function boot(): void } ``` -> **Note** +> [!NOTE] > You may completely customize how the authenticated user is captured and retrieved by implementing the `Laravel\Pulse\Contracts\ResolvesUsers` contract and binding it in Laravel's [service container](/docs/{{version}}/container#binding-a-singleton). @@ -191,7 +191,7 @@ If you wish to view all usage metrics on screen at the same time, you may includ To learn how to customize how Pulse retrieves and displays user information, consult our documentation on [resolving users](#dashboard-resolving-users). -> **Note** +> [!NOTE] > If your application receives a lot of requests or dispatches a lot of jobs, you may wish to enable [sampling](#sampling). See the [user requests recorder](#user-requests-recorder), [user jobs recorder](#user-jobs-recorder), and [slow jobs recorder](#slow-jobs-recorder) documentation for more information. @@ -246,7 +246,7 @@ Most Pulse recorders will automatically capture entries based on framework event php artisan pulse:check ``` -> **Note** +> [!NOTE] > To keep the `pulse:check` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the command does not stop running. @@ -401,7 +401,7 @@ PULSE_DB_CONNECTION=pulse ### Redis Ingest -> **Warning** +> [!WARNING] > The Redis Ingest requires Redis 6.2 or greater and `phpredis` or `predis` as the application's configured Redis client driver. By default, Pulse will store entries directly to the [configured database connection](#using-a-different-database) after the HTTP response has been sent to the client or a job has been processed; however, you may use Pulse's Redis ingest driver to send entries to a Redis stream instead. This can be enabled by configuring the `PULSE_INGEST_DRIVER` environment variable: @@ -422,7 +422,7 @@ When using the Redis ingest, you will need to run the `pulse:work` command to mo php artisan pulse:work ``` -> **Note** +> [!NOTE] > To keep the `pulse:work` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the Pulse worker does not stop running. @@ -514,7 +514,7 @@ Once you have defined your Livewire component and template, the card may be incl ``` -> **Note** +> [!NOTE] > If your card is included in a package, you will need to register the component with Livewire using the `Livewire::component` method. @@ -628,7 +628,7 @@ The available aggregation methods are: * `min` * `sum` -> **Note** +> [!NOTE] > When building a card package that captures the currently authenticated user ID, you should use the `Pulse::resolveAuthenticatedUserId()` method, which respects any [user resolver customizations](#dashboard-resolving-users) made to the application. diff --git a/queries.md b/queries.md index 774e2ec5cdb..d46aa7c2777 100644 --- a/queries.md +++ b/queries.md @@ -41,7 +41,7 @@ Laravel's database query builder provides a convenient, fluent interface to crea 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. -> **Warning** +> [!WARNING] > 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. @@ -83,7 +83,7 @@ The `get` method returns an `Illuminate\Support\Collection` instance containing echo $user->name; } -> **Note** +> [!NOTE] > Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel collections, check out the [collection documentation](/docs/{{version}}/collections). @@ -157,7 +157,7 @@ If you are updating database records while chunking results, your chunk results } }); -> **Warning** +> [!WARNING] > When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results. @@ -184,7 +184,7 @@ DB::table('users')->where('active', false) }); ``` -> **Warning** +> [!WARNING] > When updating or deleting records while iterating over them, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the results. @@ -252,7 +252,7 @@ Sometimes you may need to insert an arbitrary string into a query. To create a r ->groupBy('status') ->get(); -> **Warning** +> [!WARNING] > Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities. @@ -438,7 +438,7 @@ You may also pass an array of conditions to the `where` function. Each element o ['subscribed', '<>', '1'], ])->get(); -> **Warning** +> [!WARNING] > 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. @@ -467,7 +467,7 @@ The example above will produce the following SQL: select * from users where votes > 100 or (name = 'Abigail' and votes > 50) ``` -> **Warning** +> [!WARNING] > You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. @@ -578,7 +578,7 @@ select * from comments where user_id in ( ) ``` -> **Warning** +> [!WARNING] > 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** @@ -668,7 +668,7 @@ As you can see, passing a closure into the `where` method instructs the query bu select * from users where name = 'John' and (votes > 100 or title = 'Admin') ``` -> **Warning** +> [!WARNING] > You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. @@ -736,7 +736,7 @@ Or, you may need to construct a "where" clause that compares a column to the res ### Full Text Where Clauses -> **Warning** +> [!WARNING] > Full text where clauses are currently supported by MySQL and PostgreSQL. The `whereFullText` and `orWhereFullText` methods may be used to add full text "where" clauses to a query for columns that have [full text indexes](/docs/{{version}}/migrations#available-index-types). These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a `MATCH AGAINST` clause will be generated for applications utilizing MySQL: @@ -915,7 +915,7 @@ If the table has an auto-incrementing id, use the `insertGetId` method to insert ['email' => 'john@example.com', 'votes' => 0] ); -> **Warning** +> [!WARNING] > 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. @@ -934,7 +934,7 @@ The `upsert` method will insert records that do not exist and update the records 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. -> **Warning** +> [!WARNING] > All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. diff --git a/queues.md b/queues.md index c4d6f38d2a1..80188c236f0 100644 --- a/queues.md +++ b/queues.md @@ -62,7 +62,7 @@ Laravel queues provide a unified queueing API across a variety of different queu 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. -> **Note** +> [!NOTE] > 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. @@ -109,7 +109,7 @@ Finally, don't forget to instruct your application to use the `database` driver In order to use the `redis` queue driver, you should configure a Redis database connection in your `config/database.php` configuration file. -> **Warning** +> [!WARNING] > The `serializer` and `compression` Redis options are not supported by the `redis` queue driver. **Redis Cluster** @@ -137,7 +137,7 @@ Adjusting this value based on your queue load can be more efficient than continu 'block_for' => 5, ], -> **Warning** +> [!WARNING] > Setting `block_for` to `0` will cause queue workers to block indefinitely until a job is available. This will also prevent signals such as `SIGTERM` from being handled until the next job has been processed. @@ -167,7 +167,7 @@ php artisan make:job ProcessPodcast The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue` interface, indicating to Laravel that the job should be pushed onto the queue to run asynchronously. -> **Note** +> [!NOTE] > Job stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). @@ -226,7 +226,7 @@ If you would like to take total control over how the container injects dependenc return $job->handle($app->make(AudioProcessor::class)); }); -> **Warning** +> [!WARNING] > Binary data, such as raw image contents, should be passed through the `base64_encode` function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue. @@ -262,7 +262,7 @@ If a job receives a collection or array of Eloquent models instead of a single m ### Unique Jobs -> **Warning** +> [!WARNING] > 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. In addition, unique job constraints do not apply to jobs within batches. 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: @@ -314,7 +314,7 @@ In certain cases, you may want to define a specific "key" that makes the job uni In the example above, the `UpdateSearchIndex` job is unique by a product ID. So, any new dispatches of the job with the same product ID will be ignored until the existing job has completed processing. In addition, if the existing job is not processed within one hour, the unique lock will be released and another job with the same unique key can be dispatched to the queue. -> **Warning** +> [!WARNING] > If your application dispatches jobs from multiple web servers or containers, you should ensure that all of your servers are communicating with the same central cache server so that Laravel can accurately determine if a job is unique. @@ -354,7 +354,7 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t } } -> **Note** +> [!NOTE] > If you only need to limit the concurrent processing of a job, use the [`WithoutOverlapping`](/docs/{{version}}/queues#preventing-job-overlaps) job middleware instead. @@ -445,7 +445,7 @@ After creating job middleware, they may be attached to a job by returning them f return [new RateLimited]; } -> **Note** +> [!NOTE] > Job middleware can also be assigned to queueable event listeners, mailables, and notifications. @@ -502,7 +502,7 @@ If you do not want a job to be retried when it is rate limited, you may use the return [(new RateLimited('backups'))->dontRelease()]; } -> **Note** +> [!NOTE] > 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. @@ -560,7 +560,7 @@ The `WithoutOverlapping` middleware is powered by Laravel's atomic lock feature. return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; } -> **Warning** +> [!WARNING] > 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. @@ -656,7 +656,7 @@ Internally, this middleware uses Laravel's cache system to implement rate limiti return [(new ThrottlesExceptions(10, 10))->by('key')]; } -> **Note** +> [!NOTE] > If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. @@ -732,7 +732,7 @@ If you would like to specify that a job should not be immediately available for } } -> **Warning** +> [!WARNING] > The Amazon SQS queue service has a maximum delay time of 15 minutes. @@ -802,7 +802,7 @@ When the `after_commit` option is `true`, you may dispatch jobs within database If a transaction is rolled back due to an exception that occurs during the transaction, the jobs that were dispatched during that transaction will be discarded. -> **Note** +> [!NOTE] > Setting the `after_commit` configuration option to `true` will also cause any queued event listeners, mailables, notifications, and broadcast events to be dispatched after all open database transactions have been committed. @@ -844,7 +844,7 @@ In addition to chaining job class instances, you may also chain closures: }, ])->dispatch(); -> **Warning** +> [!WARNING] > 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. @@ -874,7 +874,7 @@ When chaining jobs, you may use the `catch` method to specify a closure that sho // A job within the chain has failed... })->dispatch(); -> **Warning** +> [!WARNING] > Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. @@ -1057,7 +1057,7 @@ As an alternative to defining how many times a job may be attempted before it fa return now()->addMinutes(10); } -> **Note** +> [!NOTE] > You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). @@ -1134,7 +1134,7 @@ You may also define the maximum number of seconds a job should be allowed to run Sometimes, IO blocking processes such as sockets or outgoing HTTP connections may not respect your specified timeout. Therefore, when using these features, you should always attempt to specify a timeout using their APIs as well. For example, when using Guzzle, you should always specify a connection and request timeout value. -> **Warning** +> [!WARNING] > The `pcntl` PHP extension must be installed in order to specify job timeouts. In addition, a job's "timeout" value should always be less than its ["retry after"](#job-expiration) value. Otherwise, the job may be re-attempted before it has actually finished executing or timed out. @@ -1198,7 +1198,7 @@ If you would like to mark your job as failed because of an exception that you ha $this->fail('Something went wrong.'); -> **Note** +> [!NOTE] > For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). @@ -1277,7 +1277,7 @@ To dispatch a batch of jobs, you should use the `batch` method of the `Bus` faca 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. -> **Warning** +> [!WARNING] > Since batch callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within the callbacks. @@ -1376,7 +1376,7 @@ In this example, we will use the `LoadImportBatch` job to hydrate the batch with })); } -> **Warning** +> [!WARNING] > You may only add jobs to a batch from within a job that belongs to the same batch. @@ -1578,7 +1578,7 @@ Using the `catch` method, you may provide a closure that should be executed if t // This job has failed... }); -> **Warning** +> [!WARNING] > Since `catch` callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within `catch` callbacks. @@ -1593,7 +1593,7 @@ Laravel includes an Artisan command that will start a queue worker and process n php artisan queue:work ``` -> **Note** +> [!NOTE] > To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. You may include the `-v` flag when invoking the `queue:work` command if you would like the processed job IDs to be included in the command's output: @@ -1702,7 +1702,7 @@ php artisan queue:restart 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. -> **Note** +> [!NOTE] > 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. @@ -1713,7 +1713,7 @@ This command will instruct all queue workers to gracefully exit after they finis 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. -> **Warning** +> [!WARNING] > 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. @@ -1727,7 +1727,7 @@ 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. -> **Warning** +> [!WARNING] > 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. @@ -1746,7 +1746,7 @@ Supervisor is a process monitor for the Linux operating system, and will automat sudo apt-get install supervisor ``` -> **Note** +> [!NOTE] > 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. @@ -1771,7 +1771,7 @@ 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. -> **Warning** +> [!WARNING] > 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. @@ -1890,7 +1890,7 @@ When a particular job fails, you may want to send an alert to your users or reve } } -> **Warning** +> [!WARNING] > A new instance of the job is instantiated before invoking the `failed` method; therefore, any class property modifications that may have occurred within the `handle` method will be lost. @@ -1932,7 +1932,7 @@ If you would like to delete a failed job, you may use the `queue:forget` command php artisan queue:forget 91401d2c-0784-4f43-824c-34f94a33c24d ``` -> **Note** +> [!NOTE] > 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 from the `failed_jobs` table, you may use the `queue:flush` command: @@ -2043,7 +2043,7 @@ If you would like to register an event listener that will be invoked when a job ## Clearing Jobs From Queues -> **Note** +> [!NOTE] > When using [Horizon](/docs/{{version}}/horizon), you should use the `horizon:clear` command to clear jobs from the queue instead of the `queue:clear` command. If you would like to delete all jobs from the default queue of the default connection, you may do so using the `queue:clear` Artisan command: @@ -2058,7 +2058,7 @@ You may also provide the `connection` argument and `queue` option to delete jobs php artisan queue:clear redis --queue=emails ``` -> **Warning** +> [!WARNING] > Clearing jobs from queues is only available for the SQS, Redis, and database queue drivers. In addition, the SQS message deletion process takes up to 60 seconds, so jobs sent to the SQS queue up to 60 seconds after you clear the queue might also be deleted. diff --git a/rate-limiting.md b/rate-limiting.md index 1bf27203f75..5d7e1979dcf 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -11,7 +11,7 @@ Laravel includes a simple to use rate limiting abstraction which, in conjunction with your application's [cache](cache), provides an easy way to limit any action during a specified window of time. -> **Note** +> [!NOTE] > If you are interested in rate limiting incoming HTTP requests, please consult the [rate limiter middleware documentation](routing#rate-limiting). diff --git a/redis.md b/redis.md index b9637225c3f..39f090460f5 100644 --- a/redis.md +++ b/redis.md @@ -263,7 +263,7 @@ The `Redis` facade's `transaction` method provides a convenient wrapper around R $redis->incr('total_visits', 1); }); -> **Warning** +> [!WARNING] > 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 @@ -284,7 +284,7 @@ In this example, we will increment a counter, inspect its new value, and increme return counter LUA, 2, 'first-counter', 'second-counter'); -> **Warning** +> [!WARNING] > Please consult the [Redis documentation](https://redis.io/commands/eval) for more information on Redis scripting. diff --git a/requests.md b/requests.md index af920226cdc..8d5a24099fb 100644 --- a/requests.md +++ b/requests.md @@ -234,7 +234,7 @@ Once you have installed these libraries, you may obtain a PSR-7 request by type- // ... }); -> **Note** +> [!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. @@ -361,7 +361,7 @@ If you need to retrieve a subset of the input data, you may use the `only` and ` $input = $request->except('credit_card'); -> **Warning** +> [!WARNING] > 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. @@ -591,7 +591,7 @@ If you do not want a filename to be automatically generated, you may use the `st $path = $request->photo->storeAs('images', 'filename.jpg', 's3'); -> **Note** +> [!NOTE] > For more information about file storage in Laravel, check out the complete [file storage documentation](/docs/{{version}}/filesystem). @@ -628,7 +628,7 @@ To solve this, you may use the `App\Http\Middleware\TrustProxies` middleware tha protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO; } -> **Note** +> [!NOTE] > If you are using AWS Elastic Load Balancing, your `$headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `$headers` property, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). diff --git a/responses.md b/responses.md index 4b82d2f1bc1..a6009034ccc 100644 --- a/responses.md +++ b/responses.md @@ -34,7 +34,7 @@ In addition to returning strings from your routes and controllers, you may also return [1, 2, 3]; }); -> **Note** +> [!NOTE] > Did you know you can also return [Eloquent collections](/docs/{{version}}/eloquent-collections) from your routes or controllers? They will automatically be converted to JSON. Give it a shot! @@ -287,7 +287,7 @@ The `download` method may be used to generate a response that forces the user's return response()->download($pathToFile, $name, $headers); -> **Warning** +> [!WARNING] > Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. diff --git a/routing.md b/routing.md index 11524a8aba6..56eafe552ce 100644 --- a/routing.md +++ b/routing.md @@ -74,7 +74,7 @@ Sometimes you may need to register a route that responds to multiple HTTP verbs. // ... }); -> **Note** +> [!NOTE] > When defining multiple routes that share the same URI, routes using the `get`, `post`, `put`, `patch`, `delete`, and `options` methods should be defined before routes using the `any`, `match`, and `redirect` methods. This ensures the incoming request is matched with the correct route. @@ -113,7 +113,7 @@ Or, you may use the `Route::permanentRedirect` method to return a `301` status c Route::permanentRedirect('/here', '/there'); -> **Warning** +> [!WARNING] > When using route parameters in redirect routes, the following parameters are reserved by Laravel and cannot be used: `destination` and `status`. @@ -125,7 +125,7 @@ If your route only needs to return a [view](/docs/{{version}}/views), you may us Route::view('/welcome', 'welcome', ['name' => 'Taylor']); -> **Warning** +> [!WARNING] > When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. @@ -277,7 +277,7 @@ The Laravel routing component allows all characters except `/` to be present wit return $search; })->where('search', '.*'); -> **Warning** +> [!WARNING] > Encoded forward slashes are only supported within the last route segment. @@ -296,7 +296,7 @@ You may also specify route names for controller actions: [UserProfileController::class, 'show'] )->name('profile'); -> **Warning** +> [!WARNING] > Route names should always be unique. @@ -330,7 +330,7 @@ If you pass additional parameters in the array, those key / value pairs will aut // /user/1/profile?photos=yes -> **Note** +> [!NOTE] > Sometimes, you may wish to specify request-wide default values for URL parameters, such as the current locale. To accomplish this, you may use the [`URL::defaults` method](/docs/{{version}}/urls#default-values). @@ -401,7 +401,7 @@ Route groups may also be used to handle subdomain routing. Subdomains may be ass }); }); -> **Warning** +> [!WARNING] > In order to ensure your subdomain routes are reachable, you should register subdomain routes before registering root domain routes. This will prevent root domain routes from overwriting subdomain routes which have the same URI path. @@ -659,7 +659,7 @@ Using the `Route::fallback` method, you may define a route that will be executed // ... }); -> **Warning** +> [!WARNING] > The fallback route should always be the last route registered by your application. @@ -812,7 +812,7 @@ You may refer to the API documentation for both the [underlying class of the Rou 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`). -> **Note** +> [!NOTE] > 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). diff --git a/sail.md b/sail.md index 0a50a8a2122..d62926f4db8 100644 --- a/sail.md +++ b/sail.md @@ -62,7 +62,7 @@ Finally, you may start Sail. To continue learning how to use Sail, please contin ./vendor/bin/sail up ``` -> **Warning** +> [!WARNING] > If you are using Docker Desktop for Linux, you should use the `default` Docker context by executing the following command: `docker context use default`. @@ -263,7 +263,7 @@ AWS_URL=http://localhost:9000/local You may create buckets via the MinIO console, which is available at `http://localhost:8900`. The default username for the MinIO console is `sail` while the default password is `password`. -> **Warning** +> [!WARNING] > Generating temporary storage URLs via the `temporaryUrl` method is not supported when using MinIO. @@ -443,7 +443,7 @@ If you would like to choose the subdomain for your shared site, you may provide sail share --subdomain=my-sail-site ``` -> **Note** +> [!NOTE] > The `share` command is powered by [Expose](https://github.com/beyondcode/expose), an open source tunneling service by [BeyondCode](https://beyondco.de). @@ -491,7 +491,7 @@ To debug your application while interacting with the application via a web brows If you're using PhpStorm, please review JetBrain's documentation regarding [zero-configuration debugging](https://www.jetbrains.com/help/phpstorm/zero-configuration-debugging.html). -> **Warning** +> [!WARNING] > Laravel Sail relies on `artisan serve` to serve your application. The `artisan serve` command only accepts the `XDEBUG_CONFIG` and `XDEBUG_MODE` variables as of Laravel version 8.53.0. Older versions of Laravel (8.52.0 and below) do not support these variables and will not accept debug connections. diff --git a/sanctum.md b/sanctum.md index 7ee79af27c7..ca97137aa9a 100644 --- a/sanctum.md +++ b/sanctum.md @@ -48,13 +48,13 @@ For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses 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. -> **Note** +> [!NOTE] > 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 -> **Note** +> [!NOTE] > The most recent versions of Laravel already include Laravel Sanctum. However, if your application's `composer.json` file does not include `laravel/sanctum`, you may follow the installation instructions below. You may install Laravel Sanctum via the Composer package manager: @@ -119,7 +119,7 @@ Then, you may instruct Sanctum to use your custom model via the `usePersonalAcce ## API Token Authentication -> **Note** +> [!NOTE] > You should not use API tokens to authenticate your own first-party SPA. Instead, use Sanctum's built-in [SPA authentication features](#spa-authentication). @@ -258,7 +258,7 @@ Sanctum also exists to provide a simple method of authenticating single page app 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. -> **Warning** +> [!WARNING] > In order to authenticate, your SPA and API must share the same top-level domain. However, they may be placed on different subdomains. Additionally, you should ensure that you send the `Accept: application/json` header and either the `Referer` or `Origin` header with your request. @@ -270,7 +270,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. -> **Warning** +> [!WARNING] > 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. @@ -327,7 +327,7 @@ If the login request is successful, you will be authenticated and subsequent req 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. -> **Warning** +> [!WARNING] > 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. @@ -412,7 +412,7 @@ Typically, you will make a request to the token endpoint from your mobile applic 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. -> **Note** +> [!NOTE] > When issuing tokens for a mobile application, you are also free to specify [token abilities](#token-abilities). diff --git a/scheduling.md b/scheduling.md index 93a61a4c517..b9932b67dbe 100644 --- a/scheduling.md +++ b/scheduling.md @@ -263,7 +263,7 @@ If you are repeatedly assigning the same timezone to all of your scheduled tasks return 'America/Chicago'; } -> **Warning** +> [!WARNING] > Remember that some timezones utilize daylight savings time. When daylight saving time changes occur, your scheduled task may run twice or even not run at all. For this reason, we recommend avoiding timezone scheduling when possible. @@ -284,7 +284,7 @@ Behind the scenes, the `withoutOverlapping` method utilizes your application's [ ### Running Tasks on One Server -> **Warning** +> [!WARNING] > 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'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! @@ -332,7 +332,7 @@ By default, multiple tasks scheduled at the same time will execute sequentially ->daily() ->runInBackground(); -> **Warning** +> [!WARNING] > The `runInBackground` method may only be used when scheduling tasks via the `command` and `exec` methods. @@ -420,7 +420,7 @@ If you only want to email the output if the scheduled Artisan or system command ->daily() ->emailOutputOnFailure('taylor@example.com'); -> **Warning** +> [!WARNING] > The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods. diff --git a/scout.md b/scout.md index 9f9c39467cb..79a0ed199cd 100644 --- a/scout.md +++ b/scout.md @@ -98,7 +98,7 @@ For more information regarding Meilisearch, please consult the [Meilisearch docu In addition, you should ensure that you install a version of `meilisearch/meilisearch-php` that is compatible with your Meilisearch binary version by reviewing [Meilisearch's documentation regarding binary compatibility](https://github.com/meilisearch/meilisearch-php#-compatibility-with-meilisearch). -> **Warning** +> [!WARNING] > When upgrading Scout on an application that utilizes Meilisearch, you should always [review any additional breaking changes](https://github.com/meilisearch/Meilisearch/releases) to the Meilisearch service itself. @@ -312,7 +312,7 @@ Enabling this feature will also pass the request's IP address and your authentic ### Database Engine -> **Warning** +> [!WARNING] > The database engine currently supports MySQL and PostgreSQL. If your application interacts with small to medium sized databases or has a light workload, you may find it more convenient to get started with Scout's "database" engine. The database engine will use "where like" clauses and full text indexes when filtering results from your existing database to determine the applicable search results for your query. @@ -353,7 +353,7 @@ public function toSearchableArray(): array } ``` -> **Warning** +> [!WARNING] > Before specifying that a column should use full text query constraints, ensure that the column has been assigned a [full text index](/docs/{{version}}/migrations#available-index-types). @@ -408,7 +408,7 @@ If you would like to modify the query that is used to retrieve all of your model return $query->with('author'); } -> **Warning** +> [!WARNING] > The `makeAllSearchableUsing` method may not be applicable when using a queue to batch import models. Relationships are [not restored](/docs/{{version}}/queues#handling-relationships) when model collections are processed by jobs. @@ -441,7 +441,7 @@ Or, if you already have a collection of Eloquent models in memory, you may call $orders->searchable(); -> **Note** +> [!NOTE] > 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. @@ -533,7 +533,7 @@ Sometimes you may need to only make a model searchable under certain conditions. 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. -> **Warning** +> [!WARNING] > The `shouldBeSearchable` method is not applicable when using Scout's "database" engine, as all searchable data is always stored in the database. To achieve similar behavior when using the database engine, you should use [where clauses](#where-clauses) instead. @@ -590,7 +590,7 @@ The `whereNotIn` method verifies that the given column's value is not contained Since a search index is not a relational database, more advanced "where" clauses are not currently supported. -> **Warning** +> [!WARNING] > If your application is using Meilisearch, you must configure your application's [filterable attributes](#configuring-filterable-data-for-meilisearch) before utilizing Scout's "where" clauses. @@ -627,7 +627,7 @@ Of course, if you would like to retrieve the pagination results as JSON, you may return Order::search($request->input('query'))->paginate(15); }); -> **Warning** +> [!WARNING] > Since search engines are not aware of your Eloquent model's global scope definitions, you should not utilize global scopes in applications that utilize Scout pagination. Or, you should recreate the global scope's constraints when searching via Scout. @@ -647,7 +647,7 @@ When this configuration option is `true`, Scout will not remove soft deleted mod // Only include trashed records when retrieving results... $orders = Order::search('Star Trek')->onlyTrashed()->get(); -> **Note** +> [!NOTE] > When a soft deleted model is permanently deleted using `forceDelete`, Scout will remove it from the search index automatically. diff --git a/seeding.md b/seeding.md index 04c7e99f2ec..4dd70fab4f2 100644 --- a/seeding.md +++ b/seeding.md @@ -12,7 +12,7 @@ Laravel includes the ability to seed your database with 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. -> **Note** +> [!NOTE] > [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled during database seeding. @@ -52,7 +52,7 @@ As an example, let's modify the default `DatabaseSeeder` class and add a databas } } -> **Note** +> [!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/session.md b/session.md index 7893050651a..8545e13212c 100644 --- a/session.md +++ b/session.md @@ -39,7 +39,7 @@ The session `driver` configuration option defines where session data will be sto
    -> **Note** +> [!NOTE] > The array driver is primarily used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted. @@ -75,7 +75,7 @@ php artisan migrate 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). -> **Note** +> [!NOTE] > In the `session` configuration file, the `connection` option may be used to specify which Redis connection is used by the session. @@ -134,7 +134,7 @@ You may also use the global `session` PHP function to retrieve and store data in session(['key' => 'value']); }); -> **Note** +> [!NOTE] > There is little practical difference between using the session via an HTTP request instance versus using the global `session` helper. Both methods are [testable](/docs/{{version}}/testing) via the `assertSessionHas` method which is available in all of your test cases. @@ -258,7 +258,7 @@ If you need to regenerate the session ID and remove all data from the session in ## Session Blocking -> **Warning** +> [!WARNING] > To utilize session blocking, your application must be using a cache driver that supports [atomic locks](/docs/{{version}}/cache#atomic-locks). Currently, those cache drivers include the `memcached`, `dynamodb`, `redis`, `database`, `file`, and `array` drivers. In addition, you may not use the `cookie` session driver. By default, Laravel allows requests using the same session to execute concurrently. So, for example, if you use a JavaScript HTTP library to make two HTTP requests to your application, they will both execute at the same time. For many applications, this is not a problem; however, session data loss can occur in a small subset of applications that make concurrent requests to two different application endpoints which both write data to the session. @@ -305,7 +305,7 @@ If none of the existing session drivers fit your application's needs, Laravel ma public function gc($lifetime) {} } -> **Note** +> [!NOTE] > Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `MongoSessionHandler`. Since the purpose of these methods is not readily understandable, let's quickly cover what each of the methods do: diff --git a/socialite.md b/socialite.md index 3b25367ba99..f55cba04016 100644 --- a/socialite.md +++ b/socialite.md @@ -17,7 +17,7 @@ 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 via Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, Bitbucket, and Slack. -> **Note** +> [!NOTE] > Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. @@ -47,7 +47,7 @@ These credentials should be placed in your application's `config/services.php` c 'redirect' => 'http://example.com/callback-url', ], -> **Note** +> [!NOTE] > If the `redirect` option contains a relative path, it will automatically be resolved to a fully qualified URL. @@ -98,7 +98,7 @@ Once the user has been retrieved from the OAuth provider, you may determine if t return redirect('/dashboard'); }); -> **Note** +> [!NOTE] > For more information regarding what user information is available from specific OAuth providers, please consult the documentation on [retrieving user details](#retrieving-user-details). @@ -156,7 +156,7 @@ A number of OAuth providers support other optional parameters on the redirect re ->with(['hd' => 'example.com']) ->redirect(); -> **Warning** +> [!WARNING] > When using the `with` method, be careful not to pass any reserved keywords such as `state` or `response_type`. @@ -215,5 +215,5 @@ The `stateless` method may be used to disable session state verification. This i return Socialite::driver('google')->stateless()->user(); -> **Warning** +> [!WARNING] > Stateless authentication is not available for the Twitter OAuth 1.0 driver. diff --git a/starter-kits.md b/starter-kits.md index b58782ab78d..b91af96843e 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -65,7 +65,7 @@ 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. -> **Note** +> [!NOTE] > To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). diff --git a/structure.md b/structure.md index 1a62b5d9642..1598f50f2d4 100644 --- a/structure.md +++ b/structure.md @@ -32,7 +32,7 @@ The default Laravel application structure is intended to provide a great starting point for both large and small applications. But you are free to organize your application however you like. Laravel imposes almost no restrictions on where any given class is located - as long as Composer can autoload the class. -> **Note** +> [!NOTE] > New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. @@ -107,7 +107,7 @@ The `app` directory contains a variety of additional directories such as `Consol A variety of other directories will be generated inside the `app` directory as you use the `make` Artisan commands to generate classes. So, for example, the `app/Jobs` directory will not exist until you execute the `make:job` Artisan command to generate a job class. -> **Note** +> [!NOTE] > Many of the classes in the `app` directory can be generated by Artisan via commands. To review the available commands, run the `php artisan list make` command in your terminal. diff --git a/telescope.md b/telescope.md index bbc6004da12..2222ba8e167 100644 --- a/telescope.md +++ b/telescope.md @@ -140,7 +140,7 @@ The Telescope dashboard may be accessed via the `/telescope` route. By default, }); } -> **Warning** +> [!WARNING] > You should ensure you change your `APP_ENV` environment variable to `production` in your production environment. Otherwise, your Telescope installation will be publicly available. diff --git a/testing.md b/testing.md index 7a8c89da306..d38515dc31a 100644 --- a/testing.md +++ b/testing.md @@ -58,7 +58,7 @@ php artisan make:test UserTest --pest php artisan make:test UserTest --unit --pest ``` -> **Note** +> [!NOTE] > 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](https://phpunit.de). To run your tests, execute the `vendor/bin/phpunit` or `php artisan test` command from your terminal: @@ -80,7 +80,7 @@ Once the test has been generated, you may define test methods as you normally wo } } -> **Warning** +> [!WARNING] > If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. @@ -121,7 +121,7 @@ By default, Laravel will create as many processes as there are available CPU cor php artisan test --parallel --processes=4 ``` -> **Warning** +> [!WARNING] > When running tests in parallel, some PHPUnit options (such as `--do-not-cache-result`) may not be available. @@ -191,7 +191,7 @@ If you would like to access the current parallel process "token" from any other ### Reporting Test Coverage -> **Warning** +> [!WARNING] > This feature requires [Xdebug](https://xdebug.org) or [PCOV](https://pecl.php.net/package/pcov). When running your application tests, you may want to determine whether your test cases are actually covering the application code and how much application code is used when running your tests. To accomplish this, you may provide the `--coverage` option when invoking the `test` command: diff --git a/upgrade.md b/upgrade.md index 412aed59728..8a46952f059 100644 --- a/upgrade.md +++ b/upgrade.md @@ -49,7 +49,7 @@ #### Estimated Upgrade Time: 10 Minutes -> **Note** +> [!NOTE] > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. diff --git a/valet.md b/valet.md index bb078f97cd2..5b42d8ffabc 100644 --- a/valet.md +++ b/valet.md @@ -22,7 +22,7 @@ ## Introduction -> **Note** +> [!NOTE] > Looking for an even easier way to develop Laravel applications on macOS? Check out [Laravel Herd](https://herd.laravel.com). Herd includes everything you need to get started with Laravel development, including Valet, PHP, and Composer. [Laravel Valet](https://github.com/laravel/valet) is a 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. @@ -69,7 +69,7 @@ However, you may extend Valet with your own [custom drivers](#custom-valet-drive ## Installation -> **Warning** +> [!WARNING] > 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. To get started, you first need to ensure that Homebrew is up to date using the `update` command: @@ -103,7 +103,7 @@ Valet will automatically start its required services each time your machine boot #### PHP Versions -> **Note** +> [!NOTE] > Instead of modifying your global PHP version, you can instruct Valet to use per-site PHP versions via the `isolate` [command](#per-site-php-versions). 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: @@ -122,7 +122,7 @@ php=php@8.1 Once this file has been created, you may simply execute the `valet use` command and the command will determine the site's preferred PHP version by reading the file. -> **Warning** +> [!WARNING] > Valet only serves one PHP version at a time, even if you have multiple PHP versions installed. @@ -294,7 +294,7 @@ valet share To stop sharing your site, you may press `Control + C`. -> **Warning** +> [!WARNING] > If you're using a custom DNS server (like `1.1.1.1`), ngrok sharing may not work correctly. If this is the case on your machine, open your Mac's system settings, go to the Network settings, open the Advanced settings, then go the DNS tab and add `127.0.0.1` as your first DNS server. @@ -306,7 +306,7 @@ Sharing your site using ngrok requires you to [create an ngrok account](https:// valet set-ngrok-token YOUR_TOKEN_HERE ``` -> **Note** +> [!NOTE] > 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). @@ -418,7 +418,7 @@ The `isStaticFile` should determine if the incoming request is for a file that i return false; } -> **Warning** +> [!WARNING] > The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. diff --git a/validation.md b/validation.md index 784b8a8beaf..6f309553f5c 100644 --- a/validation.md +++ b/validation.md @@ -202,7 +202,7 @@ Within the `lang/en/validation.php` file, you will find a translation entry for In addition, you may copy this file to another 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). -> **Warning** +> [!WARNING] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -321,7 +321,7 @@ As you might have guessed, the `authorize` method is responsible for determining ]; } -> **Note** +> [!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). 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: @@ -347,7 +347,7 @@ So, how are the validation rules evaluated? All you need to do is type-hint the 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](#validation-error-response-format). -> **Note** +> [!NOTE] > Need to add real-time form request validation to your Inertia powered Laravel frontend? Check out [Laravel Precognition](/docs/{{version}}/precognition). @@ -468,7 +468,7 @@ If you plan to handle authorization logic for the request in another part of you return true; } -> **Note** +> [!NOTE] > You may type-hint any dependencies you need within the `authorize` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). @@ -784,7 +784,7 @@ Within the `lang/en/validation.php` file, you will find a translation entry for In addition, you may copy this file to another 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). -> **Warning** +> [!WARNING] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -808,7 +808,7 @@ Many of Laravel's built-in error messages include an `:attribute` placeholder th 'email' => 'email address', ], -> **Warning** +> [!WARNING] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -834,7 +834,7 @@ Instead of displaying `cc` as the payment type value, you may specify a more use ], ], -> **Warning** +> [!WARNING] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. After defining this value, the validation rule will produce the following error message: @@ -1226,7 +1226,7 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida 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. -> **Warning** +> [!WARNING] > The `dns` and `spoof` validators require the PHP `intl` extension. @@ -1337,7 +1337,7 @@ The file under validation must have a user-assigned extension corresponding to o 'photo' => ['required', 'extensions:jpg,png'], -> **Warning** +> [!WARNING] > You should never rely on validating a file by its user-assigned extension alone. This rule should typically always be used in combination with the [`mimes`](#rule-mimes) or [`mimetypes`](#rule-mimetypes) rules. @@ -1412,7 +1412,7 @@ The field under validation must exist in _anotherfield_'s values. The field under validation must be an integer. -> **Warning** +> [!WARNING] > This validation rule does not verify that the input is of the "integer" variable type, only that the input is of a type accepted by PHP's `FILTER_VALIDATE_INT` rule. If you need to validate the input as being a number please use this rule in combination with [the `numeric` validation rule](#rule-numeric). @@ -1551,7 +1551,7 @@ 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'`. -> **Warning** +> [!WARNING] > 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. @@ -1569,7 +1569,7 @@ The field under validation must be [numeric](https://www.php.net/manual/en/funct The field under validation must match the authenticated user's password. -> **Warning** +> [!WARNING] > This rule was renamed to `current_password` with the intention of removing it in Laravel 9. Please use the [Current Password](#rule-current-password) rule instead. @@ -1673,7 +1673,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'`. -> **Warning** +> [!WARNING] > 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. @@ -1825,7 +1825,7 @@ To instruct the validator to ignore the user's ID, we'll use the `Rule` class to ], ]); -> **Warning** +> [!WARNING] > 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 also pass the entire model instance. Laravel will automatically extract the key from the model: @@ -1909,7 +1909,7 @@ In some situations, you may wish to run validation checks against a field **only In the example above, the `email` field will only be validated if it is present in the `$data` array. -> **Note** +> [!NOTE] > If you are attempting to validate a field that should always be present but may be empty, check out [this note on optional fields](#a-note-on-optional-fields). @@ -1938,7 +1938,7 @@ The first argument passed to the `sometimes` method is the name of the field we return $input->games >= 100; }); -> **Note** +> [!NOTE] > 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. @@ -2099,7 +2099,7 @@ If your application accepts images uploaded by your users, you may use the `File ], ]); -> **Note** +> [!NOTE] > More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). @@ -2368,5 +2368,5 @@ For a custom rule to run even when an attribute is empty, the rule must imply th php artisan make:rule Uppercase --implicit ``` -> **Warning** +> [!WARNING] > 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 99939443fc1..86da18c27fb 100644 --- a/verification.md +++ b/verification.md @@ -16,7 +16,7 @@ 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. -> **Note** +> [!NOTE] > 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. @@ -76,7 +76,7 @@ As mentioned previously, a route should be defined that will return a view instr The route that returns the email verification notice should be named `verification.notice`. It is important that the route is 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. -> **Note** +> [!NOTE] > 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). @@ -148,7 +148,7 @@ To get started, pass a closure to the `toMailUsing` method provided by the `Illu }); } -> **Note** +> [!NOTE] > To learn more about mail notifications, please consult the [mail notification documentation](/docs/{{version}}/notifications#mail-notifications). diff --git a/views.md b/views.md index c816c8a31fd..a6ffa5f9b2e 100644 --- a/views.md +++ b/views.md @@ -35,7 +35,7 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return return view('greeting', ['name' => 'James']); }); -> **Note** +> [!NOTE] > Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. @@ -77,7 +77,7 @@ Views may also be nested within subdirectories of the `resources/views` director return view('admin.profile', $data); -> **Warning** +> [!WARNING] > View directory names should not contain the `.` character. @@ -192,7 +192,7 @@ We'll use the `View` facade's `composer` method to register the view composer. L } } -> **Warning** +> [!WARNING] > 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 `compose` method of the `App\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: diff --git a/vite.md b/vite.md index 828bc4ae4d4..ddc56df1282 100644 --- a/vite.md +++ b/vite.md @@ -36,7 +36,7 @@ Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production. -> **Note** +> [!NOTE] > Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). @@ -54,7 +54,7 @@ Have you started a new Laravel application using our Vite scaffolding but need t ## Installation & Setup -> **Note** +> [!NOTE] > The following documentation discusses how to manually install and configure the Laravel Vite plugin. However, Laravel's [starter kits](/docs/{{version}}/starter-kits) already include all of this scaffolding and are the fastest way to get started with Laravel and Vite. @@ -346,7 +346,7 @@ export default defineConfig({ }); ``` -> **Note** +> [!NOTE] > Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Vue, and Vite. @@ -384,7 +384,7 @@ You will also need to include the additional `@viteReactRefresh` Blade directive The `@viteReactRefresh` directive must be called before the `@vite` directive. -> **Note** +> [!NOTE] > Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, React, and Vite. @@ -407,7 +407,7 @@ createInertiaApp({ }); ``` -> **Note** +> [!NOTE] > Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. @@ -454,7 +454,7 @@ export default { }; ``` -> **Note** +> [!NOTE] > Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Tailwind, PostCSS, and Vite configuration. Or, if you would like to use Tailwind and Laravel without using one of our starter kits, check out [Tailwind's installation guide for Laravel](https://tailwindcss.com/docs/guides/laravel). @@ -680,7 +680,7 @@ If you are using [SSR with Inertia](https://inertiajs.com/server-side-rendering) php artisan inertia:start-ssr ``` -> **Note** +> [!NOTE] > Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. @@ -806,7 +806,7 @@ Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, arr ]); ``` -> **Warning** +> [!WARNING] > The `$chunk` and `$manifest` arguments will be `null` while the Vite development server is running. @@ -885,4 +885,3 @@ Now, while Vite is serving Assets, it will output URLs that point to the Vite de - + ``` - From 0e0ba8b54483a4554bee9148e29ec79d6295c6da Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 10 Jan 2024 18:12:05 +0330 Subject: [PATCH 1286/2609] [11.x] Remove Doctrine DBAL (#9251) --- dusk.md | 6 ------ migrations.md | 47 ++--------------------------------------------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/dusk.md b/dusk.md index 1ee5ba5be73..e25ebd51f04 100644 --- a/dusk.md +++ b/dusk.md @@ -173,12 +173,6 @@ The `DatabaseMigrations` trait will run your database migrations before each tes #### Using Database Truncation -Before using the `DatabaseTruncation` trait, you must install the `doctrine/dbal` package using the Composer package manager: - -```shell -composer require --dev doctrine/dbal -``` - The `DatabaseTruncation` trait will migrate your database on the first test in order to ensure your database tables have been properly created. However, on subsequent tests, the database's tables will simply be truncated - providing a speed boost over re-running all of your database migrations: 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). +`->collation('utf8mb4_unicode_ci')` | Specify a collation for the column. `->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). `->invisible()` | Make the column "invisible" to `SELECT *` queries (MySQL). `->nullable($value = true)` | Allow NULL values to be inserted into the column. -`->storedAs($expression)` | Create a stored generated column (MySQL / PostgreSQL). +`->storedAs($expression)` | Create a stored generated column (MySQL / PostgreSQL / SQLite). `->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 (MySQL). @@ -1032,28 +1032,6 @@ When modifying a column, you must explicitly include all of the modifiers you wa $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); }); - -#### Modifying Columns on SQLite - -If your application is utilizing an SQLite database, you must install the `doctrine/dbal` package using the Composer package manager before modifying a column. 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 - -If you plan to modify columns created using the `timestamp` method, you must also add the following configuration to your application's `config/database.php` configuration file: - -```php -use Illuminate\Database\DBAL\TimestampType; - -'dbal' => [ - 'types' => [ - 'timestamp' => TimestampType::class, - ], -], -``` - -> [!WARNING] -> When using the `doctrine/dbal` package, the following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `double`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `tinyText`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, `ulid`, and `uuid`. - ### Renaming Columns @@ -1063,19 +1041,6 @@ To rename a column, you may use the `renameColumn` method provided by the schema $table->renameColumn('from', 'to'); }); - -#### Renaming Columns on Legacy Databases - -If you are running a database installation older than one of the following releases, you should ensure that you have installed the `doctrine/dbal` library via the Composer package manager before renaming a column: - -
    - -- MySQL < `8.0.3` -- MariaDB < `10.5.2` -- SQLite < `3.25.0` - -
    - ### Dropping Columns @@ -1091,11 +1056,6 @@ You may drop multiple columns from a table by passing an array of column names t $table->dropColumn(['votes', 'avatar', 'location']); }); - -#### Dropping Columns on Legacy Databases - -If you are running a version of SQLite prior to `3.35.0`, you must install the `doctrine/dbal` package via the Composer package manager before the `dropColumn` method may be used. Dropping or modifying multiple columns within a single migration while using this package is not supported. - #### Available Command Aliases @@ -1176,9 +1136,6 @@ To rename an index, you may use the `renameIndex` method provided by the schema $table->renameIndex('from', 'to') -> [!WARNING] -> If your application is utilizing an SQLite database, you must install the `doctrine/dbal` package via the Composer package manager before the `renameIndex` method may be used. - ### Dropping Indexes From 7eb8b6df95471a64238cb535df75deaaba8e0334 Mon Sep 17 00:00:00 2001 From: Will Taylor-Jackson Date: Thu, 11 Jan 2024 15:57:12 +0000 Subject: [PATCH 1287/2609] chore: update paths to Redis docs (#9256) --- redis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.md b/redis.md index 39f090460f5..4807ce752f5 100644 --- a/redis.md +++ b/redis.md @@ -13,7 +13,7 @@ ## Introduction -[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). +[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/docs/data-types/strings/), [hashes](https://redis.io/docs/data-types/hashes/), [lists](https://redis.io/docs/data-types/lists/), [sets](https://redis.io/docs/data-types/sets/), and [sorted sets](https://redis.io/docs/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 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. From 1de5c02a14e312fa9307effee5cf44e155781d87 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 11 Jan 2024 13:48:17 -0600 Subject: [PATCH 1288/2609] update docs for share context --- logging.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/logging.md b/logging.md index 02dac3790f1..c4c83ba8aeb 100644 --- a/logging.md +++ b/logging.md @@ -255,21 +255,34 @@ Occasionally, you may wish to specify some contextual information that should be } } -If you would like to share contextual information across _all_ logging channels, you may call the `Log::shareContext()` method. This method will provide the contextual information to all created channels and any channels that are created subsequently. Typically, the `shareContext` method should be called from the `boot` method of an application service provider: +If you would like to share contextual information across _all_ logging channels, you may call the `Log::shareContext()` method. This method will provide the contextual information to all created channels and any channels that are created subsequently: + (string) Str::uuid(), + $requestId = (string) Str::uuid(); + + Log::withContext([ + 'request-id' => $requestId ]); + + // ... } } From 48ff7f24f986865780bd67ec4f4d657d9e7779df Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 11 Jan 2024 13:48:29 -0600 Subject: [PATCH 1289/2609] fix method --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index c4c83ba8aeb..e7869ed3fb1 100644 --- a/logging.md +++ b/logging.md @@ -278,7 +278,7 @@ If you would like to share contextual information across _all_ logging channels, { $requestId = (string) Str::uuid(); - Log::withContext([ + Log::shareContext([ 'request-id' => $requestId ]); From 9a894e6c35f98dabad7e1028110263aba73b07e9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 11 Jan 2024 14:00:04 -0600 Subject: [PATCH 1290/2609] add note about job middleware --- logging.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/logging.md b/logging.md index e7869ed3fb1..c63f8d868c9 100644 --- a/logging.md +++ b/logging.md @@ -255,7 +255,7 @@ Occasionally, you may wish to specify some contextual information that should be } } -If you would like to share contextual information across _all_ logging channels, you may call the `Log::shareContext()` method. This method will provide the contextual information to all created channels and any channels that are created subsequently: +If you would like to share contextual information across _all_ logging channels, you may invoke the `Log::shareContext()` method. This method will provide the contextual information to all created channels and any channels that are created subsequently: [!NOTE] +> If you need to share log context while processing queued jobs, you may utilize [job middleware](/docs/{{version}}/queues#job-middleware). + ### Writing to Specific Channels From d6033dc56d10e954a4dad1c8a0ccddff4fa7c194 Mon Sep 17 00:00:00 2001 From: Girardot Charles-Louis <45538407+girardotcl@users.noreply.github.com> Date: Thu, 11 Jan 2024 21:58:24 +0100 Subject: [PATCH 1291/2609] Http Client attach method with third parameter (#9257) * Http Client attach method with third parameter * Update http-client.md --------- Co-authored-by: Taylor Otwell --- http-client.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-client.md b/http-client.md index 058ef9e6b88..43dfb9f004f 100644 --- a/http-client.md +++ b/http-client.md @@ -147,10 +147,10 @@ You may use the `withBody` method if you would like to provide a raw request bod #### Multi-Part Requests -If you would like to send files as multi-part requests, you should call the `attach` method before making your request. This method accepts the name of the file and its contents. If needed, you may provide a third argument which will be considered the file's filename: +If you would like to send files as multi-part requests, you should call the `attach` method before making your request. This method accepts the name of the file and its contents. If needed, you may provide a third argument which will be considered the file's filename, while a fourth argument may be used to provide headers associated with the file: $response = Http::attach( - 'attachment', file_get_contents('photo.jpg'), 'photo.jpg' + 'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg'] )->post('http://example.com/attachments'); Instead of passing the raw contents of a file, you may pass a stream resource: From d655507c36ef5f692f67cd091028758345ab2e53 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 13 Jan 2024 01:17:48 +1100 Subject: [PATCH 1292/2609] Document Vite::createAssetPathsUsing (#9238) --- vite.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vite.md b/vite.md index ddc56df1282..3acd2bb8f27 100644 --- a/vite.md +++ b/vite.md @@ -824,6 +824,9 @@ Out of the box, Laravel's Vite plugin uses sensible conventions that should work ->useBuildDirectory('bundle') // Customize the build directory... ->useManifestFilename('assets.json') // Customize the manifest filename... ->withEntryPoints(['resources/js/app.js']) // Specify the entry points... + ->createAssetPathsUsing(function (string $path, ?bool $secure) { // Customize the backend path generation for built assets... + return "https://cdn.example.com/%7B$path%7D"; + }) }} ``` From 04d6dfde9d25879ab98167c2285e4f875b429f2b Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Fri, 12 Jan 2024 15:32:06 +0100 Subject: [PATCH 1293/2609] Update auth code naming to api keys (#9258) --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 381016b9be8..839096d18b4 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -131,7 +131,7 @@ Next, you should configure your Paddle keys in your application's `.env` file. Y ```ini PADDLE_SELLER_ID=your-paddle-seller-id -PADDLE_AUTH_CODE=your-paddle-auth-code +PADDLE_API_KEY=your-paddle-api-key PADDLE_RETAIN_KEY=your-paddle-retain-key PADDLE_WEBHOOK_SECRET="your-paddle-webhook-secret" PADDLE_SANDBOX=true From 944c218697c1fc71a852e64076093b48719a73a5 Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Tue, 16 Jan 2024 16:17:21 +0100 Subject: [PATCH 1294/2609] Support eager loading with limit (#9261) --- eloquent-relationships.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index fb63b801882..088dc480544 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1739,9 +1739,6 @@ In this example, Eloquent will only eager load posts where the post's `title` co $query->orderBy('created_at', 'desc'); }])->get(); -> [!WARNING] -> The `limit` and `take` query builder methods may not be used when constraining eager loads. - #### Constraining Eager Loading of `morphTo` Relationships From 1b796f1f833ebc71d39e694180020529d1a7330d Mon Sep 17 00:00:00 2001 From: Jason Bosco Date: Tue, 16 Jan 2024 09:23:25 -0600 Subject: [PATCH 1295/2609] Docs for Typesense integration in Scout (#9250) * Docs for Typesense integration in Scout * formatting * Update scout.md --------- Co-authored-by: Taylor Otwell --- scout.md | 124 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 20 deletions(-) diff --git a/scout.md b/scout.md index 79a0ed199cd..946d4033930 100644 --- a/scout.md +++ b/scout.md @@ -2,8 +2,11 @@ - [Introduction](#introduction) - [Installation](#installation) - - [Driver Prerequisites](#driver-prerequisites) - [Queueing](#queueing) +- [Driver Prerequisites](#driver-prerequisites) + - [Algolia](#algolia) + - [Meilisearch](#meilisearch) + - [Typesense](#typesense) - [Configuration](#configuration) - [Configuring Model Indexes](#configuring-model-indexes) - [Configuring Searchable Data](#configuring-searchable-data) @@ -32,7 +35,7 @@ [Laravel Scout](https://github.com/laravel/scout) provides a simple, driver based solution for adding full-text search to your [Eloquent models](/docs/{{version}}/eloquent). Using model observers, Scout will automatically keep your search indexes in sync with your Eloquent records. -Currently, Scout ships with [Algolia](https://www.algolia.com/), [Meilisearch](https://www.meilisearch.com), and MySQL / PostgreSQL (`database`) drivers. In addition, Scout includes a "collection" driver that is designed for local development usage and does not require any external dependencies or third-party services. Furthermore, writing custom drivers is simple and you are free to extend Scout with your own search implementations. +Currently, Scout ships with [Algolia](https://www.algolia.com/), [Meilisearch](https://www.meilisearch.com), [Typesense](https://typesense.org), and MySQL / PostgreSQL (`database`) drivers. In addition, Scout includes a "collection" driver that is designed for local development usage and does not require any external dependencies or third-party services. Furthermore, writing custom drivers is simple and you are free to extend Scout with your own search implementations. ## Installation @@ -63,11 +66,33 @@ Finally, add the `Laravel\Scout\Searchable` trait to the model you would like to use Searchable; } + +### 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, + +Even when the `queue` option is set to `false`, it's important to remember that some Scout drivers like Algolia and Meilisearch always index records asynchronously. Meaning, even though the index operation has completed within your Laravel application, the search engine itself may not reflect the new and updated records immediately. + +To specify the connection and queue that your Scout jobs utilize, you may define the `queue` configuration option as an array: + + 'queue' => [ + 'connection' => 'redis', + 'queue' => 'scout' + ], + +Of course, if you customize the connection and queue that Scout jobs utilize, you should run a queue worker to process jobs on that connection and queue: + + php artisan queue:work redis --queue=scout + -### Driver Prerequisites +## Driver Prerequisites -#### Algolia +### Algolia When using the Algolia driver, you should configure your Algolia `id` and `secret` credentials in your `config/scout.php` configuration file. Once your credentials have been configured, you will also need to install the Algolia PHP SDK via the Composer package manager: @@ -76,7 +101,7 @@ composer require algolia/algoliasearch-client-php ``` -#### Meilisearch +### Meilisearch [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. @@ -101,27 +126,86 @@ In addition, you should ensure that you install a version of `meilisearch/meilis > [!WARNING] > When upgrading Scout on an application that utilizes Meilisearch, you should always [review any additional breaking changes](https://github.com/meilisearch/Meilisearch/releases) to the Meilisearch service itself. - -### Queueing + +### Typesense -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. +[Typesense](https://typesense.org) is a lightning-fast, open source search engine and supports keyword search, semantic search, geo search, and vector search. -Once you have configured a queue driver, set the value of the `queue` option in your `config/scout.php` configuration file to `true`: +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). - 'queue' => true, +To get started using Typesense with Scout, install the Typesense PHP SDK via the Composer package manager: -Even when the `queue` option is set to `false`, it's important to remember that some Scout drivers like Algolia and Meilisearch always index records asynchronously. Meaning, even though the index operation has completed within your Laravel application, the search engine itself may not reflect the new and updated records immediately. +```shell +composer require typesense/typesense-php +``` -To specify the connection and queue that your Scout jobs utilize, you may define the `queue` configuration option as an array: +Then, set the `SCOUT_DRIVER` environment variable as well as your Typesense host and API key credentials within your application's .env file: - 'queue' => [ - 'connection' => 'redis', - 'queue' => 'scout' +```env +SCOUT_DRIVER=typesense +TYPESENSE_API_KEY=masterKey +TYPESENSE_HOST=localhost +``` + +If needed, you may also specify your installation's port, path, and protocol: + +```env +TYPESENSE_PORT=8108 +TYPESENSE_PATH= +TYPESENSE_PROTOCOL=http +``` + +Additional settings and schema definitions for your Typesense collections can be found within your application's `config/scout.php` configuration file. For more information regarding Typesense, please consult the [Typesense documentation](https://typesense.org/docs/guide/#quick-start). + + +#### Preparing Data for Storage in Typesense + +When utilizing Typesense, your searchable model's must define a `toSearchableArray` method that casts your model's primary key to a string and creation date to a UNIX timestamp: + +```php +/** + * Get the indexable data array for the model. + * + * @return array + */ +public function toSearchableArray() +{ + return array_merge($this->toArray(),[ + 'id' => (string) $this->id, + 'created_at' => $this->created_at->timestamp, + ]); +} +``` + +If your searchable model is soft deletable, you should define a `__soft_deleted` field in the model's corresponding Typesense schema within your application's `config/scout.php` configuration file: + +```php +User::class => [ + 'collection-schema' => [ + 'fields' => [ + // ... + [ + 'name' => '__soft_deleted', + 'type' => 'int32', + 'optional' => true, + ], + ], ], +], +``` -Of course, if you customize the connection and queue that Scout jobs utilize, you should run a queue worker to process jobs on that connection and queue: + +#### Dynamic Search Parameters - php artisan queue:work redis --queue=scout +Typesense allows you to modify your [search parameters](https://typesense.org/docs/latest/api/search.html#search-parameters) dynamically when performing a search operation via the `withSearchParameters` method: + +```php +use App\Models\Todo; + +Todo::search('Groceries')->withSearchParameters([ + 'query_by' => 'title, description' +])->get(); +``` ## Configuration @@ -323,7 +407,7 @@ To use the database engine, you may simply set the value of the `SCOUT_DRIVER` e SCOUT_DRIVER=database ``` -Once you have specified the database engine as your preferred driver, you must [configure your searchable data](#configuring-searchable-data). Then, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia or Meilisearch indexes, is unnecessary when using the database engine. +Once you have specified the database engine as your preferred driver, you must [configure your searchable data](#configuring-searchable-data). Then, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia, Meilisearch or Typesense indexes, is unnecessary when using the database engine. #### Customizing Database Searching Strategies @@ -359,7 +443,7 @@ public function toSearchableArray(): array ### Collection Engine -While you are free to use the Algolia or Meilisearch search engines during local development, you may find it more convenient to get started with the "collection" engine. The collection engine will use "where" clauses and collection filtering on results from your existing database to determine the applicable search results for your query. When using this engine, it is not necessary to "index" your searchable models, as they will simply be retrieved from your local database. +While you are free to use the Algolia, Meilisearch, or Typesense search engines during local development, you may find it more convenient to get started with the "collection" engine. The collection engine will use "where" clauses and collection filtering on results from your existing database to determine the applicable search results for your query. When using this engine, it is not necessary to "index" your searchable models, as they will simply be retrieved from your local database. To use the collection engine, you may simply set the value of the `SCOUT_DRIVER` environment variable to `collection`, or specify the `collection` driver directly in your application's `scout` configuration file: @@ -367,7 +451,7 @@ To use the collection engine, you may simply set the value of the `SCOUT_DRIVER` SCOUT_DRIVER=collection ``` -Once you have specified the collection driver as your preferred driver, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia or Meilisearch indexes, is unnecessary when using the collection engine. +Once you have specified the collection driver as your preferred driver, you may start [executing search queries](#searching) against your models. Search engine indexing, such as the indexing needed to seed Algolia, Meilisearch, or Typesense indexes, is unnecessary when using the collection engine. #### Differences From Database Engine From e27e913397028fe76311b17571b2b9cbe272183c Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 16 Jan 2024 16:44:17 +0100 Subject: [PATCH 1296/2609] [10.x] Cashier Paddle quickstart docs (#9259) * Cashier Paddle quickstart docs * wip * wip * formatting --------- Co-authored-by: Taylor Otwell --- cashier-paddle.md | 224 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 2 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 839096d18b4..a903d939df5 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -10,6 +10,9 @@ - [Paddle JS](#paddle-js) - [Currency Configuration](#currency-configuration) - [Overriding Default Models](#overriding-default-models) +- [Quickstart](#quickstart) + - [Selling Products](#quickstart-selling-products) + - [Selling Subscriptions](#quickstart-selling-subscriptions) - [Checkout Sessions](#checkout-sessions) - [Overlay Checkout](#overlay-checkout) - [Inline Checkout](#inline-checkout) @@ -192,17 +195,234 @@ After defining your model, you may instruct Cashier to use your custom model via Cashier::useTransactionModel(Transaction::class); } + +## Quickstart + + +### Selling Products + +> [!NOTE] +> Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should [configure Paddle's webhook handling](#handling-paddle-webhooks). + +Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://www.paddle.com/billing/checkout), you can easily build modern, robust payment integrations. + +To charge customers for non-recurring, single-charge products, we'll utilize Cashier to charge customers with Paddle's Checkout Overlay, where they will provide their payment details and confirm their purchase. Once the payment has been made via the Checkout Overlay, the customer will be redirected to a success URL of your choosing within your application: + + use Illuminate\Http\Request; + + Route::get('/buy', function (Request $request) { + $checkout = $request->user()->checkout('pri_deluxe_album') + ->returnTo(route('dashboard')); + + return view('buy', ['checkout' => $checkout]); + })->name('checkout'); + +As you can see in the example above, we will utilize Cashier's provided `checkout` method to create a checkout object to present the customer the Paddle Checkout Overlay for a given "price identifier". When using Paddle, "prices" refer to [defined prices for specific products](https://developer.paddle.com/build/products/create-products-prices). + +If necessary, the `checkout` method will automatically create a customer in Paddle and connect that Paddle customer record to the corresponding user in your application's database. After completing the checkout session, the customer will be redirected to a dedicated success page where you can display an informational message to the customer. + +In the `buy` view, we will include a button to display the Checkout Overlay. The `paddle-button` Blade component is included with Cashier Paddle; however, you may also [manually render an overlay checkout](#manually-rendering-an-overlay-checkout): + +```html + + Buy Product + +``` + + +#### Providing Meta Data to Paddle Checkout + +When selling products, it's common to keep track of completed orders and purchased products via `Cart` and `Order` models defined by your own application. When redirecting customers to Paddle's Checkout Overlay to complete a purchase, you may need to provide an existing order identifier so that you can associate the completed purchase with the corresponding order when the customer is redirected back to your application. + +To accomplish this, you may provide an array of custom data to the `checkout` method. Let's imagine that a pending `Order` is created within our application when a user begins the checkout process. Remember, the `Cart` and `Order` models in this example are illustrative and not provided by Cashier. You are free to implement these concepts based on the needs of your own application: + + use App\Models\Cart; + use App\Models\Order; + use Illuminate\Http\Request; + + Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) { + $order = Order::create([ + 'cart_id' => $cart->id, + 'price_ids' => $cart->price_ids, + 'status' => 'incomplete', + ]); + + $checkout = $request->user()->checkout($order->price_ids) + ->customData(['order_id' => $order->id]); + + return view('billing', ['checkout' => $checkout]); + })->name('checkout'); + +As you can see in the example above, when a user begins the checkout process, we will provide all of the cart / order's associated Paddle price identifiers to the `checkout` method. Of course, your application is responsible for associating these items with the "shopping cart" or order as a customer adds them. We also provide the order's ID to the Paddle Checkout Overlay via the `customData` method. + +Of course, you will likely want to mark the order as "complete" once the customer has finished the checkout process. To accomplish this, you may listen to the webhooks dispatched by Paddle and raised via events by Cashier to store order information in your database. + +To get started, listen for the `TransactionCompleted` event dispatched by Cashier. Typically, you should register the event listener in the `boot` method of one of your application's service providers: + + use App\Listeners\CompleteOrder; + use Illuminate\Support\Facades\Event; + use Laravel\Paddle\Events\TransactionCompleted; + + /** + * Bootstrap any application services. + */ + public function boot(): void + { + Event::listen(TransactionCompleted::class, CompleteOrder::class); + } + +In this example, the `CompleteOrder` listener might look like the following: + + namespace App\Listeners; + + use App\Models\Order; + use Laravel\Cashier\Cashier; + use Laravel\Cashier\Events\TransactionCompleted; + + class CompleteOrder + { + /** + * Handle the incoming Cashier webhook event. + */ + public function handle(TransactionCompleted $event): void + { + $orderId = $event->payload['data']['custom_data']['order_id'] ?? null; + + $order = Order::findOrFail($orderId); + + $order->update(['status' => 'completed']); + } + } + +Please refer to Paddle's documentation for more information on the [data contained by the `transaction.completed` event](https://developer.paddle.com/webhooks/transactions/transaction-completed). + + +### Selling Subscriptions + +> [!NOTE] +> Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should [configure Paddle's webhook handling](#handling-paddle-webhooks). + +Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://www.paddle.com/billing/checkout), you can easily build modern, robust payment integrations. + +To learn how to sell subscriptions using Cashier and Paddle's Checkout Overlay, let's consider the simple scenario of a subscription service with a basic monthly (`price_basic_monthly`) and yearly (`price_basic_yearly`) plan. These two prices could be grouped under a "Basic" product (`pro_basic`) in our Paddle dashboard. In addition, our subscription service might offer an Expert plan as `pro_expert`. + +First, let's discover how a customer can subscribe to our services. Of course, you can imagine the customer might click a "subscribe" button for the Basic plan on our application's pricing page. This button will invoke a Paddle Checkout Overlay for their chosen plan. To get started, let's initiate a checkout session via the `checkout` method: + + use Illuminate\Http\Request; + + Route::get('/subscribe', function (Request $request) { + $checkout = $request->user()->checkout('price_basic_monthly') + ->returnTo(route('dashboard')); + + return view('subscribe', ['checkout' => $checkout]); + })->name('subscribe'); + +In the `subscribe` view, we will include a button to display the Checkout Overlay. The `paddle-button` Blade component is included with Cashier Paddle; however, you may also [manually render an overlay checkout](#manually-rendering-an-overlay-checkout): + +```html + + Subscribe + +``` + +Now, when the Subscribe button is clicked, the customer will be able to enter their payment details and initiate their subscription. To know when their subscription has actually started (since some payment methods require a few seconds to process), you should also [configure Cashier's webhook handling](#handling-paddle-webhooks). + +Now that customers can start subscriptions, we need to restrict certain portions of our application so that only subscribed users can access them. Of course, we can always determine a user's current subscription status via the `subscribed` method provided by Cashier's `Billable` trait: + +```blade +@if ($user->subscribed()) +

    You are subscribed.

    +@endif +``` + +We can even easily determine if a user is subscribed to specific product or price: + +```blade +@if ($user->subscribedToProduct('pro_basic')) +

    You are subscribed to our Basic product.

    +@endif + +@if ($user->subscribedToPrice('price_basic_monthly')) +

    You are subscribed to our monthly Basic plan.

    +@endif +``` + + +#### Building a Subscribed Middleware + +For convenience, you may wish to create a [middleware](/docs/{{version}}/middleware) which determines if the incoming request is from a subscribed user. Once this middleware has been defined, you may easily assign it to a route to prevent users that are not subscribed from accessing the route: + + user()?->subscribed()) { + // Redirect user to billing page and ask them to subscribe... + return redirect('/subscribe'); + } + + return $next($request); + } + } + +Once the middleware has been defined, you may assign it to a route: + + use App\Http\Middleware\Subscribed; + + Route::get('/dashboard', function () { + // ... + })->middleware([Subscribed::class]); + + +#### Allowing Customers to Manage Their Billing Plan + +Of course, customers may want to change their subscription plan to another product or "tier". In our example from above, we'd want to allow the customer to change their plan from a monthly subscription to a yearly subscription. For this you'll need to implement something like a button that leads to the below route: + + use Illuminate\Http\Request; + + Route::put('/subscription/{price}/swap', function (Request $request, $price) { + $user->subscription()->swap($price); // With "$price" being "price_basic_yearly" for this example. + + return redirect()->route('dashboard'); + })->name('subscription.swap'); + +Besides swapping plans you'll also need to allow your customers to cancel their subscription. Like swapping plans, provide a button that leads to the following route: + + use Illuminate\Http\Request; + + Route::put('/subscription/cancel', function (Request $request, $price) { + $user->subscription()->cancel(); + + return redirect()->route('dashboard'); + })->name('subscription.cancel'); + +And now your subscription will get cancelled at the end of its billing period. + +> [!NOTE] +> As long as you have configured Cashier's webhook handling, Cashier will automatically keep your application's Cashier-related database tables in sync by inspecting the incoming webhooks from Paddle. So, for example, when you cancel a customer's subscription via Paddle's dashboard, Cashier will receive the corresponding webhook and mark the subscription as "cancelled" in your application's database. + ## Checkout Sessions -Most operations to bill customers are performed using "checkouts" via Paddle's [checkout overlay widget](https://developer.paddle.com/build/checkout/build-overlay-checkout) or by utilizing [inline checkout](https://developer.paddle.com/build/checkout/build-branded-inline-checkout). +Most operations to bill customers are performed using "checkouts" via Paddle's [Checkout Overlay widget](https://developer.paddle.com/build/checkout/build-overlay-checkout) or by utilizing [inline checkout](https://developer.paddle.com/build/checkout/build-branded-inline-checkout). Before processing checkout payments using Paddle, you should define your application's [default payment link](https://developer.paddle.com/build/transactions/default-payment-link#set-default-link) in your Paddle checkout settings dashboard. ### Overlay Checkout -Before displaying the checkout overlay widget, you must generate a checkout session using Cashier. A checkout session will inform the checkout widget of the billing operation that should be performed: +Before displaying the Checkout Overlay widget, you must generate a checkout session using Cashier. A checkout session will inform the checkout widget of the billing operation that should be performed: use Illuminate\Http\Request; From 4f8109fcc7f90aeec58214759b52ff5aa4dd148c Mon Sep 17 00:00:00 2001 From: Brendan Petty - Totally Web Services <40818011+brendanpetty@users.noreply.github.com> Date: Fri, 19 Jan 2024 01:23:41 +1100 Subject: [PATCH 1297/2609] Increasing clarity of where to call parent::tearDown() (#9263) * Increasing clarity of where to call parent::tearDown() * Update testing.md --------- Co-authored-by: Taylor Otwell --- testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing.md b/testing.md index d38515dc31a..69ac8ac9284 100644 --- a/testing.md +++ b/testing.md @@ -81,7 +81,7 @@ Once the test has been generated, you may define test methods as you normally wo } > [!WARNING] -> If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. +> If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. Typically, you should invoke `parent::setUp()` at the start of your own `setUp` method, and `parent::tearDown()` at the end of your `tearDown` method. ## Running Tests From 79c7dda6322bd8319042831e5d185379980123fa Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 18 Jan 2024 12:48:56 -0600 Subject: [PATCH 1298/2609] add warning about Inline Authorization (#9265) * add warning about Inline Authorization Inline authorization does not execute your "before" and "after" checks, so we'll add a warning about it. This has bitten me a couple times, so figured I'd make it explicit. * Update authorization.md --------- Co-authored-by: Taylor Otwell --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index 07227cd5eac..52d30ff5ecc 100644 --- a/authorization.md +++ b/authorization.md @@ -245,7 +245,7 @@ Similar to the `before` method, if the `after` closure returns a non-null result ### Inline Authorization -Occasionally, you may wish to determine if the currently authenticated user is authorized to perform a given action without writing a dedicated gate that corresponds to the action. Laravel allows you to perform these types of "inline" authorization checks via the `Gate::allowIf` and `Gate::denyIf` methods: +Occasionally, you may wish to determine if the currently authenticated user is authorized to perform a given action without writing a dedicated gate that corresponds to the action. Laravel allows you to perform these types of "inline" authorization checks via the `Gate::allowIf` and `Gate::denyIf` methods. Inline authorization does not execute any defined ["before" or "after" authorization hooks](#intercepting-gate-checks): ```php use App\Models\User; From 74608b5f3fb92bc42bd1ff2a4563d457cc11bc18 Mon Sep 17 00:00:00 2001 From: Arne_ Date: Fri, 19 Jan 2024 14:29:39 +0100 Subject: [PATCH 1299/2609] Removed redundant entry for password rule (#9267) --- validation.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/validation.md b/validation.md index 6f309553f5c..19f4f192ded 100644 --- a/validation.md +++ b/validation.md @@ -934,7 +934,6 @@ Below is a list of all available validation rules and their function: [Not Regex](#rule-not-regex) [Nullable](#rule-nullable) [Numeric](#rule-numeric) -[Password](#rule-password) [Present](#rule-present) [Present If](#rule-present-if) [Present Unless](#rule-present-unless) @@ -1564,14 +1563,6 @@ The field under validation may be `null`. The field under validation must be [numeric](https://www.php.net/manual/en/function.is-numeric.php). - -#### password - -The field under validation must match the authenticated user's password. - -> [!WARNING] -> This rule was renamed to `current_password` with the intention of removing it in Laravel 9. Please use the [Current Password](#rule-current-password) rule instead. - #### present From f5947e2d5025fbe4f40410f97f0393aea2c466e1 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 19 Jan 2024 17:32:06 +0330 Subject: [PATCH 1300/2609] [11.x] Make spatial types consistent (#9266) * fix spatial types * add upgrade guid * formatting * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- migrations.md | 71 ++++++++++----------------------------------------- upgrade.md | 25 ++++++++++++++++++ 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/migrations.md b/migrations.md index 12e97462730..a58ee60f938 100644 --- a/migrations.md +++ b/migrations.md @@ -410,7 +410,7 @@ The schema builder blueprint offers a variety of methods that correspond to the [foreignIdFor](#column-method-foreignIdFor) [foreignUlid](#column-method-foreignUlid) [foreignUuid](#column-method-foreignUuid) -[geometryCollection](#column-method-geometryCollection) +[geography](#column-method-geography) [geometry](#column-method-geometry) [id](#column-method-id) [increments](#column-method-increments) @@ -418,22 +418,16 @@ The schema builder blueprint offers a variety of methods that correspond to the [ipAddress](#column-method-ipAddress) [json](#column-method-json) [jsonb](#column-method-jsonb) -[lineString](#column-method-lineString) [longText](#column-method-longText) [macAddress](#column-method-macAddress) [mediumIncrements](#column-method-mediumIncrements) [mediumInteger](#column-method-mediumInteger) [mediumText](#column-method-mediumText) [morphs](#column-method-morphs) -[multiLineString](#column-method-multiLineString) -[multiPoint](#column-method-multiPoint) -[multiPolygon](#column-method-multiPolygon) [nullableMorphs](#column-method-nullableMorphs) [nullableTimestamps](#column-method-nullableTimestamps) [nullableUlidMorphs](#column-method-nullableUlidMorphs) [nullableUuidMorphs](#column-method-nullableUuidMorphs) -[point](#column-method-point) -[polygon](#column-method-polygon) [rememberToken](#column-method-rememberToken) [set](#column-method-set) [smallIncrements](#column-method-smallIncrements) @@ -546,7 +540,7 @@ The `enum` method creates a `ENUM` equivalent column with the given valid values The `float` method creates a `FLOAT` equivalent column with the given precision: - $table->float('amount', 53); + $table->float('amount', $precision = 53); #### `foreignId()` {.collection-method} @@ -576,19 +570,25 @@ The `foreignUuid` method creates a `UUID` equivalent column: $table->foreignUuid('user_id'); - -#### `geometryCollection()` {.collection-method} + +#### `geography()` {.collection-method} -The `geometryCollection` method creates a `GEOMETRYCOLLECTION` equivalent column: +The `geography` method creates a `GEOGRAPHY` equivalent column with the given spatial type and SRID (Spatial Reference System Identifier): - $table->geometryCollection('positions'); + $table->geography('coordinates', subtype: 'point', srid: 4326); + +> [!NOTE] +> Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the [PostGIS](https://postgis.net) extension before the `geography` method may be used. #### `geometry()` {.collection-method} -The `geometry` method creates a `GEOMETRY` equivalent column: +The `geometry` method creates a `GEOMETRY` equivalent column with the given spatial type and SRID (Spatial Reference System Identifier): + + $table->geometry('positions', subtype: 'point', srid: 0); - $table->geometry('positions'); +> [!NOTE] +> Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the [PostGIS](https://postgis.net) extension before the `geometry` method may be used. #### `id()` {.collection-method} @@ -634,13 +634,6 @@ The `jsonb` method creates a `JSONB` equivalent column: $table->jsonb('options'); - -#### `lineString()` {.collection-method} - -The `lineString` method creates a `LINESTRING` equivalent column: - - $table->lineString('positions'); - #### `longText()` {.collection-method} @@ -685,27 +678,6 @@ This method is intended to be used when defining the columns necessary for a pol $table->morphs('taggable'); - -#### `multiLineString()` {.collection-method} - -The `multiLineString` method creates a `MULTILINESTRING` equivalent column: - - $table->multiLineString('positions'); - - -#### `multiPoint()` {.collection-method} - -The `multiPoint` method creates a `MULTIPOINT` equivalent column: - - $table->multiPoint('positions'); - - -#### `multiPolygon()` {.collection-method} - -The `multiPolygon` method creates a `MULTIPOLYGON` equivalent column: - - $table->multiPolygon('positions'); - #### `nullableTimestamps()` {.collection-method} @@ -734,20 +706,6 @@ The method is similar to the [uuidMorphs](#column-method-uuidMorphs) method; how $table->nullableUuidMorphs('taggable'); - -#### `point()` {.collection-method} - -The `point` method creates a `POINT` equivalent column: - - $table->point('position'); - - -#### `polygon()` {.collection-method} - -The `polygon` method creates a `POLYGON` equivalent column: - - $table->polygon('position'); - #### `rememberToken()` {.collection-method} @@ -974,7 +932,6 @@ Modifier | Description `->virtualAs($expression)` | Create a virtual generated column (MySQL / PostgreSQL / SQLite). `->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). -`->isGeometry()` | Set spatial column type to `geometry` - the default type is `geography` (PostgreSQL). #### Default Expressions diff --git a/upgrade.md b/upgrade.md index c9af938aebf..a9ce1d81a6f 100644 --- a/upgrade.md +++ b/upgrade.md @@ -18,6 +18,7 @@
    - [The `Enumerable` Contract](#the-enumerable-contract) +- [Spatial Types](#spatial-types)
    @@ -62,3 +63,27 @@ The `dump` method of the `Illuminate\Support\Enumerable` contract has been updat ```php public function dump(...$args); ``` + + +### Database + + +#### Spatial Types + +**Likelihood Of Impact: Low** + +The spatial column types of database migrations have been rewritten to be consistent across all databases. Therefore, you may remove `point`, `lineString`, `polygon`, `geometryCollection`, `multiPoint`, `multiLineString`, `multiPolygon`, and `multiPolygonZ` methods from your migrations and use `geometry` or `geography` methods instead: + +```php +$table->geometry('shapes'); +$table->geography('coordinates'); +``` + +To explicitly restrict the type or the spatial reference system identifier for values stored in the column on MySQL and PostgreSQL, you may pass the `subtype` and `srid` to the method: + +```php +$table->geometry('dimension', subtype: 'polygon', srid: 0); +$table->geography('latitude', subtype: 'point', srid: 4326); +``` + +The `isGeometry` and `projection` column modifiers of the PostgreSQL grammar have been removed accordingly. From 44f3c0042dcc293df7edeb0842c89aa8d465f2ad Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 19 Jan 2024 15:48:53 -0600 Subject: [PATCH 1301/2609] [10.x] updates for the new Settler box and Homestead version (#9269) * updates for the new Settler box and Homestead version - Mailhog is gone. Mailpit is the default and only option now. - Ubuntu is now version 22.04 - PostgreSQL is no version 16 - The "mysql" feature flag never actually did anything, so we drop it. - Add PHP 8.3 * switch back to PostgreSQL 15 the base box specifically has v15 in it. --- homestead.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/homestead.md b/homestead.md index e27086e85e8..b61bfb3d7ef 100644 --- a/homestead.md +++ b/homestead.md @@ -20,7 +20,7 @@ - [Connecting to Databases](#connecting-to-databases) - [Database Backups](#database-backups) - [Configuring Cron Schedules](#configuring-cron-schedules) - - [Configuring MailHog](#configuring-mailhog) + - [Configuring Mailpit](#configuring-mailpit) - [Configuring Minio](#configuring-minio) - [Laravel Dusk](#laravel-dusk) - [Sharing Your Environment](#sharing-your-environment) @@ -36,7 +36,7 @@ ## Introduction -Laravel strives to make the entire PHP development experience delightful, including your local development environment. [Laravel Homestead](https://github.com/laravel/homestead) is an official, pre-packaged Vagrant box that provides you a wonderful development environment without requiring you to install PHP, a web server, and any other server software on your local machine. +Laravel strives to make the entire PHP development experience delightful, including your local development environment. [Laravel Homestead](https://github.com/laravel/homestead) is an official, pre-packaged Vagrant box that provides you a wonderful development environment without requiring you to install PHP, a web server, or any other server software on your local machine. [Vagrant](https://www.vagrantup.com) provides a simple, elegant way to manage and provision Virtual Machines. Vagrant boxes are completely disposable. If something goes wrong, you can destroy and re-create the box in minutes! @@ -58,8 +58,9 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M
    -- Ubuntu 20.04 +- Ubuntu 22.04 - Git +- PHP 8.3 - PHP 8.2 - PHP 8.1 - PHP 8.0 @@ -80,7 +81,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Redis - Memcached - Beanstalkd -- Mailhog +- Mailpit - avahi - ngrok - Xdebug @@ -116,7 +117,6 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Grafana - InfluxDB - Logstash -- Mailpit (Replaces Mailhog) - MariaDB - Meilisearch - MinIO @@ -346,12 +346,10 @@ features: - grafana: true - influxdb: true - logstash: true - - mailpit: true - mariadb: true - meilisearch: true - minio: true - mongodb: true - - mysql: true - neo4j: true - ohmyzsh: true - openresty: true @@ -558,7 +556,7 @@ Below is a list of additional Homestead service ports that you may wish to map f - **MySQL:** 33060 → To 3306 - **PostgreSQL:** 54320 → To 5432 - **MongoDB:** 27017 → To 27017 -- **Mailhog:** 8025 → To 8025 +- **Mailpit:** 8025 → To 8025 - **Minio:** 9600 → To 9600
    @@ -566,7 +564,7 @@ Below is a list of additional Homestead service ports that you may wish to map f ### PHP Versions -Homestead supports running multiple versions of PHP on the same virtual machine. You may specify which version of PHP to use for a given site within your `Homestead.yaml` file. The available PHP versions are: "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", and "8.2" (the default): +Homestead supports running multiple versions of PHP on the same virtual machine. You may specify which version of PHP to use for a given site within your `Homestead.yaml` file. The available PHP versions are: "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", and "8.3", (the default): ```yaml sites: @@ -587,6 +585,7 @@ php7.4 artisan list php8.0 artisan list php8.1 artisan list php8.2 artisan list +php8.3 artisan list ``` You may change the default version of PHP used by the CLI by issuing the following commands from within your Homestead virtual machine: @@ -601,6 +600,7 @@ php74 php80 php81 php82 +php83 ``` @@ -636,10 +636,10 @@ sites: The cron job for the site will be defined in the `/etc/cron.d` directory of the Homestead virtual machine. - -### Configuring MailHog + +### Configuring Mailpit -[MailHog](https://github.com/mailhog/MailHog) allows you to intercept your outgoing email and examine it without actually sending the mail to its recipients. To get started, update your application's `.env` file to use the following mail settings: +[Mailpit](https://github.com/axllent/mailpit) allows you to intercept your outgoing email and examine it without actually sending the mail to its recipients. To get started, update your application's `.env` file to use the following mail settings: ```ini MAIL_MAILER=smtp @@ -650,7 +650,7 @@ MAIL_PASSWORD=null MAIL_ENCRYPTION=null ``` -Once MailHog has been configured, you may access the MailHog dashboard at `http://localhost:8025`. +Once Mailpit has been configured, you may access the Mailpit dashboard at `http://localhost:8025`. ### Configuring Minio From 3e43a0e36e6a675badc6fe377e4a8b5b58e5c7aa Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 20 Jan 2024 23:09:13 +0330 Subject: [PATCH 1302/2609] [11.x] Schema Upgrade Guide (#9268) * wip * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- upgrade.md | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/upgrade.md b/upgrade.md index a9ce1d81a6f..bc8d7b157c1 100644 --- a/upgrade.md +++ b/upgrade.md @@ -9,6 +9,17 @@ - [Updating Dependencies](#updating-dependencies) - [Updating Minimum Stability](#updating-minimum-stability) +- [SQLite Minimum Version](#sqlite-minimum-version) + +
    + + +## Medium Impact Changes + +
    + +- [Modifying Columns](#modifying-columns) +- [Floating-Point Types](#floating-point-types)
    @@ -19,6 +30,7 @@ - [The `Enumerable` Contract](#the-enumerable-contract) - [Spatial Types](#spatial-types) +- [Doctrine DBAL Removal](#doctrine-dbal-removal)
    @@ -50,6 +62,8 @@ You should update the following dependencies in your application's `composer.jso
    +In addition, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. + ### Collections @@ -67,6 +81,53 @@ public function dump(...$args); ### Database + +#### SQLite 3.35.0+ + +**Likelihood Of Impact: High** + +If your application is utilizing an SQLite database, SQLite 3.35.0 or greater is required. + + +#### Modifying Columns + +**Likelihood Of Impact: Medium** + +When modifying a column, you must now explicitly include all the modifiers you want to keep on the column definition after it is changed. Any missing attributes will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column, even if those attributes have been assigned to the column by a previous migration: + +```php +Schema::table('users', function (Blueprint $table) { + $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); +}); +``` + + +#### Floating-Point Types + +**Likelihood Of Impact: Medium** + +The `double` and `float` migration column types have been rewritten to be consistent across all databases. + +The `double` column type now creates a `DOUBLE` equivalent column without total digits and places (digits after decimal point), which is the standard SQL syntax. Therefore, you may remove the arguments for `$total` and `$places`: + +```php +$table->double('amount'); +``` + +The `float` column type now creates a `FLOAT` equivalent column without total digits and places (digits after decimal point), but with an optional `$precision` specification to determine storage size as a 4-byte single-precision column or an 8-byte double-precision column. Therefore, you may remove the arguments for `$total` and `$places` and specify the optional `$precision` to your desired value and according to your database's documentation: + +```php +$table->float('amount', precision: 53); +``` + +The `unsignedDecimal`, `unsignedDouble`, and `unsignedFloat` methods have been removed, as the unsigned modifier for these column types has been deprecated by MySQL, and was never standardized on other database systems. However, if you wish to continue using the deprecated unsigned attribute for these column types, you may chain the `unsigned` method onto the column's definition: + +```php +$table->decimal('amount', total: 8, places: 2)->unsigned(); +$table->double('amount')->unsigned(); +$table->float('amount', precision: 53)->unsigned(); +``` + #### Spatial Types @@ -87,3 +148,39 @@ $table->geography('latitude', subtype: 'point', srid: 4326); ``` The `isGeometry` and `projection` column modifiers of the PostgreSQL grammar have been removed accordingly. + + +#### Doctrine DBAL Removal + +**Likelihood Of Impact: Low** + +The following list of Doctrine DBAL related classes and methods have been removed. Laravel is no longer dependent on this package and registering custom Doctrines types is no longer necessary for the proper creation and alteration of various column types that previously required custom types: + +
    + +- `Illuminate\Database\Schema\Builder::$alwaysUsesNativeSchemaOperationsIfPossible` class property +- `Illuminate\Database\Schema\Builder::useNativeSchemaOperationsIfPossible()` method +- `Illuminate\Database\Connection::usingNativeSchemaOperations()` method +- `Illuminate\Database\Connection::isDoctrineAvailable()` method +- `Illuminate\Database\Connection::getDoctrineConnection()` method +- `Illuminate\Database\Connection::getDoctrineSchemaManager()` method +- `Illuminate\Database\Connection::getDoctrineColumn()` method +- `Illuminate\Database\Connection::registerDoctrineType()` method +- `Illuminate\Database\DatabaseManager::registerDoctrineType()` method +- `Illuminate\Database\PDO` directory +- `Illuminate\Database\DBAL\TimestampType` class +- `Illuminate\Database\Schema\Grammars\ChangeColumn` class +- `Illuminate\Database\Schema\Grammars\RenameColumn` class +- `Illuminate\Database\Schema\Grammars\Grammar::getDoctrineTableDiff()` method + +
    + +In addition, registering custom Doctrine types via `dbal.types` in your application's `database` configuration file is no longer required. + + +#### Schema Builder `getColumnType()` Method + +**Likelihood Of Impact: Very Low** + +The `Schema::getColumnType()` method now always returns actual type of the given column, not the Doctrine DBAL equivalent type. + From c8e8814a45cef619c37bb118c895d94fc1347919 Mon Sep 17 00:00:00 2001 From: Azim Kordpour Date: Mon, 22 Jan 2024 14:56:31 +0100 Subject: [PATCH 1303/2609] Remove importing Controller from the first example (#9271) --- queries.md | 1 - 1 file changed, 1 deletion(-) diff --git a/queries.md b/queries.md index d46aa7c2777..ce92e35c60a 100644 --- a/queries.md +++ b/queries.md @@ -56,7 +56,6 @@ You may use the `table` method provided by the `DB` facade to begin a query. The namespace App\Http\Controllers; - use App\Http\Controllers\Controller; use Illuminate\Support\Facades\DB; use Illuminate\View\View; From d6605b0162301072ea404cac9eff166d068455dc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 22 Jan 2024 08:00:20 -0600 Subject: [PATCH 1304/2609] wip --- facades.md | 1 + 1 file changed, 1 insertion(+) diff --git a/facades.md b/facades.md index 189580c0440..2774406fdba 100644 --- a/facades.md +++ b/facades.md @@ -282,6 +282,7 @@ Process | [Illuminate\Process\Factory](https://laravel.com/api/{{version}}/Ill Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) |   +RateLimiter | [Illuminate\Cache\RateLimiter](https://laravel.com/api/{{version}}/Illuminate/Cache/RateLimiter.html) |   Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/{{version}}/Illuminate/Routing/Redirector.html) | `redirect` Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/{{version}}/Illuminate/Redis/RedisManager.html) | `redis` Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/{{version}}/Illuminate/Redis/Connections/Connection.html) | `redis.connection` From 335662060f2c1193e6abff9761f552dd7ef22cce Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Jan 2024 08:32:53 -0600 Subject: [PATCH 1305/2609] wip --- scout.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/scout.md b/scout.md index 946d4033930..f7402a65b83 100644 --- a/scout.md +++ b/scout.md @@ -194,19 +194,6 @@ User::class => [ ], ``` - -#### Dynamic Search Parameters - -Typesense allows you to modify your [search parameters](https://typesense.org/docs/latest/api/search.html#search-parameters) dynamically when performing a search operation via the `withSearchParameters` method: - -```php -use App\Models\Todo; - -Todo::search('Groceries')->withSearchParameters([ - 'query_by' => 'title, description' -])->get(); -``` - ## Configuration From eb0cd7def82eb89c97e0f1070612f6fdedd83120 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 23 Jan 2024 15:33:17 +0100 Subject: [PATCH 1306/2609] Add version to installer command (#9275) --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 12829b01a95..fd97df019a8 100644 --- a/installation.md +++ b/installation.md @@ -64,7 +64,7 @@ composer create-project laravel/laravel example-app Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: ```nothing -composer global require laravel/installer +composer global require laravel/installer:^5.3 laravel new example-app ``` From 4933a80bb51863d70308e5ae04a49b19807584c1 Mon Sep 17 00:00:00 2001 From: Jason Bosco Date: Tue, 23 Jan 2024 09:20:04 -0600 Subject: [PATCH 1307/2609] [10.x] Add docs for Typesense Sail support (#9262) * Add docs for Typesense Sail support * Add Typesense sail mention to installation section as well * Update sail.md --------- Co-authored-by: Taylor Otwell --- installation.md | 2 +- sail.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/installation.md b/installation.md index fd97df019a8..4750d7844b3 100644 --- a/installation.md +++ b/installation.md @@ -244,7 +244,7 @@ Once the application's Docker containers have been started, you can access the a ### Choosing Your Sail Services -When creating a new Laravel application via Sail, you may use the `with` query string variable to choose which services should be configured in your new application's `docker-compose.yml` file. Available services include `mysql`, `pgsql`, `mariadb`, `redis`, `memcached`, `meilisearch`, `minio`, `selenium`, and `mailpit`: +When creating a new Laravel application via Sail, you may use the `with` query string variable to choose which services should be configured in your new application's `docker-compose.yml` file. Available services include `mysql`, `pgsql`, `mariadb`, `redis`, `memcached`, `meilisearch`, `typesense`, `minio`, `selenium`, and `mailpit`: ```shell curl -s "https://laravel.build/example-app?with=mysql,redis" | bash diff --git a/sail.md b/sail.md index d62926f4db8..a4adf18f645 100644 --- a/sail.md +++ b/sail.md @@ -14,6 +14,7 @@ - [MySQL](#mysql) - [Redis](#redis) - [Meilisearch](#meilisearch) + - [Typesense](#typesense) - [File Storage](#file-storage) - [Running Tests](#running-tests) - [Laravel Dusk](#laravel-dusk) @@ -238,6 +239,20 @@ If you chose to install the [Meilisearch](https://www.meilisearch.com) service w From your local machine, you may access Meilisearch's web based administration panel by navigating to `http://localhost:7700` in your web browser. + +### Typesense + +If you chose to install the [Typesense](https://typesense.org) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this lightning fast, open-source search-engine that is natively integrated with [Laravel Scout](/docs/{{version}}/scout#typesense). Once you have started your containers, you may connect to the Typesense instance within your application by setting the following environment variables: + +```ini +TYPESENSE_HOST=typesense +TYPESENSE_PORT=8108 +TYPESENSE_PROTOCOL=http +TYPESENSE_API_KEY=xyz +``` + +From your local machine, you may access Typesense's API via `http://localhost:8108`. + ## File Storage From c36b582408cb0eddf291238ce5fb1bee21979402 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Jan 2024 09:23:24 -0600 Subject: [PATCH 1308/2609] document routes method --- notifications.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/notifications.md b/notifications.md index 6089e52f9c9..403b136721e 100644 --- a/notifications.md +++ b/notifications.md @@ -325,6 +325,13 @@ If you would like to provide the recipient's name when sending an on-demand noti 'barrett@example.com' => 'Barrett Blair', ])->notify(new InvoicePaid($invoice)); +Using the `routes` method, you may provide ad-hoc routing information for multiple notification channels at once: + + Notification::routes([ + 'mail' => ['barrett@example.com' => 'Barrett Blair'], + 'vonage' => '5555555555', + ])->notify(new InvoicePaid($invoice)); + ## Mail Notifications From 4a7160cc2532f054515be8a330cb806268e84668 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Jan 2024 09:27:05 -0600 Subject: [PATCH 1309/2609] wip --- strings.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/strings.md b/strings.md index 27a08bcf001..97095554a27 100644 --- a/strings.md +++ b/strings.md @@ -100,6 +100,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::ucsplit](#method-str-ucsplit) [Str::upper](#method-str-upper) [Str::ulid](#method-str-ulid) +[Str::unwrap](#method-str-unwrap) [Str::uuid](#method-str-uuid) [Str::wordCount](#method-str-word-count) [Str::wordWrap](#method-str-word-wrap) @@ -195,6 +196,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [trim](#method-fluent-str-trim) [ucfirst](#method-fluent-str-ucfirst) [ucsplit](#method-fluent-str-ucsplit) +[unwrap](#method-fluent-str-unwrap) [upper](#method-fluent-str-upper) [when](#method-fluent-str-when) [whenContains](#method-fluent-str-when-contains) @@ -1204,6 +1206,21 @@ use Illuminate\Support\Str; $date = Carbon::createFromId((string) Str::ulid()); ``` + +#### `Str::unwrap()` {.collection-method} + +The `Str::unwrap` method removes the specified strings from the beginning and end of a given string: + + use Illuminate\Support\Str; + + Str::unwrap('-Laravel-', '-'); + + // Laravel + + Str::unwrap('{framework: "Laravel"}', '{', '}'); + + // framework: "Laravel" + #### `Str::uuid()` {.collection-method} @@ -2421,6 +2438,21 @@ The `ucsplit` method splits the given string into a collection by uppercase char // collect(['Foo', 'Bar']) + +#### `unwrap` {.collection-method} + +The `unwrap` method removes the specified strings from the beginning and end of a given string: + + use Illuminate\Support\Str; + + Str::of('-Laravel-')->unwrap('-'); + + // Laravel + + Str::of('{framework: "Laravel"}')->unwrap('{', '}'); + + // framework: "Laravel" + #### `upper` {.collection-method} From 6969f95a8fbc52d17de2e0aa251259a71fb5b83f Mon Sep 17 00:00:00 2001 From: Sergey Karakhanyan Date: Tue, 23 Jan 2024 19:43:15 +0400 Subject: [PATCH 1310/2609] Add Typesense Dynamic Search Parameters Doc (#9276) --- scout.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scout.md b/scout.md index f7402a65b83..10c9a228fa2 100644 --- a/scout.md +++ b/scout.md @@ -194,6 +194,19 @@ User::class => [ ], ``` + +#### Dynamic Search Parameters + +Typesense allows you to modify your [search parameters](https://typesense.org/docs/latest/api/search.html#search-parameters) dynamically when performing a search operation via the `options` method: + +```php +use App\Models\Todo; + +Todo::search('Groceries')->options([ + 'query_by' => 'title, description' +])->get(); +``` + ## Configuration From 298491b069bd4b42395e455e8ddb2e006ce9fdc0 Mon Sep 17 00:00:00 2001 From: Jason Bosco Date: Wed, 24 Jan 2024 12:44:53 -0600 Subject: [PATCH 1311/2609] Add mention of required Typesense configuration in Scout config (#9280) * Add mention of required Typesense configuration in Scout config Based on feedback from here: https://github.com/laravel/scout/issues/796#issuecomment-1908591943 * formatting --------- Co-authored-by: Taylor Otwell --- scout.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scout.md b/scout.md index 10c9a228fa2..ae83455bcc3 100644 --- a/scout.md +++ b/scout.md @@ -177,6 +177,8 @@ public function toSearchableArray() } ``` +You should also define your Typesense collection schemas in your application's `config/scout.php` file. A collection schema describes the data types of each field that is searchable via Typesense. For more information on all available schema options, please consult the [Typesense documentation](https://typesense.org/docs/latest/api/collections.html#schema-parameters). If you need to change your Typesense collection's schema after it has been defined, you must do so via Typesense's API. + If your searchable model is soft deletable, you should define a `__soft_deleted` field in the model's corresponding Typesense schema within your application's `config/scout.php` configuration file: ```php From 66e84fe813654e33624b4593411a147391d692a2 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 25 Jan 2024 18:53:42 +0330 Subject: [PATCH 1312/2609] [11.x] Deprecated Schema Methods Removal (#9281) * deprecated schema methods removal * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/upgrade.md b/upgrade.md index bc8d7b157c1..bd88ba88ced 100644 --- a/upgrade.md +++ b/upgrade.md @@ -177,6 +177,19 @@ The following list of Doctrine DBAL related classes and methods have been remove In addition, registering custom Doctrine types via `dbal.types` in your application's `database` configuration file is no longer required. + +#### Deprecated Schema Methods + +**Likelihood Of Impact: Very Low** + +The deprecated `Schema::getAllTables()`, `Schema::getAllViews()`, and `Schema::getAllTypes()` methods have been removed in favor of new `Schema::getTables()`, `Schema::getViews()`, and `Schema::getTypes()` methods. + +When using PostgreSQL, none of the new schema methods will accept a three-part reference (e.g. `database.schema.table`). Therefore, you should use `connection()` to declare the database instead: + +```php +Schema::connection('database')->hasTable('schema.table'); +``` + #### Schema Builder `getColumnType()` Method From ebe5c98bee0769c1b6c8901007bb0be276446632 Mon Sep 17 00:00:00 2001 From: uhshwin <98401812+uhshwin@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:12:09 +0530 Subject: [PATCH 1313/2609] Update 5.3 to 5.4 (#9282) Update the instructions for composer global require laravel/installer to the latest version. --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 4750d7844b3..82f138705d5 100644 --- a/installation.md +++ b/installation.md @@ -64,7 +64,7 @@ composer create-project laravel/laravel example-app Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: ```nothing -composer global require laravel/installer:^5.3 +composer global require laravel/installer:^5.4 laravel new example-app ``` From 95384acd1a391fe4aaaab4596a8e852f64a9b924 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 29 Jan 2024 09:17:05 -0500 Subject: [PATCH 1314/2609] Update billing.md (#9285) --- billing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/billing.md b/billing.md index d790a4efbb0..b828e969d4f 100644 --- a/billing.md +++ b/billing.md @@ -920,7 +920,7 @@ If you would like to add a subscription to a customer who already has a default #### Creating Subscriptions From the Stripe Dashboard -You may also create subscriptions from the Stripe dashboard itself. When doing so, Cashier will sync newly added subscriptions and assign them a type of `default`. To customize the subscription type that is assigned to dashboard created subscriptions, [extend the `WebhookController`](#defining-webhook-event-handlers) and overwrite the `newSubscriptionType` method. +You may also create subscriptions from the Stripe dashboard itself. When doing so, Cashier will sync newly added subscriptions and assign them a type of `default`. To customize the subscription type that is assigned to dashboard created subscriptions, [define webhook event handlers](#defining-webhook-event-handlers). In addition, you may only create one type of subscription via the Stripe dashboard. If your application offers multiple subscriptions that use different types, only one type of subscription may be added through the Stripe dashboard. From 9c42f8596c5ceadc3496f20044b7a38f620419a2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 29 Jan 2024 08:20:13 -0600 Subject: [PATCH 1315/2609] wip --- broadcasting.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 51360d1b90c..68de72b5f6a 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -52,7 +52,7 @@ The core concepts behind broadcasting are simple: clients connect to named chann #### 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.com). However, community driven packages such as [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) and [soketi](https://docs.soketi.app/) provide additional broadcasting drivers that do not require commercial broadcasting providers. +By default, Laravel includes two server-side broadcasting drivers for you to choose from: [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.com). However, community driven packages such as [soketi](https://docs.soketi.app/) provide additional broadcasting drivers that do not require commercial broadcasting providers. > [!NOTE] > Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). @@ -110,7 +110,7 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst #### Open Source Pusher Alternatives -The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) and [soketi](https://docs.soketi.app/) packages provide Pusher compatible WebSocket servers for Laravel. These packages allow you to leverage the full power of Laravel broadcasting without a commercial WebSocket provider. For more information on installing and using these packages, please consult our documentation on [open source alternatives](#open-source-alternatives). +[soketi](https://docs.soketi.app/) provides a Pusher compatible WebSocket server for Laravel, allowing you to leverage the full power of Laravel broadcasting without a commercial WebSocket provider. For more information on installing and using open source packages for broadcasting, please consult our documentation on [open source alternatives](#open-source-alternatives). ### Ably @@ -141,11 +141,6 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst ### Open Source Alternatives - -#### PHP - -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). - #### Node From 83688777dab6342690ae0c91cb9cb91c7514d355 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 30 Jan 2024 10:30:26 -0600 Subject: [PATCH 1316/2609] unlink --- filesystem.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/filesystem.md b/filesystem.md index 6370658687e..454efbc6afe 100644 --- a/filesystem.md +++ b/filesystem.md @@ -73,6 +73,12 @@ You may configure additional symbolic links in your `filesystems` configuration public_path('images') => storage_path('app/images'), ], +The `storage:unlink` command may be used to destroy your configured symbolic links: + +```shell +php artisan storage:unlink +``` + ### Driver Prerequisites From 1a1d2b6cf750c7296056111b9b44485161702d38 Mon Sep 17 00:00:00 2001 From: Arnaud Becher Date: Wed, 31 Jan 2024 18:19:34 +0100 Subject: [PATCH 1317/2609] [10.x] add missing Number helper methods (#9289) * add missing Number helper methods * quickfix * quickfix * quickfix * corrections * formatting --------- Co-authored-by: Taylor Otwell --- helpers.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index cbd6f55aa6d..d2ebb5f70e4 100644 --- a/helpers.md +++ b/helpers.md @@ -94,6 +94,10 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Number::currency](#method-number-currency) [Number::fileSize](#method-number-file-size) [Number::forHumans](#method-number-for-humans) +[Number::ordinal](#method-number-ordinal) +[Number::spell](#method-number-spell) +[Number::useLocale](#method-number-use-locale) +[Number::withLocale](#method-number-with-locale)
    @@ -1220,6 +1224,87 @@ The `Number::forHumans` method returns the human-readable format of the provided // 1.23 million + +#### `Number::ordinal()` {.collection-method} + +The `Number::ordinal` method returns a number's ordinal representation: + + use Illuminate\Support\Number; + + $number = Number::ordinal(1); + + // 1st + + $number = Number::ordinal(2); + + // 2nd + + $number = Number::ordinal(21); + + // 21st + + +#### `Number::spell()` {.collection-method} + +The `Number::spell` method transforms the given number into a string of words: + + use Illuminate\Support\Number; + + $number = Number::spell(102); + + // one hundred and two + + $number = Number::spell(88, locale: 'fr'); + + // quatre-vingt-huit + + +The `after` argument allows you to specify a value after which all numbers should be spelled out: + + $number = Number::spell(10, after: 10); + + // 10 + + $number = Number::spell(11, after: 10); + + // eleven + +The `until` argument allows you to specify a value before which all numbers should be spelled out: + + $number = Number::spell(5, until: 10); + + // five + + $number = Number::spell(10, until: 10); + + // 10 + + +#### `Number::useLocale()` {.collection-method} + +The `Number::useLocale` method sets the default number locale globally, which affects how numbers and currency are formatted by subsequent invocations to the `Number` class's methods: + + use Illuminate\Support\Number; + + /** + * Bootstrap any application services. + */ + public function boot(): void + { + Number::useLocale('de'); + } + + +#### `Number::withLocale()` {.collection-method} + +The `Number::withLocale` method executes the given closure using the specified locale and then restores the original locale after the callback has executed: + + use Illuminate\Support\Number; + + $number = Number::withLocale('de', function () { + return Number::format(1500); + }); + ## Paths @@ -1996,7 +2081,7 @@ The `value` function returns the value it is given. However, if you pass a closu }); // false - + Additional arguments may be passed to the `value` function. If the first argument is a closure then the additional parameters will be passed to the closure as arguments, otherwise they will be ignored: $result = value(function (string $name) { From 9175141914ae31154a0d98043989d1bfc39ecdb1 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 31 Jan 2024 23:47:04 +0330 Subject: [PATCH 1318/2609] [11.x] Index modifiers when modifying columns (#9290) * add a note for index modifiers * formatting --------- Co-authored-by: Taylor Otwell --- migrations.md | 12 +++++++++++- upgrade.md | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/migrations.md b/migrations.md index a58ee60f938..8b71734933e 100644 --- a/migrations.md +++ b/migrations.md @@ -983,12 +983,22 @@ The `change` method allows you to modify the type and attributes of existing col $table->string('name', 50)->change(); }); -When modifying a column, you must explicitly include all of the modifiers you want to keep on the column definition - any missing attribute will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column: +When modifying a column, you must explicitly include all the modifiers you want to keep on the column definition - any missing attribute will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column: Schema::table('users', function (Blueprint $table) { $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); }); +The `change` method does not change the indexes of the column. Therefore, you may use index modifiers to explicitly add or drop an index when modifying the column: + +```php +// Add an index... +$table->bigIncrements('id')->primary()->change(); + +// Drop an index... +$table->char('postal_code', 10)->unique(false)->change(); +``` + ### Renaming Columns diff --git a/upgrade.md b/upgrade.md index bd88ba88ced..1fa7799defa 100644 --- a/upgrade.md +++ b/upgrade.md @@ -101,6 +101,16 @@ Schema::table('users', function (Blueprint $table) { }); ``` +The `change` method does not change the indexes of the column. Therefore, you may use index modifiers to explicitly add or drop an index when modifying the column: + +```php +// Add an index... +$table->bigIncrements('id')->primary()->change(); + +// Drop an index... +$table->char('postal_code', 10)->unique(false)->change(); +``` + #### Floating-Point Types @@ -140,7 +150,7 @@ $table->geometry('shapes'); $table->geography('coordinates'); ``` -To explicitly restrict the type or the spatial reference system identifier for values stored in the column on MySQL and PostgreSQL, you may pass the `subtype` and `srid` to the method: +To explicitly restrict the type or the spatial reference system identifier for values stored in the column on MySQL, MariaDB, and PostgreSQL, you may pass the `subtype` and `srid` to the method: ```php $table->geometry('dimension', subtype: 'polygon', srid: 0); From dc113868736123ab845451034064a460d21ca13e Mon Sep 17 00:00:00 2001 From: Kasper Hartwich Date: Thu, 1 Feb 2024 00:12:35 +0100 Subject: [PATCH 1319/2609] Added documentation for stripTags() (#9292) --- strings.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/strings.md b/strings.md index 97095554a27..29590de2f16 100644 --- a/strings.md +++ b/strings.md @@ -185,6 +185,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [squish](#method-fluent-str-squish) [start](#method-fluent-str-start) [startsWith](#method-fluent-str-starts-with) +[stripTags](#method-fluent-str-strip-tags) [studly](#method-fluent-str-studly) [substr](#method-fluent-str-substr) [substrReplace](#method-fluent-str-substrreplace) @@ -2295,6 +2296,21 @@ The `startsWith` method determines if the given string begins with the given val // true + +#### `stripTags` {.collection-method} + +The `stripTags` method removes all HTML and PHP tags from a string: + + use Illuminate\Support\Str; + + $result = Str::of('Taylor Otwell')->stripTags(); + + // Taylor Otwell + + $result = Str::of('Taylor Otwell')->stripTags(''); + + // Taylor Otwell + #### `studly` {.collection-method} From f34f0297ec86e214f39e9034bfedae47609a4fe6 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 31 Jan 2024 23:19:12 +0000 Subject: [PATCH 1320/2609] [11.x] Adds Pest snippets (#9291) * Adds Pest snippets * Remove comment --------- Co-authored-by: Taylor Otwell --- billing.md | 2 +- console-tests.md | 103 +++++--- database-testing.md | 151 ++++++++---- dusk.md | 246 ++++++++++++------ events.md | 180 +++++++++----- facades.md | 96 +++++--- filesystem.md | 73 ++++-- helpers.md | 56 +++-- http-tests.md | 589 +++++++++++++++++++++++++++++++------------- mail.md | 167 +++++++++---- mocking.md | 224 ++++++++++++----- notifications.md | 77 ++++-- packages.md | 2 +- passport.md | 80 ++++-- pennant.md | 12 +- processes.md | 25 +- queues.md | 118 ++++++--- sail.md | 2 +- sanctum.md | 40 ++- structure.md | 2 +- testing.md | 56 +++-- vite.md | 10 +- 22 files changed, 1615 insertions(+), 696 deletions(-) diff --git a/billing.md b/billing.md index b828e969d4f..29af60aefa0 100644 --- a/billing.md +++ b/billing.md @@ -2288,7 +2288,7 @@ You may invoke the `stripe` method on the `Cashier` class if you would like to u ## Testing -When testing an application that uses Cashier, you may mock the actual HTTP requests to the Stripe API; however, this requires you to partially re-implement Cashier's own behavior. Therefore, we recommend allowing your tests to hit the actual Stripe API. While this is slower, it provides more confidence that your application is working as expected and any slow tests may be placed within their own PHPUnit testing group. +When testing an application that uses Cashier, you may mock the actual HTTP requests to the Stripe API; however, this requires you to partially re-implement Cashier's own behavior. Therefore, we recommend allowing your tests to hit the actual Stripe API. While this is slower, it provides more confidence that your application is working as expected and any slow tests may be placed within their own Pest / PHPUnit testing group. When testing, remember that Cashier itself already has a great test suite, so you should only focus on testing the subscription and payment flow of your own application and not every underlying Cashier behavior. diff --git a/console-tests.md b/console-tests.md index 587ef58fb98..98c8f59a601 100644 --- a/console-tests.md +++ b/console-tests.md @@ -15,13 +15,21 @@ In addition to simplifying HTTP testing, Laravel provides a simple API for testi To get started, let's explore how to make assertions regarding an Artisan command's exit code. To accomplish this, we will use the `artisan` method to invoke an Artisan command from our test. Then, we will use the `assertExitCode` method to assert that the command completed with a given exit code: - /** - * Test a console command. - */ - public function test_console_command(): void - { - $this->artisan('inspire')->assertExitCode(0); - } +```php tab=Pest +test('console command', function () { + $this->artisan('inspire')->assertExitCode(0); +}); +``` + +```php tab=PHPUnit +/** + * Test a console command. + */ +public function test_console_command(): void +{ + $this->artisan('inspire')->assertExitCode(0); +} +``` You may use the `assertNotExitCode` method to assert that the command did not exit with a given exit code: @@ -52,20 +60,35 @@ Laravel allows you to easily "mock" user input for your console commands using t You may test this command with the following test which utilizes the `expectsQuestion`, `expectsOutput`, `doesntExpectOutput`, `expectsOutputToContain`, `doesntExpectOutputToContain`, and `assertExitCode` methods: - /** - * Test a console command. - */ - public function test_console_command(): void - { - $this->artisan('question') - ->expectsQuestion('What is your name?', 'Taylor Otwell') - ->expectsQuestion('Which language do you prefer?', 'PHP') - ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') - ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') - ->expectsOutputToContain('Taylor Otwell') - ->doesntExpectOutputToContain('you prefer Ruby') - ->assertExitCode(0); - } +```php tab=Pest +test('console command', function () { + $this->artisan('question') + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsQuestion('Which language do you prefer?', 'PHP') + ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') + ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') + ->expectsOutputToContain('Taylor Otwell') + ->doesntExpectOutputToContain('you prefer Ruby') + ->assertExitCode(0); +}); +``` + +```php tab=PHPUnit +/** + * Test a console command. + */ +public function test_console_command(): void +{ + $this->artisan('question') + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsQuestion('Which language do you prefer?', 'PHP') + ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') + ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') + ->expectsOutputToContain('Taylor Otwell') + ->doesntExpectOutputToContain('you prefer Ruby') + ->assertExitCode(0); +} +``` #### Confirmation Expectations @@ -95,16 +118,28 @@ If your command displays a table of information using Artisan's `table` method, By default, the `Illuminate\Console\Events\CommandStarting` and `Illuminate\Console\Events\CommandFinished` events are not dispatched while running your application's tests. However, you can enable these events for a given test class by adding the `Illuminate\Foundation\Testing\WithConsoleEvents` trait to the class: - get('/'); - /** - * A basic functional test example. - */ - public function test_basic_example(): void - { - $response = $this->get('/'); + // ... +}); +``` - // ... - } +```php tab=PHPUnit +get('/'); + + // ... } +} +``` The `Illuminate\Foundation\Testing\RefreshDatabase` trait does not migrate your database if your schema is up to date. Instead, it will only execute the test within a database transaction. Therefore, any records added to the database by test cases that do not use this trait may still exist in the database. @@ -49,54 +65,95 @@ When testing, you may need to insert a few records into your database before exe To learn more about creating and utilizing model factories to create models, please consult the complete [model factory documentation](/docs/{{version}}/eloquent-factories). Once you have defined a model factory, you may utilize the factory within your test to create models: - use App\Models\User; +```php tab=Pest +use App\Models\User; - public function test_models_can_be_instantiated(): void - { - $user = User::factory()->create(); +test('models can be instantiated', function () { + $user = User::factory()->create(); - // ... - } + // ... +}); +``` + +```php tab=PHPUnit +use App\Models\User; + +public function test_models_can_be_instantiated(): void +{ + $user = User::factory()->create(); + + // ... +} +``` ## Running Seeders If you would like to use [database seeders](/docs/{{version}}/seeding) to populate your database during a feature test, you may invoke the `seed` method. By default, the `seed` method will execute the `DatabaseSeeder`, which should execute all of your other seeders. Alternatively, you pass a specific seeder class name to the `seed` method: - seed(); + + // Run a specific seeder... + $this->seed(OrderStatusSeeder::class); + + // ... + + // Run an array of specific seeders... + $this->seed([ + OrderStatusSeeder::class, + TransactionStatusSeeder::class, + // ... + ]); +}); +``` - class ExampleTest extends TestCase +```php tab=PHPUnit +seed(); - /** - * Test creating a new order. - */ - public function test_orders_can_be_created(): void - { - // Run the DatabaseSeeder... - $this->seed(); + // Run a specific seeder... + $this->seed(OrderStatusSeeder::class); - // Run a specific seeder... - $this->seed(OrderStatusSeeder::class); + // ... + // Run an array of specific seeders... + $this->seed([ + OrderStatusSeeder::class, + TransactionStatusSeeder::class, // ... - - // Run an array of specific seeders... - $this->seed([ - OrderStatusSeeder::class, - TransactionStatusSeeder::class, - // ... - ]); - } + ]); } +} +``` Alternatively, you may instruct Laravel to automatically seed the database before each test that uses the `RefreshDatabase` trait. You may accomplish this by defining a `$seed` property on your base test class: @@ -132,7 +189,7 @@ When the `$seed` property is `true`, the test will run the `Database\Seeders\Dat ## Available Assertions -Laravel provides several database assertions for your [PHPUnit](https://phpunit.de/) feature tests. We'll discuss each of these assertions below. +Laravel provides several database assertions for your [Pest](https://pestphp.com) or [PHPUnit](https://phpunit.de) feature tests. We'll discuss each of these assertions below. #### assertDatabaseCount diff --git a/dusk.md b/dusk.md index e25ebd51f04..a9ec7f26565 100644 --- a/dusk.md +++ b/dusk.md @@ -153,19 +153,33 @@ Most of the tests you write will interact with pages that retrieve data from you The `DatabaseMigrations` trait will run your database migrations before each test. However, dropping and re-creating your database tables for each test is typically slower than truncating the tables: - [!WARNING] > SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. @@ -175,22 +189,40 @@ The `DatabaseMigrations` trait will run your database migrations before each tes The `DatabaseTruncation` trait will migrate your database on the first test in order to ensure your database tables have been properly created. However, on subsequent tests, the database's tables will simply be truncated - providing a speed boost over re-running all of your database migrations: - [!NOTE] +> If you are using Pest, you should define properties or methods on the base `DuskTestCase` class or on any class your test file extends. + /** * Indicates which tables should be truncated. * @@ -249,7 +281,7 @@ If you had test failures the last time you ran the `dusk` command, you may save php artisan dusk:fails ``` -The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/10.1/annotations.html#group): +The `dusk` command accepts any argument that is normally accepted by the Pest / PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/10.1/annotations.html#group): ```shell php artisan dusk --group=foo @@ -302,38 +334,63 @@ When running tests, Dusk will back-up your `.env` file and rename your Dusk envi To get started, let's write a test that verifies we can log into our application. After generating a test, we can modify it to navigate to the login page, enter some credentials, and click the "Login" button. To create a browser instance, you may call the `browse` method from within your Dusk test: - create([ + 'email' => 'taylor@laravel.com', + ]); - /** - * A basic browser test example. - */ - public function test_basic_example(): void - { - $user = User::factory()->create([ - 'email' => 'taylor@laravel.com', - ]); - - $this->browse(function (Browser $browser) use ($user) { - $browser->visit('/login') - ->type('email', $user->email) - ->type('password', 'password') - ->press('Login') - ->assertPathIs('/home'); - }); - } + $this->browse(function (Browser $browser) use ($user) { + $browser->visit('/login') + ->type('email', $user->email) + ->type('password', 'password') + ->press('Login') + ->assertPathIs('/home'); + }); +}); +``` + +```php tab=PHPUnit +create([ + 'email' => 'taylor@laravel.com', + ]); + + $this->browse(function (Browser $browser) use ($user) { + $browser->visit('/login') + ->type('email', $user->email) + ->type('password', 'password') + ->press('Login') + ->assertPathIs('/home'); + }); } +} +``` As you can see in the example above, the `browse` method accepts a closure. A browser instance will automatically be passed to this closure by Dusk and is the main object used to interact with and make assertions against your application. @@ -1720,16 +1777,27 @@ Dusk even allows you to make assertions on the state of [Vue component](https:// You may assert on the state of the Vue component like so: - /** - * A basic Vue test example. - */ - public function test_vue(): void - { - $this->browse(function (Browser $browser) { - $browser->visit('/') - ->assertVue('user.name', 'Taylor', '@profile-component'); - }); - } +```php tab=Pest +test('vue', function () { + $this->browse(function (Browser $browser) { + $browser->visit('/') + ->assertVue('user.name', 'Taylor', '@profile-component'); + }); +}); +``` + +```php tab=PHPUnit +/** + * A basic Vue test example. + */ +public function test_vue(): void +{ + $this->browse(function (Browser $browser) { + $browser->visit('/') + ->assertVue('user.name', 'Taylor', '@profile-component'); + }); +} +``` #### assertVueIsNot @@ -1962,31 +2030,53 @@ As shown above, a "date picker" is an example of a component that might exist th Once the component has been defined, we can easily select a date within the date picker from any test. And, if the logic necessary to select a date changes, we only need to update the component: - browse(function (Browser $browser) { + $browser->visit('/') + ->within(new DatePicker, function (Browser $browser) { + $browser->selectDate(2019, 1, 30); + }) + ->assertSee('January'); + }); +}); +``` - class ExampleTest extends DuskTestCase +```php tab=PHPUnit +browse(function (Browser $browser) { - $browser->visit('/') - ->within(new DatePicker, function (Browser $browser) { - $browser->selectDate(2019, 1, 30); - }) - ->assertSee('January'); - }); - } + $this->browse(function (Browser $browser) { + $browser->visit('/') + ->within(new DatePicker, function (Browser $browser) { + $browser->selectDate(2019, 1, 30); + }) + ->assertSee('January'); + }); } +} +``` ## Continuous Integration diff --git a/events.md b/events.md index 68240d8f066..ff25daa197a 100644 --- a/events.md +++ b/events.md @@ -682,39 +682,67 @@ When testing code that dispatches events, you may wish to instruct Laravel to no 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`, `assertNotDispatched`, and `assertNothingDispatched` methods: - create(); + $order = Order::factory()->create(); - Event::assertDispatched(OrderCreated::class); + Event::assertDispatched(OrderCreated::class); - // Other events are dispatched as normal... - $order->update([...]); - } + // Other events are dispatched as normal... + $order->update([...]); +}); +``` + +```php tab=PHPUnit +/** + * Test order process. + */ +public function test_orders_can_be_processed(): void +{ + Event::fake([ + OrderCreated::class, + ]); + + $order = Order::factory()->create(); + + Event::assertDispatched(OrderCreated::class); + + // Other events are dispatched as normal... + $order->update([...]); +} +``` You may fake all events except for a set of specified events using the `except` method: @@ -765,31 +810,54 @@ You may fake all events except for a set of specified events using the `except` If you only want to fake event listeners for a portion of your test, you may use the `fakeFor` method: - create(); + + Event::assertDispatched(OrderCreated::class); + + return $order; + }); - class ExampleTest extends TestCase + // Events are dispatched as normal and observers will run ... + $order->update([...]); +}); +``` + +```php tab=PHPUnit +create(); + $order = Event::fakeFor(function () { + $order = Order::factory()->create(); - Event::assertDispatched(OrderCreated::class); + Event::assertDispatched(OrderCreated::class); - return $order; - }); + return $order; + }); - // Events are dispatched as normal and observers will run ... - $order->update([...]); - } + // Events are dispatched as normal and observers will run ... + $order->update([...]); } +} +``` diff --git a/facades.md b/facades.md index 2774406fdba..e8ecca5e55c 100644 --- a/facades.md +++ b/facades.md @@ -69,21 +69,37 @@ Typically, it would not be possible to mock or stub a truly static class method. Using Laravel's facade testing methods, we can write the following test to verify that the `Cache::get` method was called with the argument we expected: - use Illuminate\Support\Facades\Cache; +```php tab=Pest +use Illuminate\Support\Facades\Cache; - /** - * A basic functional test example. - */ - public function test_basic_example(): void - { - Cache::shouldReceive('get') - ->with('key') - ->andReturn('value'); +test('basic example', function () { + Cache::shouldReceive('get') + ->with('key') + ->andReturn('value'); - $response = $this->get('/cache'); + $response = $this->get('/cache'); - $response->assertSee('value'); - } + $response->assertSee('value'); +}); +``` + +```php tab=PHPUnit +use Illuminate\Support\Facades\Cache; + +/** + * A basic functional test example. + */ +public function test_basic_example(): void +{ + Cache::shouldReceive('get') + ->with('key') + ->andReturn('value'); + + $response = $this->get('/cache'); + + $response->assertSee('value'); +} +``` ### Facades vs. Helper Functions @@ -215,31 +231,51 @@ Injecting a publisher implementation into the method allows us to easily test th When the real-time facade is used, the publisher implementation will be resolved out of the service container using the portion of the interface or class name that appears after the `Facades` prefix. When testing, we can use Laravel's built-in facade testing helpers to mock this method call: - create(); - /** - * A test example. - */ - public function test_podcast_can_be_published(): void - { - $podcast = Podcast::factory()->create(); + Publisher::shouldReceive('publish')->once()->with($podcast); - Publisher::shouldReceive('publish')->once()->with($podcast); + $podcast->publish(); +}); +``` - $podcast->publish(); - } +```php tab=PHPUnit +create(); + + Publisher::shouldReceive('publish')->once()->with($podcast); + + $podcast->publish(); } +} +``` ## Facade Class Reference diff --git a/filesystem.md b/filesystem.md index 454efbc6afe..f1ac8911449 100644 --- a/filesystem.md +++ b/filesystem.md @@ -647,37 +647,66 @@ Finally, the `deleteDirectory` method may be used to remove a directory and all 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: - json('POST', '/photos', [ + UploadedFile::fake()->image('photo1.jpg'), + UploadedFile::fake()->image('photo2.jpg') + ]); + + // Assert one or more files were stored... + Storage::disk('photos')->assertExists('photo1.jpg'); + Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); + + // Assert one or more files were not stored... + Storage::disk('photos')->assertMissing('missing.jpg'); + Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); + + // Assert that a given directory is empty... + Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); +}); +``` - class ExampleTest extends TestCase +```php tab=PHPUnit +json('POST', '/photos', [ - UploadedFile::fake()->image('photo1.jpg'), - UploadedFile::fake()->image('photo2.jpg') - ]); + $response = $this->json('POST', '/photos', [ + UploadedFile::fake()->image('photo1.jpg'), + UploadedFile::fake()->image('photo2.jpg') + ]); - // Assert one or more files were stored... - Storage::disk('photos')->assertExists('photo1.jpg'); - Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); + // Assert one or more files were stored... + Storage::disk('photos')->assertExists('photo1.jpg'); + Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']); - // Assert one or more files were not stored... - Storage::disk('photos')->assertMissing('missing.jpg'); - Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); + // Assert one or more files were not stored... + Storage::disk('photos')->assertMissing('missing.jpg'); + Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); - // Assert that a given directory is empty... - Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); - } + // Assert that a given directory is empty... + Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); } +} +``` 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. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). diff --git a/helpers.md b/helpers.md index cbd6f55aa6d..9f00477883b 100644 --- a/helpers.md +++ b/helpers.md @@ -2223,29 +2223,55 @@ When testing code that utilizes the `Sleep` class or PHP's native sleep function Typically, testing this code would take _at least_ one second. Luckily, the `Sleep` class allows us to "fake" sleeping so that our test suite stays fast: - public function test_it_waits_until_ready() - { - Sleep::fake(); +```php tab=Pest +it('waits until ready', function () { + Sleep::fake(); - // ... - } + // ... +}); +``` + +```php tab=PHPUnit +public function test_it_waits_until_ready() +{ + Sleep::fake(); + + // ... +} +``` When faking the `Sleep` class, the actual execution pause is by-passed, leading to a substantially faster test. Once the `Sleep` class has been faked, it is possible to make assertions against the expected "sleeps" that should have occurred. To illustrate this, let's imagine we are testing code that pauses execution three times, with each pause increasing by a single second. Using the `assertSequence` method, we can assert that our code "slept" for the proper amount of time while keeping our test fast: - public function test_it_checks_if_ready_four_times() - { - Sleep::fake(); +```php tab=Pest +it('checks if ready three times', function () { + Sleep::fake(); - // ... + // ... - Sleep::assertSequence([ - Sleep::for(1)->second(), - Sleep::for(2)->seconds(), - Sleep::for(3)->seconds(), - ]); - } + Sleep::assertSequence([ + Sleep::for(1)->second(), + Sleep::for(2)->seconds(), + Sleep::for(3)->seconds(), + ]); +} +``` + +```php tab=PHPUnit +public function test_it_checks_if_ready_four_times() +{ + Sleep::fake(); + + // ... + + Sleep::assertSequence([ + Sleep::for(1)->second(), + Sleep::for(2)->seconds(), + Sleep::for(3)->seconds(), + ]); +} +``` Of course, the `Sleep` class offers a variety of other assertions you may use when testing: diff --git a/http-tests.md b/http-tests.md index 8d577bd52d6..20c3b122ca0 100644 --- a/http-tests.md +++ b/http-tests.md @@ -22,7 +22,17 @@ Laravel provides a very fluent API for making HTTP requests to your application and examining the responses. For example, take a look at the feature test defined below: -```php +```php tab=Pest +get('/'); + + $response->assertStatus(200); +}); +``` + +```php tab=PHPUnit get('/'); - namespace Tests\Feature; + $response->assertStatus(200); +}); +``` + +```php tab=PHPUnit +get('/'); + $response = $this->get('/'); - $response->assertStatus(200); - } + $response->assertStatus(200); } +} +``` In general, each of your tests should only make one request to your application. Unexpected behavior may occur if multiple requests are executed within a single test method. -> [!NOTE] +> [!NOTE] > For convenience, the CSRF middleware is automatically disabled when running tests. @@ -81,90 +103,157 @@ In general, each of your tests should only make one request to your application. You may use the `withHeaders` method to customize the request's headers before it is sent to the application. This method allows you to add any custom headers you would like to the request: - withHeaders([ + 'X-Header' => 'Value', + ])->post('/user', ['name' => 'Sally']); - namespace Tests\Feature; + $response->assertStatus(201); +}); +``` + +```php tab=PHPUnit +withHeaders([ - 'X-Header' => 'Value', - ])->post('/user', ['name' => 'Sally']); + $response = $this->withHeaders([ + 'X-Header' => 'Value', + ])->post('/user', ['name' => 'Sally']); - $response->assertStatus(201); - } + $response->assertStatus(201); } +} +``` ### Cookies You may use the `withCookie` or `withCookies` methods to set cookie values before making a request. The `withCookie` method accepts a cookie name and value as its two arguments, while the `withCookies` method accepts an array of name / value pairs: - withCookie('color', 'blue')->get('/'); + + $response = $this->withCookies([ + 'color' => 'blue', + 'name' => 'Taylor', + ])->get('/'); - namespace Tests\Feature; + // +}); +``` - use Tests\TestCase; +```php tab=PHPUnit +withCookie('color', 'blue')->get('/'); + $response = $this->withCookie('color', 'blue')->get('/'); - $response = $this->withCookies([ - 'color' => 'blue', - 'name' => 'Taylor', - ])->get('/'); - } + $response = $this->withCookies([ + 'color' => 'blue', + 'name' => 'Taylor', + ])->get('/'); + + // } +} +``` ### Session / Authentication Laravel provides several helpers for interacting with the session during HTTP testing. First, you may set the session data to a given array using the `withSession` method. This is useful for loading the session with data before issuing a request to your application: - withSession(['banned' => false])->get('/'); + + // +}); +``` + +```php tab=PHPUnit +withSession(['banned' => false])->get('/'); - } + $response = $this->withSession(['banned' => false])->get('/'); + + // } +} +``` Laravel's session is typically used to maintain state for the currently authenticated user. Therefore, the `actingAs` helper method provides a simple way to authenticate a given user as the current user. For example, we may use a [model factory](/docs/{{version}}/eloquent-factories) to generate and authenticate a user: - create(); + + $response = $this->actingAs($user) + ->withSession(['banned' => false]) + ->get('/'); + + // +}); +``` + +```php tab=PHPUnit +create(); + $user = User::factory()->create(); - $response = $this->actingAs($user) - ->withSession(['banned' => false]) - ->get('/'); - } + $response = $this->actingAs($user) + ->withSession(['banned' => false]) + ->get('/'); + + // } +} +``` You may also specify which guard should be used to authenticate the given user by passing the guard name as the second argument to the `actingAs` method. The guard that is provided to the `actingAs` method will also become the default guard for the duration of the test: @@ -175,53 +264,85 @@ You may also specify which guard should be used to authenticate the given user b After making a test request to your application, the `dump`, `dumpHeaders`, and `dumpSession` methods may be used to examine and debug the response contents: - get('/'); - namespace Tests\Feature; + $response->dumpHeaders(); - use Tests\TestCase; + $response->dumpSession(); + + $response->dump(); +}); +``` - class ExampleTest extends TestCase +```php tab=PHPUnit +get('/'); + $response = $this->get('/'); - $response->dumpHeaders(); + $response->dumpHeaders(); - $response->dumpSession(); + $response->dumpSession(); - $response->dump(); - } + $response->dump(); } +} +``` Alternatively, you may use the `dd`, `ddHeaders`, and `ddSession` methods to dump information about the response and then stop execution: - get('/'); + + $response->ddHeaders(); + + $response->ddSession(); - namespace Tests\Feature; + $response->dd(); +}); +``` + +```php tab=PHPUnit +get('/'); + $response = $this->get('/'); - $response->ddHeaders(); + $response->ddHeaders(); - $response->ddSession(); + $response->ddSession(); - $response->dd(); - } + $response->dd(); } +} +``` ### Exception Handling @@ -248,34 +369,56 @@ $this->assertThrows( Laravel also provides several helpers for testing JSON APIs and their responses. For example, the `json`, `getJson`, `postJson`, `putJson`, `patchJson`, `deleteJson`, and `optionsJson` methods may be used to issue JSON requests with various HTTP verbs. You may also easily pass data and headers to these methods. To get started, let's write a test to make a `POST` request to `/api/user` and assert that the expected JSON data was returned: - postJson('/api/user', ['name' => 'Sally']); + + $response + ->assertStatus(201) + ->assertJson([ + 'created' => true, + ]); +}); +``` + +```php tab=PHPUnit +postJson('/api/user', ['name' => 'Sally']); + $response = $this->postJson('/api/user', ['name' => 'Sally']); - $response - ->assertStatus(201) - ->assertJson([ - 'created' => true, - ]); - } + $response + ->assertStatus(201) + ->assertJson([ + 'created' => true, + ]); } +} +``` In addition, JSON response data may be accessed as array variables on the response, making it convenient for you to inspect the individual values returned within a JSON response: - $this->assertTrue($response['created']); +```php tab=Pest +expect($response['created'])->toBeTrue(); +``` -> [!NOTE] +```php tab=PHPUnit +$this->assertTrue($response['created']); +``` + +> [!NOTE] > The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. @@ -283,54 +426,85 @@ In addition, JSON response data may be accessed as array variables on the respon As previously mentioned, the `assertJson` method may be used to assert that a fragment of JSON exists within the JSON response. If you would like to verify that a given array **exactly matches** the JSON returned by your application, you should use the `assertExactJson` method: - postJson('/user', ['name' => 'Sally']); - use Tests\TestCase; + $response + ->assertStatus(201) + ->assertExactJson([ + 'created' => true, + ]); +}); - class ExampleTest extends TestCase +``` + +```php tab=PHPUnit +postJson('/user', ['name' => 'Sally']); + $response = $this->postJson('/user', ['name' => 'Sally']); - $response - ->assertStatus(201) - ->assertExactJson([ - 'created' => true, - ]); - } + $response + ->assertStatus(201) + ->assertExactJson([ + 'created' => true, + ]); } +} +``` #### Asserting on JSON Paths If you would like to verify that the JSON response contains the given data at a specified path, you should use the `assertJsonPath` method: - postJson('/user', ['name' => 'Sally']); - namespace Tests\Feature; + $response + ->assertStatus(201) + ->assertJsonPath('team.owner.name', 'Darian'); +}); +``` - use Tests\TestCase; +```php tab=PHPUnit +postJson('/user', ['name' => 'Sally']); + $response = $this->postJson('/user', ['name' => 'Sally']); - $response - ->assertStatus(201) - ->assertJsonPath('team.owner.name', 'Darian'); - } + $response + ->assertStatus(201) + ->assertJsonPath('team.owner.name', 'Darian'); } +} +``` The `assertJsonPath` method also accepts a closure, which may be used to dynamically determine if the assertion should pass: @@ -341,25 +515,45 @@ The `assertJsonPath` method also accepts a closure, which may be used to dynamic Laravel also offers a beautiful way to fluently test your application's JSON responses. To get started, pass a closure to the `assertJson` method. This closure will be invoked with an instance of `Illuminate\Testing\Fluent\AssertableJson` which can be used to make assertions against the JSON that was returned by your application. The `where` method may be used to make assertions against a particular attribute of the JSON, while the `missing` method may be used to assert that a particular attribute is missing from the JSON: - use Illuminate\Testing\Fluent\AssertableJson; +```php tab=Pest +use Illuminate\Testing\Fluent\AssertableJson; - /** - * A basic functional test example. - */ - public function test_fluent_json(): void - { - $response = $this->getJson('/users/1'); +test('fluent json', function () { + $response = $this->getJson('/users/1'); - $response - ->assertJson(fn (AssertableJson $json) => - $json->where('id', 1) - ->where('name', 'Victoria Faith') - ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) - ->whereNot('status', 'pending') - ->missing('password') - ->etc() - ); - } + $response + ->assertJson(fn (AssertableJson $json) => + $json->where('id', 1) + ->where('name', 'Victoria Faith') + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) + ->whereNot('status', 'pending') + ->missing('password') + ->etc() + ); +}); +``` + +```php tab=PHPUnit +use Illuminate\Testing\Fluent\AssertableJson; + +/** + * A basic functional test example. + */ +public function test_fluent_json(): void +{ + $response = $this->getJson('/users/1'); + + $response + ->assertJson(fn (AssertableJson $json) => + $json->where('id', 1) + ->where('name', 'Victoria Faith') + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) + ->whereNot('status', 'pending') + ->missing('password') + ->etc() + ); +} +``` #### Understanding the `etc` Method @@ -484,29 +678,50 @@ The `whereType` and `whereAllType` methods recognize the following types: `strin The `Illuminate\Http\UploadedFile` class provides a `fake` method which may be used to generate dummy files or images for testing. This, combined with the `Storage` facade's `fake` method, greatly simplifies the testing of file uploads. For example, you may combine these two features to easily test an avatar upload form: - image('avatar.jpg'); - namespace Tests\Feature; + $response = $this->post('/avatar', [ + 'avatar' => $file, + ]); - use Illuminate\Http\UploadedFile; - use Illuminate\Support\Facades\Storage; - use Tests\TestCase; + Storage::disk('avatars')->assertExists($file->hashName()); +}); +``` - class ExampleTest extends TestCase +```php tab=PHPUnit +image('avatar.jpg'); + $file = UploadedFile::fake()->image('avatar.jpg'); - $response = $this->post('/avatar', [ - 'avatar' => $file, - ]); + $response = $this->post('/avatar', [ + 'avatar' => $file, + ]); - Storage::disk('avatars')->assertExists($file->hashName()); - } + Storage::disk('avatars')->assertExists($file->hashName()); } +} +``` If you would like to assert that a given file does not exist, you may use the `assertMissing` method provided by the `Storage` facade: @@ -538,21 +753,33 @@ If needed, you may pass a `$mimeType` argument to the method to explicitly defin Laravel also allows you to render a view without making a simulated HTTP request to the application. To accomplish this, you may call the `view` method within your test. The `view` method accepts the view name and an optional array of data. The method returns an instance of `Illuminate\Testing\TestView`, which offers several methods to conveniently make assertions about the view's contents: - view('welcome', ['name' => 'Taylor']); - use Tests\TestCase; + $view->assertSee('Taylor'); +}); +``` - class ExampleTest extends TestCase +```php tab=PHPUnit +view('welcome', ['name' => 'Taylor']); + $view = $this->view('welcome', ['name' => 'Taylor']); - $view->assertSee('Taylor'); - } + $view->assertSee('Taylor'); } +} +``` The `TestView` class provides the following assertion methods: `assertSee`, `assertSeeInOrder`, `assertSeeText`, `assertSeeTextInOrder`, `assertDontSee`, and `assertDontSeeText`. @@ -1327,7 +1554,13 @@ Passing a closure as the second argument to the `assertViewHas` method will allo In addition, view data may be accessed as array variables on the response, allowing you to conveniently inspect it: - $this->assertEquals('Taylor', $response['name']); +```php tab=Pest +expect($response['name'])->toBe('Taylor'); +``` + +```php tab=PHPUnit +$this->assertEquals('Taylor', $response['name']); +``` #### assertViewHasAll diff --git a/mail.md b/mail.md index 9ee73b47a6c..e0304c55c08 100644 --- a/mail.md +++ b/mail.md @@ -986,37 +986,72 @@ Laravel provides a variety of methods for inspecting your mailable's structure. As you might expect, the "HTML" assertions assert that the HTML version of your mailable contains a given string, while the "text" assertions assert that the plain-text version of your mailable contains a given string: - use App\Mail\InvoicePaid; - use App\Models\User; +```php tab=Pest +use App\Mail\InvoicePaid; +use App\Models\User; + +test('mailable content', function () { + $user = User::factory()->create(); + + $mailable = new InvoicePaid($user); + + $mailable->assertFrom('jeffrey@example.com'); + $mailable->assertTo('taylor@example.com'); + $mailable->assertHasCc('abigail@example.com'); + $mailable->assertHasBcc('victoria@example.com'); + $mailable->assertHasReplyTo('tyler@example.com'); + $mailable->assertHasSubject('Invoice Paid'); + $mailable->assertHasTag('example-tag'); + $mailable->assertHasMetadata('key', 'value'); + + $mailable->assertSeeInHtml($user->email); + $mailable->assertSeeInHtml('Invoice Paid'); + $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']); + + $mailable->assertSeeInText($user->email); + $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']); + + $mailable->assertHasAttachment('/path/to/file'); + $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file')); + $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); +}); +``` - public function test_mailable_content(): void - { - $user = User::factory()->create(); - - $mailable = new InvoicePaid($user); - - $mailable->assertFrom('jeffrey@example.com'); - $mailable->assertTo('taylor@example.com'); - $mailable->assertHasCc('abigail@example.com'); - $mailable->assertHasBcc('victoria@example.com'); - $mailable->assertHasReplyTo('tyler@example.com'); - $mailable->assertHasSubject('Invoice Paid'); - $mailable->assertHasTag('example-tag'); - $mailable->assertHasMetadata('key', 'value'); - - $mailable->assertSeeInHtml($user->email); - $mailable->assertSeeInHtml('Invoice Paid'); - $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']); - - $mailable->assertSeeInText($user->email); - $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']); - - $mailable->assertHasAttachment('/path/to/file'); - $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file')); - $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']); - $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); - $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); - } +```php tab=PHPUnit +use App\Mail\InvoicePaid; +use App\Models\User; + +public function test_mailable_content(): void +{ + $user = User::factory()->create(); + + $mailable = new InvoicePaid($user); + + $mailable->assertFrom('jeffrey@example.com'); + $mailable->assertTo('taylor@example.com'); + $mailable->assertHasCc('abigail@example.com'); + $mailable->assertHasBcc('victoria@example.com'); + $mailable->assertHasReplyTo('tyler@example.com'); + $mailable->assertHasSubject('Invoice Paid'); + $mailable->assertHasTag('example-tag'); + $mailable->assertHasMetadata('key', 'value'); + + $mailable->assertSeeInHtml($user->email); + $mailable->assertSeeInHtml('Invoice Paid'); + $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']); + + $mailable->assertSeeInText($user->email); + $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']); + + $mailable->assertHasAttachment('/path/to/file'); + $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file')); + $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); +} +``` ### Testing Mailable Sending @@ -1025,38 +1060,68 @@ We suggest testing the content of your mailables separately from your tests that You may use the `Mail` facade's `fake` method to prevent mail from being sent. After calling the `Mail` facade's `fake` method, you may then assert that mailables were instructed to be sent to users and even inspect the data the mailables received: - instance( - Service::class, - Mockery::mock(Service::class, function (MockInterface $mock) { - $mock->shouldReceive('process')->once(); - }) - ); - } +```php tab=Pest +use App\Service; +use Mockery; +use Mockery\MockInterface; + +test('something can be mocked', function () { + $this->instance( + Service::class, + Mockery::mock(Service::class, function (MockInterface $mock) { + $mock->shouldReceive('process')->once(); + }) + ); +}); +``` + +```php tab=PHPUnit +use App\Service; +use Mockery; +use Mockery\MockInterface; + +public function test_something_can_be_mocked(): void +{ + $this->instance( + Service::class, + Mockery::mock(Service::class, function (MockInterface $mock) { + $mock->shouldReceive('process')->once(); + }) + ); +} +``` 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: @@ -88,27 +105,46 @@ Unlike traditional static method calls, [facades](/docs/{{version}}/facades) (in We can mock the call to the `Cache` facade by using the `shouldReceive` method, which will return an instance of a [Mockery](https://github.com/padraic/mockery) mock. Since facades are actually resolved and managed by the Laravel [service container](/docs/{{version}}/container), they have much more testability than a typical static class. For example, let's mock our call to the `Cache` facade's `get` method: - once() + ->with('key') + ->andReturn('value'); + + $response = $this->get('/users'); + + // ... +}); +``` - class UserControllerTest extends TestCase +```php tab=PHPUnit +once() - ->with('key') - ->andReturn('value'); + Cache::shouldReceive('get') + ->once() + ->with('key') + ->andReturn('value'); - $response = $this->get('/users'); + $response = $this->get('/users'); - // ... - } + // ... } +} +``` > [!WARNING] > 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. @@ -118,46 +154,86 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, If you would like to [spy](http://docs.mockery.io/en/latest/reference/spies.html) on a facade, you may call the `spy` method on the corresponding facade. Spies are similar to mocks; however, spies record any interaction between the spy and the code being tested, allowing you to make assertions after the code is executed: - use Illuminate\Support\Facades\Cache; +```php tab=Pest +get('/'); +test('values are be stored in cache', function () { + Cache::spy(); - $response->assertStatus(200); + $response = $this->get('/'); - Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10); - } + $response->assertStatus(200); + + Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10); +}); +``` + +```php tab=PHPUnit +use Illuminate\Support\Facades\Cache; + +public function test_values_are_be_stored_in_cache(): void +{ + Cache::spy(); + + $response = $this->get('/'); + + $response->assertStatus(200); + + Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10); +} +``` ## Interacting With Time When testing, you may occasionally need to modify the time returned by helpers such as `now` or `Illuminate\Support\Carbon::now()`. Thankfully, Laravel's base feature test class includes helpers that allow you to manipulate the current time: - use Illuminate\Support\Carbon; - - public function test_time_can_be_manipulated(): void - { - // Travel into the future... - $this->travel(5)->milliseconds(); - $this->travel(5)->seconds(); - $this->travel(5)->minutes(); - $this->travel(5)->hours(); - $this->travel(5)->days(); - $this->travel(5)->weeks(); - $this->travel(5)->years(); - - // Travel into the past... - $this->travel(-5)->hours(); - - // Travel to an explicit time... - $this->travelTo(now()->subHours(6)); - - // Return back to the present time... - $this->travelBack(); - } +```php tab=Pest +test('time can be manipulated', function () { + // Travel into the future... + $this->travel(5)->milliseconds(); + $this->travel(5)->seconds(); + $this->travel(5)->minutes(); + $this->travel(5)->hours(); + $this->travel(5)->days(); + $this->travel(5)->weeks(); + $this->travel(5)->years(); + + // Travel into the past... + $this->travel(-5)->hours(); + + // Travel to an explicit time... + $this->travelTo(now()->subHours(6)); + + // Return back to the present time... + $this->travelBack(); +}); +``` + +```php tab=PHPUnit +public function test_time_can_be_manipulated(): void +{ + // Travel into the future... + $this->travel(5)->milliseconds(); + $this->travel(5)->seconds(); + $this->travel(5)->minutes(); + $this->travel(5)->hours(); + $this->travel(5)->days(); + $this->travel(5)->weeks(); + $this->travel(5)->years(); + + // Travel into the past... + $this->travel(-5)->hours(); + + // Travel to an explicit time... + $this->travelTo(now()->subHours(6)); + + // Return back to the present time... + $this->travelBack(); +} +``` You may also provide a closure to the various time travel methods. The closure will be invoked with time frozen at the specified time. Once the closure has executed, time will resume as normal: @@ -185,13 +261,27 @@ The `freezeTime` method may be used to freeze the current time. Similarly, the ` As you would expect, all of the methods discussed above are primarily useful for testing time sensitive application behavior, such as locking inactive posts on a discussion forum: - use App\Models\Thread; - - public function test_forum_threads_lock_after_one_week_of_inactivity() - { - $thread = Thread::factory()->create(); - - $this->travel(1)->week(); - - $this->assertTrue($thread->isLockedByInactivity()); - } +```php tab=Pest +use App\Models\Thread; + +test('forum threads lock after one week of inactivity', function () { + $thread = Thread::factory()->create(); + + $this->travel(1)->week(); + + expect($thread->isLockedByInactivity())->toBeTrue(); +}); +``` + +```php tab=PHPUnit +use App\Models\Thread; + +public function test_forum_threads_lock_after_one_week_of_inactivity() +{ + $thread = Thread::factory()->create(); + + $this->travel(1)->week(); + + $this->assertTrue($thread->isLockedByInactivity()); +} +``` diff --git a/notifications.md b/notifications.md index 403b136721e..39004ee7413 100644 --- a/notifications.md +++ b/notifications.md @@ -1347,39 +1347,70 @@ You may use the `Notification` facade's `fake` method to prevent notifications f After calling the `Notification` facade's `fake` method, you may then assert that notifications were instructed to be sent to users and even inspect the data the notifications received: - create(), - ['create-servers'] - ); +test('servers can be created', function () { + Passport::actingAs( + User::factory()->create(), + ['create-servers'] + ); - $response = $this->post('/api/create-server'); + $response = $this->post('/api/create-server'); - $response->assertStatus(201); - } + $response->assertStatus(201); +}); +``` + +```php tab=PHPUnit +use App\Models\User; +use Laravel\Passport\Passport; + +public function test_servers_can_be_created(): void +{ + Passport::actingAs( + User::factory()->create(), + ['create-servers'] + ); + + $response = $this->post('/api/create-server'); + + $response->assertStatus(201); +} +``` Passport's `actingAsClient` method may be used to specify the currently authenticated client as well as its scopes. The first argument given to the `actingAsClient` method is the client instance and the second is an array of scopes that should be granted to the client's token: - use Laravel\Passport\Client; - use Laravel\Passport\Passport; +```php tab=Pest +use Laravel\Passport\Client; +use Laravel\Passport\Passport; - public function test_orders_can_be_retrieved(): void - { - Passport::actingAsClient( - Client::factory()->create(), - ['check-status'] - ); +test('orders can be retrieved', function () { + Passport::actingAsClient( + Client::factory()->create(), + ['check-status'] + ); - $response = $this->get('/api/orders'); + $response = $this->get('/api/orders'); - $response->assertStatus(200); - } + $response->assertStatus(200); +}); +``` + +```php tab=PHPUnit +use Laravel\Passport\Client; +use Laravel\Passport\Passport; + +public function test_orders_can_be_retrieved(): void +{ + Passport::actingAsClient( + Client::factory()->create(), + ['check-status'] + ); + + $response = $this->get('/api/orders'); + + $response->assertStatus(200); +} +``` diff --git a/pennant.md b/pennant.md index e0f73439176..e8a79140368 100644 --- a/pennant.md +++ b/pennant.md @@ -800,7 +800,17 @@ Feature::define('purchase-button', fn () => Arr::random([ To modify the feature's returned value in your tests, you may re-define the feature at the beginning of the test. The following test will always pass, even though the `Arr::random()` implementation is still present in the service provider: -```php +```php tab=Pest +use Laravel\Pennant\Feature; + +test('it can control feature values', function () { + Feature::define('purchase-button', 'seafoam-green'); + + $this->assertSame('seafoam-green', Feature::value('purchase-button')); +}); +``` + +```php tab=PHPUnit use Laravel\Pennant\Feature; public function test_it_can_control_feature_values() diff --git a/processes.md b/processes.md index fba80d999e0..15666a6d86d 100644 --- a/processes.md +++ b/processes.md @@ -394,7 +394,30 @@ Route::get('/import', function () { When testing this route, we can instruct Laravel to return a fake, successful process result for every invoked process by calling the `fake` method on the `Process` facade with no arguments. In addition, we can even [assert](#available-assertions) that a given process was "run": -```php +```php tab=Pest +get('/import'); + + // Simple process assertion... + Process::assertRan('bash import.sh'); + + // Or, inspecting the process configuration... + Process::assertRan(function (PendingProcess $process, ProcessResult $result) { + return $process->command === 'bash import.sh' && + $process->timeout === 60; + }); +}); +``` + +```php tab=PHPUnit ## 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: +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 Pest / PHPUnit may also be passed to the `test` command: ```shell sail test diff --git a/sanctum.md b/sanctum.md index ca97137aa9a..acac48dedd2 100644 --- a/sanctum.md +++ b/sanctum.md @@ -440,20 +440,38 @@ To allow users to revoke API tokens issued to mobile devices, you may list them 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; +```php tab=Pest +use App\Models\User; +use Laravel\Sanctum\Sanctum; - public function test_task_list_can_be_retrieved(): void - { - Sanctum::actingAs( - User::factory()->create(), - ['view-tasks'] - ); +test('task list can be retrieved', function () { + Sanctum::actingAs( + User::factory()->create(), + ['view-tasks'] + ); - $response = $this->get('/api/task'); + $response = $this->get('/api/task'); - $response->assertOk(); - } + $response->assertOk(); +}); +``` + +```php tab=PHPUnit +use App\Models\User; +use Laravel\Sanctum\Sanctum; + +public function test_task_list_can_be_retrieved(): void +{ + Sanctum::actingAs( + User::factory()->create(), + ['view-tasks'] + ); + + $response = $this->get('/api/task'); + + $response->assertOk(); +} +``` If you would like to grant all abilities to the token, you should include `*` in the ability list provided to the `actingAs` method: diff --git a/structure.md b/structure.md index 1598f50f2d4..f9152bf9562 100644 --- a/structure.md +++ b/structure.md @@ -91,7 +91,7 @@ The `storage/app/public` directory may be used to store user-generated files, su #### The Tests Directory -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 `tests` directory contains your automated tests. Example [Pest](https://pestphp.com) or [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 diff --git a/testing.md b/testing.md index 69ac8ac9284..60bd9e03a38 100644 --- a/testing.md +++ b/testing.md @@ -11,13 +11,13 @@ ## Introduction -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. +Laravel is built with testing in mind. In fact, support for testing with [Pest](https://pestphp.com) and [PHPUnit](https://phpunit.de) 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. 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. 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. +An `ExampleTest.php` file is provided in both the `Feature` and `Unit` test directories. After installing a new Laravel application, execute the `vendor/bin/pest`, `vendor/bin/phpunit`, or `php artisan test` commands to run your tests. ## Environment @@ -29,7 +29,7 @@ You are free to define other testing environment configuration values as necessa #### The `.env.testing` Environment File -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. +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 Pest and PHPUnit tests or executing Artisan commands with the `--env=testing` option. #### The `CreatesApplication` Trait @@ -58,37 +58,51 @@ php artisan make:test UserTest --pest php artisan make:test UserTest --unit --pest ``` -> [!NOTE] +> [!NOTE] > 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](https://phpunit.de). To run your tests, execute the `vendor/bin/phpunit` or `php artisan test` command from your terminal: +Once the test has been generated, you may define test as you normally would using Pest or PHPUnit. To run your tests, execute the `vendor/bin/pest`, `vendor/bin/phpunit`, or `php artisan test` command from your terminal: - toBeTrue(); +}); +``` - namespace Tests\Unit; +```php tab=PHPUnit +assertTrue(true); - } + $this->assertTrue(true); } +} +``` -> [!WARNING] +> [!WARNING] > If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. Typically, you should invoke `parent::setUp()` at the start of your own `setUp` method, and `parent::tearDown()` at the end of your `tearDown` method. ## Running Tests -As mentioned previously, once you've written tests, you may run them using `phpunit`: +As mentioned previously, once you've written tests, you may run them using `pest` or `phpunit`: -```shell +```shell tab=Pest +./vendor/bin/pest +``` + +```shell tab=PHPUnit ./vendor/bin/phpunit ``` @@ -107,7 +121,7 @@ php artisan test --testsuite=Feature --stop-on-failure ### Running Tests in Parallel -By default, Laravel and PHPUnit execute your tests sequentially within a single process. However, you may greatly reduce the amount of time it takes to run your tests by running tests simultaneously across multiple processes. To get started, you should install the `brianium/paratest` Composer package as a "dev" dependency. Then, include the `--parallel` option when executing the `test` Artisan command: +By default, Laravel and Pest / PHPUnit execute your tests sequentially within a single process. However, you may greatly reduce the amount of time it takes to run your tests by running tests simultaneously across multiple processes. To get started, you should install the `brianium/paratest` Composer package as a "dev" dependency. Then, include the `--parallel` option when executing the `test` Artisan command: ```shell composer require brianium/paratest --dev @@ -122,7 +136,7 @@ php artisan test --parallel --processes=4 ``` > [!WARNING] -> When running tests in parallel, some PHPUnit options (such as `--do-not-cache-result`) may not be available. +> When running tests in parallel, some Pest / PHPUnit options (such as `--do-not-cache-result`) may not be available. #### Parallel Testing and Databases diff --git a/vite.md b/vite.md index 3acd2bb8f27..021e9d2240d 100644 --- a/vite.md +++ b/vite.md @@ -602,7 +602,15 @@ Laravel's Vite integration will attempt to resolve your assets while running you If you would prefer to mock Vite during testing, you may call the `withoutVite` method, which is available for any tests that extend Laravel's `TestCase` class: -```php +```php tab=Pest +test('without vite example', function () { + $this->withoutVite(); + + // ... +}); +``` + +```php tab=PHPUnit use Tests\TestCase; class ExampleTest extends TestCase From 0cf23285511b8b3b591e840a509b668ad620ee78 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 1 Feb 2024 18:12:25 +0000 Subject: [PATCH 1321/2609] Removes documentation of `--pest` as is detected now (#9299) --- testing.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/testing.md b/testing.md index 60bd9e03a38..f3a579abcfb 100644 --- a/testing.md +++ b/testing.md @@ -51,13 +51,6 @@ If you would like to create a test within the `tests/Unit` directory, you may us php artisan make:test UserTest --unit ``` -If you would like to create a [Pest PHP](https://pestphp.com) test, you may provide the `--pest` option to the `make:test` command: - -```shell -php artisan make:test UserTest --pest -php artisan make:test UserTest --unit --pest -``` - > [!NOTE] > Test stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). From 6160ff529511cb9527cefa55a616c75075a202e6 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 1 Feb 2024 18:13:48 +0000 Subject: [PATCH 1322/2609] Specifies that `services` must be published (#9298) --- socialite.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/socialite.md b/socialite.md index f55cba04016..a771f0ba571 100644 --- a/socialite.md +++ b/socialite.md @@ -29,6 +29,12 @@ To get started with Socialite, use the Composer package manager to add the packa composer require laravel/socialite ``` +Next, you should publish the `config/services.php` configuration file using the `config:publish` Artisan command: + +```shell +php artisan config:publish services +``` + ## Upgrading Socialite From 6ace3e8575acc565cecdbdde0feabc3e9cd5c138 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 1 Feb 2024 18:14:07 +0000 Subject: [PATCH 1323/2609] Removes references to `CreatesApplication` (#9297) --- database-testing.md | 2 -- testing.md | 5 ----- vite.md | 2 -- 3 files changed, 9 deletions(-) diff --git a/database-testing.md b/database-testing.md index e1162fa5981..5da114a8748 100644 --- a/database-testing.md +++ b/database-testing.md @@ -165,8 +165,6 @@ Alternatively, you may instruct Laravel to automatically seed the database befor abstract class TestCase extends BaseTestCase { - use CreatesApplication; - /** * Indicates whether the default seeder should run before each test. * diff --git a/testing.md b/testing.md index f3a579abcfb..d8e1007c804 100644 --- a/testing.md +++ b/testing.md @@ -31,11 +31,6 @@ You are free to define other testing environment configuration values as necessa 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 Pest and PHPUnit tests or executing Artisan commands with the `--env=testing` option. - -#### The `CreatesApplication` Trait - -Laravel includes a `CreatesApplication` trait that is applied to your application's base `TestCase` class. This trait contains a `createApplication` method that bootstraps the Laravel application before running your tests. It's important that you leave this trait at its original location as some features, such as Laravel's parallel testing feature, depend on it. - ## Creating Tests diff --git a/vite.md b/vite.md index 021e9d2240d..01bf4600004 100644 --- a/vite.md +++ b/vite.md @@ -635,8 +635,6 @@ use Illuminate\Foundation\Testing\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { - use CreatesApplication; - protected function setUp(): void// [tl! add:start] { parent::setUp(); From c7f1d2092cc8878ee36eabce7bfd72f86da6d287 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:47:35 +0330 Subject: [PATCH 1324/2609] Update dusk.md (#9294) --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index 1ee5ba5be73..8fb7f8e6ad8 100644 --- a/dusk.md +++ b/dusk.md @@ -61,7 +61,7 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome) and add the `laravel/dusk` Composer dependency to your project: ```shell -composer require --dev laravel/dusk +composer require laravel/dusk --dev ``` > [!WARNING] From 298272be28936d28da4c800ff6b519a05f0cf687 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 1 Feb 2024 18:38:31 +0000 Subject: [PATCH 1325/2609] Documents casts method in favour of casts property (#9300) --- eloquent-mutators.md | 171 +++++++++++++++++++++++--------------- eloquent-serialization.md | 11 ++- 2 files changed, 112 insertions(+), 70 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index d53e1e940e9..3aeaeca0981 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -196,9 +196,9 @@ protected function address(): Attribute ## Attribute Casting -Attribute casting provides functionality similar to accessors and mutators without requiring you to define any additional methods on your model. Instead, your model's `$casts` property provides a convenient method of converting attributes to common data types. +Attribute casting provides functionality similar to accessors and mutators without requiring you to define any additional methods on your model. Instead, your model's `casts` method provides a convenient way of converting attributes to common data types. -The `$casts` property should be an array where the key is the name of the attribute being cast and the value is the type you wish to cast the column to. The supported cast types are: +The `casts` method should return an array where the key is the name of the attribute being cast and the value is the type you wish to cast the column to. The supported cast types are:
    @@ -237,13 +237,16 @@ To demonstrate attribute casting, let's cast the `is_admin` attribute, which is class User extends Model { /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'is_admin' => 'boolean', - ]; + protected function casts(): array + { + return [ + 'is_admin' => 'boolean', + ]; + } } After defining the cast, the `is_admin` attribute will always be cast to a boolean when you access it, even if the underlying value is stored in the database as an integer: @@ -279,13 +282,16 @@ You may use the `Illuminate\Database\Eloquent\Casts\AsStringable` cast class to class User extends Model { /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'directory' => AsStringable::class, - ]; + protected function casts(): array + { + return [ + 'directory' => AsStringable::class, + ]; + } } @@ -302,13 +308,16 @@ The `array` cast is particularly useful when working with columns that are store class User extends Model { /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'options' => 'array', - ]; + protected function casts(): array + { + return [ + 'options' => 'array', + ]; + } } Once the cast is defined, you may access the `options` attribute and it will automatically be deserialized from JSON into a PHP array. When you set the value of the `options` attribute, the given array will automatically be serialized back into JSON for storage: @@ -345,26 +354,32 @@ To solve this, Laravel offers an `AsArrayObject` cast that casts your JSON attri use Illuminate\Database\Eloquent\Casts\AsArrayObject; /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'options' => AsArrayObject::class, - ]; + protected function casts(): array + { + return [ + 'options' => AsArrayObject::class, + ]; + } Similarly, Laravel offers an `AsCollection` cast that casts your JSON attribute to a Laravel [Collection](/docs/{{version}}/collections) instance: use Illuminate\Database\Eloquent\Casts\AsCollection; /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'options' => AsCollection::class, - ]; + protected function casts(): array + { + return [ + 'options' => AsCollection::class, + ]; + } If you would like the `AsCollection` cast to instantiate a custom collection class instead of Laravel's base collection class, you may provide the collection class name as a cast argument: @@ -372,29 +387,35 @@ If you would like the `AsCollection` cast to instantiate a custom collection cla use Illuminate\Database\Eloquent\Casts\AsCollection; /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'options' => AsCollection::class.':'.OptionCollection::class, - ]; + protected function casts(): array + { + return [ + 'options' => AsCollection::using(OptionCollection::class), + ]; + } ### Date Casting -By default, Eloquent will cast the `created_at` and `updated_at` columns to instances of [Carbon](https://github.com/briannesbitt/Carbon), which extends the PHP `DateTime` class and provides an assortment of helpful methods. You may cast additional date attributes by defining additional date casts within your model's `$casts` property array. Typically, dates should be cast using the `datetime` or `immutable_datetime` cast types. +By default, Eloquent will cast the `created_at` and `updated_at` columns to instances of [Carbon](https://github.com/briannesbitt/Carbon), which extends the PHP `DateTime` class and provides an assortment of helpful methods. You may cast additional date attributes by defining additional date casts within your model's `casts` method. Typically, dates should be cast using the `datetime` or `immutable_datetime` cast types. When defining a `date` or `datetime` cast, you may also specify the date's format. This format will be used when the [model is serialized to an array or JSON](/docs/{{version}}/eloquent-serialization): /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'created_at' => 'datetime:Y-m-d', - ]; + protected function casts(): array + { + return [ + 'created_at' => 'datetime:Y-m-d', + ]; + } When a column is cast as a date, you may set the corresponding model attribute value to a UNIX timestamp, date string (`Y-m-d`), date-time string, or a `DateTime` / `Carbon` instance. The date's value will be correctly converted and stored in your database. @@ -427,18 +448,21 @@ If a custom format is applied to the `date` or `datetime` cast, such as `datetim ### Enum Casting -Eloquent also allows you to cast your attribute values to PHP [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: +Eloquent also allows you to cast your attribute values to PHP [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `casts` method: use App\Enums\ServerStatus; /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'status' => ServerStatus::class, - ]; + protected function casts(): array + { + return [ + 'status' => ServerStatus::class, + ]; + } Once you have defined the cast on your model, the specified attribute will be automatically cast to and from an enum when you interact with the attribute: @@ -457,13 +481,16 @@ Sometimes you may need your model to store an array of enum values within a sing use Illuminate\Database\Eloquent\Casts\AsEnumCollection; /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'statuses' => AsEnumCollection::class.':'.ServerStatus::class, - ]; + protected function casts(): array + { + return [ + 'statuses' => AsEnumCollection::of(ServerStatus::class), + ]; + } ### Encrypted Casting @@ -555,13 +582,16 @@ Once you have defined a custom cast type, you may attach it to a model attribute class User extends Model { /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'options' => Json::class, - ]; + protected function casts(): array + { + return [ + 'options' => Json::class, + ]; + } } @@ -708,13 +738,16 @@ A classic example of an inbound only cast is a "hashing" cast. For example, we m When attaching a custom cast to a model, cast parameters may be specified by separating them from the class name using a `:` character and comma-delimiting multiple parameters. The parameters will be passed to the constructor of the cast class: /** - * The attributes that should be cast. + * Get the attributes that should be cast. * - * @var array + * @return array */ - protected $casts = [ - 'secret' => Hash::class.':sha256', - ]; + protected function casts(): array + { + return [ + 'secret' => Hash::class.':sha256', + ]; + } ### Castables @@ -723,9 +756,12 @@ You may want to allow your application's value objects to define their own custo use App\ValueObjects\Address; - protected $casts = [ - 'address' => Address::class, - ]; + protected function casts(): array + { + return [ + 'address' => Address::class, + ]; + } Objects that implement the `Castable` interface must define a `castUsing` method that returns the class name of the custom caster class that is responsible for casting to and from the `Castable` class: @@ -749,13 +785,16 @@ Objects that implement the `Castable` interface must define a `castUsing` method } } -When using `Castable` classes, you may still provide arguments in the `$casts` definition. The arguments will be passed to the `castUsing` method: +When using `Castable` classes, you may still provide arguments in the `casts` method definition. The arguments will be passed to the `castUsing` method: use App\ValueObjects\Address; - protected $casts = [ - 'address' => Address::class.':argument', - ]; + protected function casts(): array + { + return [ + 'address' => Address::class.':argument', + ]; + } #### Castables & Anonymous Cast Classes diff --git a/eloquent-serialization.md b/eloquent-serialization.md index 4192403d37e..98e1956a5f0 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -204,7 +204,10 @@ You may customize the default serialization format by overriding the `serializeD You may customize the serialization format of individual Eloquent date attributes by specifying the date format in the model's [cast declarations](/docs/{{version}}/eloquent-mutators#attribute-casting): - protected $casts = [ - 'birthday' => 'date:Y-m-d', - 'joined_at' => 'datetime:Y-m-d H:00', - ]; + protected function casts(): array + { + return [ + 'birthday' => 'date:Y-m-d', + 'joined_at' => 'datetime:Y-m-d H:00', + ]; + } From 1b40cbdc9f2b4c7c4beabfab7f41a5b4088d4ee8 Mon Sep 17 00:00:00 2001 From: Davo Date: Thu, 1 Feb 2024 12:40:36 -0600 Subject: [PATCH 1326/2609] Don't suggest cache tags for Redis (#9286) * Don't suggest cache tags for redis * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/upgrade.md b/upgrade.md index 8a46952f059..c615a88dadd 100644 --- a/upgrade.md +++ b/upgrade.md @@ -130,11 +130,7 @@ The `registerPolicies` method of the `AuthServiceProvider` is now invoked automa **Likelihood Of Impact: Medium** -Redis [cache tag](/docs/{{version}}/cache#cache-tags) support has been rewritten for better performance and storage efficiency. In previous releases of Laravel, stale cache tags would accumulate in the cache when using Redis as your application's cache driver. - -However, to properly prune stale cache tag entries, Laravel's new `cache:prune-stale-tags` Artisan command should be [scheduled](/docs/{{version}}/scheduling) in your application's `App\Console\Kernel` class: - - $schedule->command('cache:prune-stale-tags')->hourly(); +Usage of `Cache::tags()` is only recommended for applications using Memcached. If you are using Redis as your application's cache driver, you should consider moving to Memcached or using an alternative solution. ### Database From 8dacb4ca63361ccb80fb45e1c482d1fee39ecebd Mon Sep 17 00:00:00 2001 From: Jason Bosco Date: Fri, 2 Feb 2024 10:44:26 -0600 Subject: [PATCH 1327/2609] Update info about handling Typesense schema changes (#9301) * Update info about handling Typesense schema changes * Update scout.md --------- Co-authored-by: Taylor Otwell --- scout.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scout.md b/scout.md index ae83455bcc3..535fb5bb18b 100644 --- a/scout.md +++ b/scout.md @@ -177,7 +177,9 @@ public function toSearchableArray() } ``` -You should also define your Typesense collection schemas in your application's `config/scout.php` file. A collection schema describes the data types of each field that is searchable via Typesense. For more information on all available schema options, please consult the [Typesense documentation](https://typesense.org/docs/latest/api/collections.html#schema-parameters). If you need to change your Typesense collection's schema after it has been defined, you must do so via Typesense's API. +You should also define your Typesense collection schemas in your application's `config/scout.php` file. A collection schema describes the data types of each field that is searchable via Typesense. For more information on all available schema options, please consult the [Typesense documentation](https://typesense.org/docs/latest/api/collections.html#schema-parameters). + +If you need to change your Typesense collection's schema after it has been defined, you may either run `scout:flush` and `scout:import`, which will delete all existing indexed data and recreate the schema. Or, you may use Typesense's API to modify the collection's schema without removing any indexed data. If your searchable model is soft deletable, you should define a `__soft_deleted` field in the model's corresponding Typesense schema within your application's `config/scout.php` configuration file: From b4e2e3ee15726166362e4965a84af6eac53f59e4 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 7 Feb 2024 08:53:05 +0100 Subject: [PATCH 1328/2609] Update releases.md --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 6caff873b4c..be76277df0d 100644 --- a/releases.md +++ b/releases.md @@ -28,7 +28,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | --- | --- | --- | --- | --- | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | -| 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | +| 10 | 8.1 - 8.3 | Q1 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 - 8.3 | Q1 2024 | August 5th, 2025 | February 3rd, 2026 |
    From 4dff8e43d8b2d20053866bb4ebe080d74aa16985 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 7 Feb 2024 08:53:34 +0100 Subject: [PATCH 1329/2609] Update releases.md --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index be76277df0d..6caff873b4c 100644 --- a/releases.md +++ b/releases.md @@ -28,7 +28,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | --- | --- | --- | --- | --- | | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | -| 10 | 8.1 - 8.3 | Q1 2023 | August 6th, 2024 | February 4th, 2025 | +| 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 - 8.3 | Q1 2024 | August 5th, 2025 | February 3rd, 2026 |
    From c05c67d6cbcbedb81953d220595ff821fe387cfa Mon Sep 17 00:00:00 2001 From: amoschou <4948673+amoschou@users.noreply.github.com> Date: Fri, 9 Feb 2024 01:08:30 +1030 Subject: [PATCH 1330/2609] Update eloquent.md (#9315) Generalised a comment. --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index a7a62eee992..955e73e46c1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -188,7 +188,7 @@ If your model's primary key is not an integer, you should define a protected `$k class Flight extends Model { /** - * The data type of the auto-incrementing ID. + * The data type of the primary key ID. * * @var string */ From a945883abe3353bb3124043d744d408b4755e9ba Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 8 Feb 2024 15:41:53 +0100 Subject: [PATCH 1331/2609] Add v12 indicate dates (#9314) * Add v12 indicate dates * Update releases.md Co-authored-by: Andy Hinkle --------- Co-authored-by: Andy Hinkle --- releases.md | 1 + 1 file changed, 1 insertion(+) diff --git a/releases.md b/releases.md index 7771ff005e1..e112e2e2394 100644 --- a/releases.md +++ b/releases.md @@ -29,6 +29,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 - 8.3 | Q1 2024 | August 5th, 2025 | February 3rd, 2026 | +| 12 | 8.2 - 8.3 | Q1 2025 | August, 2026 | February, 2027 |
    From 96594e4610ea8195bf06b43d184e00304377e5f7 Mon Sep 17 00:00:00 2001 From: Patrick Carlo-Hickman Date: Thu, 8 Feb 2024 09:45:33 -0500 Subject: [PATCH 1332/2609] Remove warning about schema dumps and in-memory SQLite databases. (#9312) --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index 230ec7604ae..3afb0cc1b8b 100644 --- a/migrations.md +++ b/migrations.md @@ -71,7 +71,7 @@ php artisan schema:dump --database=testing --prune You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure. > [!WARNING] -> Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. Schema dumps may not be restored to in-memory SQLite databases. +> Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. ## Migration Structure From 01022bfaf930303cb15c61985cd00f18d3caa113 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 8 Feb 2024 23:49:45 +0900 Subject: [PATCH 1333/2609] [10.x] Be more clear and consistent (#9309) --- validation.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/validation.md b/validation.md index 19f4f192ded..c4b64ed455f 100644 --- a/validation.md +++ b/validation.md @@ -970,12 +970,12 @@ 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 or similar fields. +The field under validation must be `"yes"`, `"on"`, `1`, `"1"`, `true`, or `"true"`. This is useful for validating "Terms of Service" acceptance or similar fields. #### accepted_if:anotherfield,value,... -The field under validation must be `"yes"`, `"on"`, `1`, or `true` if another field under validation is equal to a specified value. This is useful for validating "Terms of Service" acceptance or similar fields. +The field under validation must be `"yes"`, `"on"`, `1`, `"1"`, `true`, or `"true"` if another field under validation is equal to a specified value. This is useful for validating "Terms of Service" acceptance or similar fields. #### active_url @@ -1131,12 +1131,12 @@ The field under validation must be numeric and must contain the specified number #### declined -The field under validation must be `"no"`, `"off"`, `0`, or `false`. +The field under validation must be `"no"`, `"off"`, `0`, `"0"`, `false`, or `"false"`. #### declined_if:anotherfield,value,... -The field under validation must be `"no"`, `"off"`, `0`, or `false` if another field under validation is equal to a specified value. +The field under validation must be `"no"`, `"off"`, `0`, `"0"`, `false`, or `"false"` if another field under validation is equal to a specified value. #### different:_field_ @@ -1702,7 +1702,7 @@ If you would like to construct a more complex condition for the `required_if` ru #### required_if_accepted:_anotherfield_,... -The field under validation must be present and not empty if the _anotherfield_ field is equal to `yes`, `on`, `1`, `"1"`, `true`, or `"true"`. +The field under validation must be present and not empty if the _anotherfield_ field is equal to `"yes"`, `"on"`, `1`, `"1"`, `true`, or `"true"`. #### required_unless:_anotherfield_,_value_,... From 307b4681448dc47a1558a22a1eeadc1aad0b0cab Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Thu, 8 Feb 2024 14:55:08 +0000 Subject: [PATCH 1334/2609] [10.x] Adds dispatch helper info to upgrades (#9306) * Add dispatch helper info to L10 Upgrade docs * remove whitespace * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/upgrade.md b/upgrade.md index c615a88dadd..13c71d0d186 100644 --- a/upgrade.md +++ b/upgrade.md @@ -219,6 +219,13 @@ If you are using third-party logging services such as BugSnag or Rollbar, you ma The deprecated `Bus::dispatchNow` and `dispatch_now` methods have been removed. Instead, your application should use the `Bus::dispatchSync` and `dispatch_sync` methods, respectively. + +#### The `dispatch()` Helper Return Value + +**Likelihood Of Impact: Low** + +Invoking `dispatch` with a class that does not implement `Illuminate\Contracts\Queue` would previously return the result of the class's `handle` method. However, this will now return an `Illuminate\Foundation\Bus\PendingBatch` instance. You may use `dispatch_sync()` to replicate the previous behavior. + ### Routing From 02e6dff35f1785715550571d22e380f72abefcd6 Mon Sep 17 00:00:00 2001 From: Ahmet Imamoglu Date: Thu, 8 Feb 2024 18:07:07 +0300 Subject: [PATCH 1335/2609] Updated map type declaration (#9316) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index d81b0194484..7ad60a58102 100644 --- a/collections.md +++ b/collections.md @@ -16,7 +16,7 @@ The `Illuminate\Support\Collection` class provides a fluent, convenient wrapper for working with arrays of data. For example, check out the following code. We'll use the `collect` helper to create a new collection instance from the array, run the `strtoupper` function on each element, and then remove all empty elements: - $collection = collect(['taylor', 'abigail', null])->map(function (string $name) { + $collection = collect(['taylor', 'abigail', null])->map(function (?string $name) { return strtoupper($name); })->reject(function (string $name) { return empty($name); From 3be2d18bf5bb509332cf24ddbd828610e793ff94 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 8 Feb 2024 09:32:38 -0600 Subject: [PATCH 1336/2609] wip --- localization.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/localization.md b/localization.md index d42dc3f248d..c0fffeec92f 100644 --- a/localization.md +++ b/localization.md @@ -10,7 +10,6 @@ - [Retrieving Translation Strings](#retrieving-translation-strings) - [Replacing Parameters in Translation Strings](#replacing-parameters-in-translation-strings) - [Pluralization](#pluralization) - - [Handling Missing Translation Strings](#handling-missing-translation-strings) - [Overriding Package Language Files](#overriding-package-language-files) @@ -234,27 +233,6 @@ If you would like to display the integer value that was passed to the `trans_cho 'apples' => '{0} There are none|{1} There is one|[2,*] There are :count', - -### Handling Missing Translation Strings - -Typically, when attempting to translate a string that does not have a corresponding key in your language files, Laravel will return the translation string key. - -You may customize or intercept this behavior using the `handleMissingKeysUsing` method. Typically, you should invoke this method in the `boot` method of your application's `AppServiceProvider`: - - use Illuminate\Support\Facades\Lang; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Lang::handleMissingKeysUsing(function (string $key, array $replacements, string $locale) { - info("Missing translation key [$key] detected."); - - return $key; - }); - } - ## Overriding Package Language Files From d8adbbbc2b6a07e9c804b6e7b30b8690e362cef7 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 8 Feb 2024 19:26:38 +0330 Subject: [PATCH 1337/2609] [11.x] Inspecting database (#9317) * inspecting databases * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/upgrade.md b/upgrade.md index 2c0203f990c..3291b785367 100644 --- a/upgrade.md +++ b/upgrade.md @@ -187,14 +187,16 @@ The following list of Doctrine DBAL related classes and methods have been remove In addition, registering custom Doctrine types via `dbal.types` in your application's `database` configuration file is no longer required. +If you were previously using Doctrine DBAL to inspect your database and its associated tables, you may use Laravel's new native schema methods (`Schema::getTables()`, `Schema::getColumns()`, `Schema::getIndexes()`, `Schema::getForeignKeys()`, etc.) instead. + #### Deprecated Schema Methods **Likelihood Of Impact: Very Low** -The deprecated `Schema::getAllTables()`, `Schema::getAllViews()`, and `Schema::getAllTypes()` methods have been removed in favor of new `Schema::getTables()`, `Schema::getViews()`, and `Schema::getTypes()` methods. +The deprecated, Doctrine based `Schema::getAllTables()`, `Schema::getAllViews()`, and `Schema::getAllTypes()` methods have been removed in favor of new Laravel native `Schema::getTables()`, `Schema::getViews()`, and `Schema::getTypes()` methods. -When using PostgreSQL, none of the new schema methods will accept a three-part reference (e.g. `database.schema.table`). Therefore, you should use `connection()` to declare the database instead: +When using PostgreSQL and SQL Server, none of the new schema methods will accept a three-part reference (e.g. `database.schema.table`). Therefore, you should use `connection()` to declare the database instead: ```php Schema::connection('database')->hasTable('schema.table'); From 8155f824bef698e679307e52751ec004abd78c47 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:08:21 +0900 Subject: [PATCH 1338/2609] [10.x] Update Laravel installer to 5.5 (#9320) * [10.x] Update Laravel installer to 5.5 * Update installation.md --------- Co-authored-by: Taylor Otwell --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 82f138705d5..5920f5ea777 100644 --- a/installation.md +++ b/installation.md @@ -64,7 +64,7 @@ composer create-project laravel/laravel example-app Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: ```nothing -composer global require laravel/installer:^5.4 +composer global require laravel/installer laravel new example-app ``` From a5baa4885d5738e5cb7d5b6bda672bffb3ab02c8 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 9 Feb 2024 18:42:37 +0330 Subject: [PATCH 1339/2609] [11.x] Enable passport's password grant type (#9318) * enable password grant * Update passport.md --------- Co-authored-by: Taylor Otwell --- passport.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/passport.md b/passport.md index db5915134bb..0b3f72df107 100644 --- a/passport.md +++ b/passport.md @@ -658,6 +658,16 @@ If the state parameter matches, the consumer should issue a `POST` request to yo 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. +To enable the password grant, call the `enablePasswordGrant` method in the `boot` method of your application's `App\Providers\AppServiceProvider` class: + + /** + * Bootstrap any application services. + */ + public function boot(): void + { + Passport::enablePasswordGrant(); + } + ### Creating a Password Grant Client From 0795d6a3c908a682ade3327a04e9f486dd216368 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 9 Feb 2024 15:22:40 +0000 Subject: [PATCH 1340/2609] [11.x] Updates `Databases and Migrations` on installation (#9303) * Updates initial configuration related to migrations * formatting * move note * formatting --------- Co-authored-by: Taylor Otwell --- installation.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/installation.md b/installation.md index 82f138705d5..2f8fbe19710 100644 --- a/installation.md +++ b/installation.md @@ -64,7 +64,7 @@ composer create-project laravel/laravel example-app Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: ```nothing -composer global require laravel/installer:^5.4 +composer global require laravel/installer laravel new example-app ``` @@ -79,7 +79,7 @@ php artisan serve Once you have started the Artisan development server, your application will be accessible in your web browser at [http://localhost:8000](http://localhost:8000). Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). -> [!NOTE] +> [!NOTE] > If you would like a head start when developing your Laravel application, 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. @@ -96,36 +96,35 @@ Since many of Laravel's configuration option values may vary depending on whethe Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. -> [!NOTE] +> [!NOTE] > For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). ### Databases and Migrations -Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with a MySQL database and will access the database at `127.0.0.1`. +Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with a SQLite database. -> [!NOTE] -> If you are developing on macOS and need to install MySQL, Postgres, or Redis locally, consider using [DBngin](https://dbngin.com/). +During the creation of the project, Laravel created a `database/database.sqlite` file for you, and ran the necessary migrations to create the application's database tables. -If you do not want to install MySQL or Postgres on your local machine, you can always use a [SQLite](https://www.sqlite.org/index.html) database. SQLite is a small, fast, self-contained database engine. To get started, update your `.env` configuration file to use Laravel's `sqlite` database driver. You may remove the other database configuration options: +If you prefer to use another database driver such as MySQL or Postgres, you can update your `.env` configuration file to use the appropriate database. For example, if you wish to use MySQL, update your `.env` configuration file's `DB_*` variables like so: ```ini -DB_CONNECTION=sqlite # [tl! add] -DB_CONNECTION=mysql # [tl! remove] -DB_HOST=127.0.0.1 # [tl! remove] -DB_PORT=3306 # [tl! remove] -DB_DATABASE=laravel # [tl! remove] -DB_USERNAME=root # [tl! remove] -DB_PASSWORD= # [tl! remove] +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=laravel +DB_USERNAME=root +DB_PASSWORD= ``` -Once you have configured your SQLite database, you may run your application's [database migrations](/docs/{{version}}/migrations), which will create your application's database tables: +If you choose to use a database other than SQLite, you will need to create the database and run your application's [database migrations](/docs/{{version}}/migrations): ```shell php artisan migrate ``` -If an SQLite database does not exist for your application, Laravel will ask you if you would like the database to be created. Typically, the SQLite database file will be created at `database/database.sqlite`. +> [!NOTE] +> If you are developing on macOS and need to install MySQL, Postgres, or Redis locally, consider using [DBngin](https://dbngin.com/). ### Directory Configuration From c0c56323507b2d765c6342a26b35c680ba710eda Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 9 Feb 2024 15:26:28 +0000 Subject: [PATCH 1341/2609] [11.x] Rewrites `structure` section (#9302) * Rewrites structure * Minor touches * formatting --------- Co-authored-by: Taylor Otwell --- structure.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/structure.md b/structure.md index f9152bf9562..9708675af64 100644 --- a/structure.md +++ b/structure.md @@ -51,7 +51,7 @@ The `bootstrap` directory contains the `app.php` file which bootstraps the frame #### The Config Directory -The `config` directory, as the name implies, contains all of your application's configuration files. It's a great idea to read through all of these files and familiarize yourself with all of the options available to you. +The `config` directory, as the name implies, contains all of your application's configuration files. By default, the directory is empty; however, packages may place their configuration files in this directory. You may also create your own configuration files in this directory. #### The Database Directory @@ -71,14 +71,16 @@ The `resources` directory contains your [views](/docs/{{version}}/views) as well #### 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, two route files are included with Laravel: `web.php` and `console.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 then all 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. 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 `web.php` file contains routes that Laravel 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 all your routes will most likely be defined in the `web.php` file. 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. +Optionally, you may install additional route files for API routes (`api.php`) and broadcasting channels (`channels.php`), via the `install:api` and `install:broadcasting` Artisan commands. + +The `api.php` file contains routes that 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 `channels.php` file is where you may register all of the [event broadcasting](/docs/{{version}}/broadcasting) channels that your application supports. @@ -91,7 +93,7 @@ The `storage/app/public` directory may be used to store user-generated files, su #### The Tests Directory -The `tests` directory contains your automated tests. Example [Pest](https://pestphp.com) or [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 `tests` directory contains your automated tests. Example [Pest](https://pestphp.com) or [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 `/vendor/bin/pest` or `/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 @@ -103,9 +105,9 @@ The `vendor` directory contains your [Composer](https://getcomposer.org) depende The majority of your application is housed in the `app` directory. By default, this directory is namespaced under `App` and is autoloaded by Composer using the [PSR-4 autoloading standard](https://www.php-fig.org/psr/psr-4/). -The `app` directory contains a variety of additional directories such as `Console`, `Http`, and `Providers`. Think of the `Console` and `Http` directories as providing an API into the core of your application. The HTTP protocol and CLI are both mechanisms to interact with your application, but do not actually contain application logic. In other words, they are two ways of issuing commands to your application. The `Console` directory contains all of your Artisan commands, while the `Http` directory contains your controllers, middleware, and requests. +By default, the `app` directory contains the `Http`, `Models`, and `Providers` directories. However, over time, a variety of other directories will be generated inside the app directory as you use the make Artisan commands to generate classes. For example, the `app/Console` directory will not exist until you execute the `make:command` Artisan command to generate a command class. -A variety of other directories will be generated inside the `app` directory as you use the `make` Artisan commands to generate classes. So, for example, the `app/Jobs` directory will not exist until you execute the `make:job` Artisan command to generate a job class. +Both the `Console` and `Http` directories are further explained in their respective sections below, but think of the `Console` and `Http` directories as providing an API into the core of your application. The HTTP protocol and CLI are both mechanisms to interact with your application, but do not actually contain application logic. In other words, they are two ways of issuing commands to your application. The `Console` directory contains all of your Artisan commands, while the `Http` directory contains your controllers, middleware, and requests. > [!NOTE] > Many of the classes in the `app` directory can be generated by Artisan via commands. To review the available commands, run the `php artisan list make` command in your terminal. From f5c7a09e45d45091e820e92f699367d7ef9561f6 Mon Sep 17 00:00:00 2001 From: Joseph Farruggio Date: Fri, 9 Feb 2024 10:04:16 -0600 Subject: [PATCH 1342/2609] Update blade docs to show how to conditionally check for $slot content (#9304) * Share example of checking for $slot content Folks might mistakenly expect @if($slot) to work, like I did. It would be helpful to clarify with an example of $slot->isEmpty(). * formatting --------- Co-authored-by: Taylor Otwell --- blade.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/blade.md b/blade.md index 8d78f399223..f703461d991 100644 --- a/blade.md +++ b/blade.md @@ -1148,6 +1148,20 @@ You may define the content of the named slot using the `x-slot` tag. Any content ``` +You may invoke a slot's `isEmpty` method to determine if the slot contains content: + +```blade +{{ $title }} + +
    + @if ($slot->isEmpty()) + This is default content if the slot is empty. + @else + {{ $slot }} + @endif +
    +``` + #### Scoped Slots From ff9705d9281d9d72aa8469e1222cf12e6ae1e619 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Sun, 11 Feb 2024 22:04:33 +0330 Subject: [PATCH 1343/2609] [10.x] Add example for `@use` directive (#9313) * Update blade.md * Update blade.md --------- Co-authored-by: Taylor Otwell --- blade.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blade.md b/blade.md index f703461d991..056347029f4 100644 --- a/blade.md +++ b/blade.md @@ -629,6 +629,12 @@ Or, if you only need to use PHP to import a class, you may use the `@use` direct @use('App\Models\Flight') ``` +A second argument may be provided to the `@use` directive to alias the imported class: + +```php +@use('App\Models\Flight', 'FlightModel') +``` + ### Comments From 6ce1b7642303fc22b7aad6f71f576912ca391b32 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 13 Feb 2024 03:54:25 +1100 Subject: [PATCH 1344/2609] [10.x] Add "name" property docs for Pennant (#9322) * Add "name" property docs * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pennant.md b/pennant.md index e0f73439176..56038e1c11c 100644 --- a/pennant.md +++ b/pennant.md @@ -143,6 +143,28 @@ class NewApi > [!NOTE] Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. +#### Customizing the Stored Feature Name + +By default, Pennant will store the feature class's fully qualified class name. If you would like to decouple the stored feature name from the application's internal structure, you may specify a `$name` property on the feature class. The value of this property will be stored in place of the class name: + +```php + ## Checking Features From eb80ae09e91fa364dc1719f928a47a7a1ec36212 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Mon, 12 Feb 2024 17:01:05 +0000 Subject: [PATCH 1345/2609] [10.x] Add 'Assigning a validation message to a different key via a closure' section to upgrade.md (#9324) * [10.x] Assigning a validation message info in upgrade docs Found during a recent upgrade. Appears to be very obscure but a breaking change that others might come across when upgrading from 9 to 10. * Update upgrade.md * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/upgrade.md b/upgrade.md index 13c71d0d186..cefe59712cf 100644 --- a/upgrade.md +++ b/upgrade.md @@ -292,6 +292,22 @@ public function rules() } ``` + +#### Validation Messages and Closure Rules + +**Likelihood Of Impact: Very Low** + +Previously, you could assign a failure message to a different key by providing an array to the `$fail` callback injected into Closure based validation rules. However, you should now provide the key as the first argument and the failure message as the second argument: + +```php +Validator::make([ + 'foo' => 'string', + 'bar' => [function ($attribute, $value, $fail) { + $fail('foo', 'Something went wrong!'); + }], +]); +``` + #### Form Request After Method From aa18086c63330d2e0344c8d6fbe31994580d44fa Mon Sep 17 00:00:00 2001 From: Achyut Neupane <30431426+achyutkneupane@users.noreply.github.com> Date: Mon, 12 Feb 2024 22:52:07 +0545 Subject: [PATCH 1346/2609] Add maintenance mode warning in Queues Documentation (#9323) * Maintenance mode warning in Queues Documentation * Sentence replaced to make more sense. * Force flag description added To enforce queue run in maintenance mode * Formatting fix * formatting --------- Co-authored-by: Taylor Otwell --- queues.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/queues.md b/queues.md index 80188c236f0..5010b3f9e47 100644 --- a/queues.md +++ b/queues.md @@ -1673,6 +1673,17 @@ When jobs are available on the queue, the worker will keep processing jobs with php artisan queue:work --sleep=3 ``` + +#### Maintenance Mode and Queues + +While your application is in [maintenance mode](/docs/{{version}}/configuration#maintenance-mode), no queued jobs will be handled. The jobs will continue to be handled as normal once the application is out of maintenance mode. + +To force your queue workers to process jobs even if maintenance mode is enabled, you may use `--force` option: + +```shell +php artisan queue:work --force +``` + #### Resource Considerations From c896b9b09d1cd96e5c93afcc967ed7b636250074 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 13 Feb 2024 09:22:47 -0600 Subject: [PATCH 1347/2609] recent updates --- blade.md | 8 ++++++++ collections.md | 22 ++++++++++++++++++++++ eloquent.md | 42 +++++++++++++++++++++++++++--------------- helpers.md | 24 +++++++++++++++++++++++- 4 files changed, 80 insertions(+), 16 deletions(-) diff --git a/blade.md b/blade.md index 056347029f4..44c5db32223 100644 --- a/blade.md +++ b/blade.md @@ -1168,6 +1168,14 @@ You may invoke a slot's `isEmpty` method to determine if the slot contains conte
    ``` +Additionally, the `hasActualContent` method may be used to determine if the slot contains any "actual" content that is not an HTML comment: + +```blade +@if ($slot->hasActualContent()) + The scope has non-comment content. +@endif +``` + #### Scoped Slots diff --git a/collections.md b/collections.md index 7ad60a58102..bc1a9782f17 100644 --- a/collections.md +++ b/collections.md @@ -184,6 +184,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [replaceRecursive](#method-replacerecursive) [reverse](#method-reverse) [search](#method-search) +[select](#method-select) [shift](#method-shift) [shuffle](#method-shuffle) [skip](#method-skip) @@ -2142,6 +2143,27 @@ Alternatively, you may provide your own closure to search for the first item tha // 2 + +#### `select()` {.collection-method} + +The `select` method selects the given keys from the collection, similar to an SQL `SELECT` statement: + +```php +$users = collect([ + ['name' => 'Taylor Otwell', 'role' => 'Developer', 'status' => 'active'], + ['name' => 'Victoria Faith', 'role' => 'Researcher', 'status' => 'active'], +]); + +$users->select(['name', 'role']); + +/* + [ + ['name' => 'Taylor Otwell', 'role' => 'Developer'], + ['name' => 'Victoria Faith', 'role' => 'Researcher'], + ], +*/ +``` + #### `shift()` {.collection-method} diff --git a/eloquent.md b/eloquent.md index 955e73e46c1..fea48c128a1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1210,7 +1210,22 @@ Writing a global scope is simple. First, use the `make:scope` command to generat #### Applying Global Scopes -To assign a global scope to a model, you should override the model's `booted` method and invoke the model's `addGlobalScope` method. The `addGlobalScope` method accepts an instance of your scope as its only argument: +To assign a global scope to a model, you may simply place the `ScopedBy` attribute on the model: + + [UserObserver::class], - ]; + public function boot(): void + { + User::observe(UserObserver::class); + } > [!NOTE] > There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. diff --git a/helpers.md b/helpers.md index d2ebb5f70e4..17d0862289b 100644 --- a/helpers.md +++ b/helpers.md @@ -69,6 +69,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::sortDesc](#method-array-sort-desc) [Arr::sortRecursive](#method-array-sort-recursive) [Arr::sortRecursiveDesc](#method-array-sort-recursive-desc) +[Arr::take](#method-array-take) [Arr::toCssClasses](#method-array-to-css-classes) [Arr::toCssStyles](#method-array-to-css-styles) [Arr::undot](#method-array-undot) @@ -842,10 +843,31 @@ If you would like the results sorted in descending order, you may use the `Arr:: $sorted = Arr::sortRecursiveDesc($array); + +#### `Arr::take()` {.collection-method} + +The `Arr::take` method returns a new array with the specified number of items: + + use Illuminate\Support\Arr; + + $array = [0, 1, 2, 3, 4, 5]; + + $chunk = Arr::take(3); + + // [0, 1, 2] + +You may also pass a negative integer to take the specified number of items from the end of the array: + + $array = [0, 1, 2, 3, 4, 5]; + + $chunk = Arr::take(-2); + + // [4, 5] + #### `Arr::toCssClasses()` {.collection-method} -The `Arr::toCssClasses` conditionally compiles a CSS class string. The method accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list: +The `Arr::toCssClasses` method conditionally compiles a CSS class string. The method accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list: use Illuminate\Support\Arr; From a38d8686919e2e0f063943dae83cab396363a5c0 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 13 Feb 2024 15:26:05 +0000 Subject: [PATCH 1348/2609] Updates L11 requirements for deployment (#9327) --- deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment.md b/deployment.md index cbbfb822744..6fb3b810812 100644 --- a/deployment.md +++ b/deployment.md @@ -25,7 +25,7 @@ The Laravel framework has a few system requirements. You should ensure that your
    -- PHP >= 8.1 +- PHP >= 8.2 - Ctype PHP Extension - cURL PHP Extension - DOM PHP Extension @@ -96,7 +96,7 @@ server { When deploying to production, make sure that you are optimizing Composer's class autoloader map so Composer can quickly find the proper file to load for a given class: ```shell -composer install --optimize-autoloader --no-dev +composer install --no-dev ``` > [!NOTE] From 3b96a5e3f4f63318422099175e45af5ee4a423a3 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 13 Feb 2024 18:05:21 +0100 Subject: [PATCH 1349/2609] Update Paddle token (#9329) --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index a903d939df5..bfc72536dd8 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -133,7 +133,7 @@ If you have billable entities that are not users, you may also add the trait to 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: ```ini -PADDLE_SELLER_ID=your-paddle-seller-id +PADDLE_CLIENT_SIDE_TOKEN=your-paddle-client-side-token PADDLE_API_KEY=your-paddle-api-key PADDLE_RETAIN_KEY=your-paddle-retain-key PADDLE_WEBHOOK_SECRET="your-paddle-webhook-secret" From eedba9693ce2420b39de657edac6570e9eed2823 Mon Sep 17 00:00:00 2001 From: Ahmed shamim Date: Wed, 14 Feb 2024 04:00:02 +0600 Subject: [PATCH 1350/2609] [10.x] HTTP client doc update for retry backoff option (#9328) * http client doc update for retry backoff option * formatting --------- Co-authored-by: Taylor Otwell --- http-client.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/http-client.md b/http-client.md index 43dfb9f004f..c172dc8a31c 100644 --- a/http-client.md +++ b/http-client.md @@ -231,6 +231,18 @@ If you would like the HTTP client to automatically retry the request if a client $response = Http::retry(3, 100)->post(/* ... */); +If you would like to manually calculate the number of milliseconds to sleep between attempts, you may pass a closure as the second argument to the `retry` method: + + use Exception; + + $response = Http::retry(3, function (int $attempt, Exception $exception) { + return $attempt * 100; + })->post(/* ... */); + +For convenience, you may also provide an array as the first argument to the `retry` method. This array will be used to determine how many milliseconds to sleep between subsequent attempts: + + $response = Http::retry([100, 200])->post(/* ... */); + If needed, you may pass a third argument to the `retry` method. The third argument should be a callable that determines if the retries should actually be attempted. For example, you may wish to only retry the request if the initial request encounters an `ConnectionException`: use Exception; From 539b0e5a8cb793ca64c6035dfec3054346368a4c Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:33:12 +0330 Subject: [PATCH 1351/2609] [10.x] Add `loadJsonTranslationsFrom` into packages (#9330) * add `loadJsonTranslationsFrom` into packages * Update packages.md --------- Co-authored-by: Taylor Otwell --- packages.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages.md b/packages.md index 5d6a5ca0ab0..1ebc1eab12c 100644 --- a/packages.md +++ b/packages.md @@ -174,6 +174,18 @@ Package translation lines are referenced using the `package::file.line` syntax c echo trans('courier::messages.welcome'); +You can register JSON translation files for your package using the `loadJsonTranslationsFrom` method. This method accepts the path to the directory that contains your package's JSON translation files: + +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->loadJsonTranslationsFrom(__DIR__.'/../lang'); +} +``` + #### Publishing Language Files From d125b394aec888fc4a48632683e4f20fd766fadc Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 13 Feb 2024 22:09:32 +0000 Subject: [PATCH 1352/2609] [11.x] Rewrites `Initial Configuration` on installation (#9325) * Rewrites `Initial Configuration` on installation * wording * formatting --------- Co-authored-by: Taylor Otwell --- installation.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/installation.md b/installation.md index 2f8fbe19710..da524332104 100644 --- a/installation.md +++ b/installation.md @@ -4,7 +4,6 @@ - [Why Laravel?](#why-laravel) - [Creating a Laravel Project](#creating-a-laravel-project) - [Initial Configuration](#initial-configuration) - - [Environment Based Configuration](#environment-based-configuration) - [Databases and Migrations](#databases-and-migrations) - [Directory Configuration](#directory-configuration) - [Docker Installation Using Sail](#docker-installation-using-sail) @@ -85,19 +84,14 @@ Once you have started the Artisan development server, your application will be a ## Initial Configuration -All of the configuration files for the Laravel framework are stored in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you. +Laravel requires no additional configuration out of the box. However, you may wish to review the `.env` file that exists at the root of your application to explore some of the configuration options available to you. -Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. +The `.env` file is where you may configure most of your application's settings, including your application's name and database connection information. - -### Environment Based Configuration - -Since many of Laravel's configuration option values may vary depending on whether your application is running on your local machine or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. - -Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. +Many of Laravel's configuration options may vary depending on whether your application is running on your local machine or on a production web server, so your `.env` file should not be committed to your application's source control since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would be exposed. > [!NOTE] -> For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). +> For more information about the `.env` file and the options that can be configured using environment variables, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). ### Databases and Migrations From 6fca9f4dc99f75599644a63026c3ee0653783f22 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 13 Feb 2024 22:20:03 +0000 Subject: [PATCH 1353/2609] [11.x] Rewrites `Introduction` on configuration (#9326) * Rewrites `Introduction` on configuration page * formatting * formatting --------- Co-authored-by: Taylor Otwell --- configuration.md | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/configuration.md b/configuration.md index b2760786433..46531d2d4f1 100644 --- a/configuration.md +++ b/configuration.md @@ -14,14 +14,27 @@ ## Introduction -All of the configuration files for the Laravel framework are stored in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you. +While Laravel requires no additional configuration out of the box, over time you may need to configure various aspects of the framework, such as using a different database connection. -These configuration files allow you to configure things like your database connection information, your mail server information, as well as various other core configuration values such as your application timezone and encryption key. + +#### Publishing the Configuration Files - -#### Application Overview +Most aspects of the framework's behavior can be configured via your application's [environment file](#environment-configuration); however, some applications, such as those utilizing multiple database connections, may need to publish Laravel's full configuration files. -In a hurry? You can get a quick overview of your application's configuration, drivers, and environment via the `about` Artisan command: +Laravel's configuration files are not published by default. To publish the configuration files to your application's `config` directory, you may use the `config:publish` Artisan command: + +```shell +php artisan config:publish + +php artisan config:publish --all +``` + +Each configuration file is thoroughly documented, so feel free to review all published configuration files in order to get familiar with the options available to your application. + + +#### The `about` Command + +Laravel can display an overview of your application's configuration, drivers, and environment via the `about` Artisan command. ```shell php artisan about @@ -46,11 +59,11 @@ It is often helpful to have different configuration values based on the environm To make this a cinch, Laravel utilizes the [DotEnv](https://github.com/vlucas/phpdotenv) PHP library. In a fresh Laravel installation, the root directory of your application will contain a `.env.example` file that defines many common environment variables. During the Laravel installation process, this file will automatically be copied to `.env`. -Laravel's default `.env` file contains some common configuration values that may differ based on whether your application is running locally or on a production web server. These values are then retrieved from various Laravel configuration files within the `config` directory using Laravel's `env` function. +Laravel's default `.env` file contains some common configuration values that may differ based on whether your application is running locally or on a production web server. These values are then read by Laravel's various configuration files within the `vendor/laravel/framework/config` and `config` directories using Laravel's `env` function. -If you are developing with a team, you may wish to continue including a `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. +If you are developing with a team, you may wish to continue including and updating the `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. -> [!NOTE] +> [!NOTE] > Any variable in your `.env` file can be overridden by external environment variables such as server-level or system-level environment variables. From 46f65c457315d1bb899d53eb58b7ff38984103fc Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 13 Feb 2024 22:23:27 +0000 Subject: [PATCH 1354/2609] [11.x] Rewrites `lifecycle` for L11 (#9331) * Adjusts lifecycle for L11 * Formatting --------- Co-authored-by: Taylor Otwell --- lifecycle.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lifecycle.md b/lifecycle.md index b532c0f2c3e..f565f64f405 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -29,31 +29,31 @@ The `index.php` file loads the Composer generated autoloader definition, and the ### HTTP / Console Kernels -Next, the incoming request is sent to either the HTTP kernel or the console kernel, depending on the type of request that is entering the application. These two kernels serve as the central location that all requests flow through. For now, let's just focus on the HTTP kernel, which is located in `app/Http/Kernel.php`. +Next, the incoming request is sent to either the HTTP kernel or the console kernel, using the `handleRequest` or `handleCommand` methods of the application instance, depending on the type of request entering the application. These two kernels serve as the central location through which all requests flow. For now, let's just focus on the HTTP kernel, which is an instance of `Illuminate\Foundation\Http\Kernel`. -The HTTP kernel extends the `Illuminate\Foundation\Http\Kernel` class, which defines an array of `bootstrappers` that will be run before the request is executed. These bootstrappers configure error handling, configure logging, [detect the application environment](/docs/{{version}}/configuration#environment-configuration), and perform other tasks that need to be done before the request is actually handled. Typically, these classes handle internal Laravel configuration that you do not need to worry about. +The HTTP kernel defines an array of `bootstrappers` that will be run before the request is executed. These bootstrappers configure error handling, configure logging, [detect the application environment](/docs/{{version}}/configuration#environment-configuration), and perform other tasks that need to be done before the request is actually handled. Typically, these classes handle internal Laravel configuration that you do not need to worry about. -The HTTP kernel also defines a list of HTTP [middleware](/docs/{{version}}/middleware) that all requests must pass through before being handled by the application. These middleware handle reading and writing the [HTTP session](/docs/{{version}}/session), determining if the application is in maintenance mode, [verifying the CSRF token](/docs/{{version}}/csrf), and more. We'll talk more about these soon. +The HTTP kernel is also responsible for passing the request though the application's middleware stack. These middleware handle reading and writing the [HTTP session](/docs/{{version}}/session), determining if the application is in maintenance mode, [verifying the CSRF token](/docs/{{version}}/csrf), and more. We'll talk more about these soon. The method signature for the HTTP kernel's `handle` method is quite simple: it receives a `Request` and returns a `Response`. Think of the kernel as being a big black box that represents your entire application. Feed it HTTP requests and it will return HTTP responses. ### Service Providers -One of the most important kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. All of the service providers for the application are configured in the `config/app.php` configuration file's `providers` array. +One of the most important kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. Laravel will iterate through this list of providers and instantiate each of them. After instantiating the providers, the `register` method will be called on all of the providers. Then, once all of the providers have been registered, the `boot` method will be called on each provider. This is so service providers may depend on every container binding being registered and available by the time their `boot` method is executed. Essentially every major feature offered by Laravel is bootstrapped and configured by a service provider. Since they bootstrap and configure so many features offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process. +While the framework internally uses dozens of service providers, you also have the option to create your own. You can find a list of the user-defined or third-party service providers that your application is using in the `bootstrap/providers.php` file. + ### Routing -One of the most important service providers in your application is the `App\Providers\RouteServiceProvider`. This service provider loads the route files contained within your application's `routes` directory. Go ahead, crack open the `RouteServiceProvider` code and take a look at how it works! - Once the application has been bootstrapped and all service providers have been registered, the `Request` will be handed off to the router for dispatching. The router will dispatch the request to a route or controller, as well as run any route specific middleware. -Middleware provide a convenient mechanism for filtering or examining HTTP requests entering your application. For example, Laravel includes a middleware that verifies if the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. Some middleware are assigned to all routes within the application, like those defined in the `$middleware` property of your HTTP kernel, while some are only assigned to specific routes or route groups. You can learn more about middleware by reading the complete [middleware documentation](/docs/{{version}}/middleware). +Middleware provide a convenient mechanism for filtering or examining HTTP requests entering your application. For example, Laravel includes a middleware that verifies if the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. Some middleware are assigned to all routes within the application, like `PreventRequestsDuringMaintenance`, while some are only assigned to specific routes or route groups. You can learn more about middleware by reading the complete [middleware documentation](/docs/{{version}}/middleware). If the request passes through all of the matched route's assigned middleware, the route or controller method will be executed and the response returned by the route or controller method will be sent back through the route's chain of middleware. @@ -62,13 +62,13 @@ If the request passes through all of the matched route's assigned middleware, th Once the route or controller method returns a response, the response will travel back outward through the route's middleware, giving the application a chance to modify or examine the outgoing response. -Finally, once the response travels back through the middleware, the HTTP kernel's `handle` method returns the response object and the `index.php` file calls the `send` method on the returned response. The `send` method sends the response content to the user's web browser. We've finished our journey through the entire Laravel request lifecycle! +Finally, once the response travels back through the middleware, the HTTP kernel's `handle` method returns the response object to the `handleRequest` of the application instance, and this method calls the `send` method on the returned response. The `send` method sends the response content to the user's web browser. We've now completed our journey through the entire Laravel request lifecycle! ## Focus on Service Providers Service providers are truly the key to bootstrapping a Laravel application. The application instance is created, the service providers are registered, and the request is handed to the bootstrapped application. It's really that simple! -Having a firm grasp of how a Laravel application is built and bootstrapped via service providers is very valuable. Your application's default service providers are stored in the `app/Providers` directory. +Having a firm grasp of how a Laravel application is built and bootstrapped via service providers is very valuable. Your application's used-defined service providers are stored in the `app/Providers` directory. By default, the `AppServiceProvider` is fairly empty. This provider is a great place to add your application's own bootstrapping and service container bindings. For large applications, you may wish to create several service providers, each with more granular bootstrapping for specific services used by your application. From 4a7fc2ecf47f830f2bc17a3c6266d0912ca586c4 Mon Sep 17 00:00:00 2001 From: Zep Fietje Date: Wed, 14 Feb 2024 18:36:48 +0100 Subject: [PATCH 1355/2609] Fix security vulnerability in Cashier Stripe quickstart example (#9334) --- billing.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/billing.md b/billing.md index b828e969d4f..ea9202f95f2 100644 --- a/billing.md +++ b/billing.md @@ -309,7 +309,17 @@ Next, let's build the Checkout success route. This is the route that users will Route::get('/checkout/success', function (Request $request) { $sessionId = $request->get('session_id'); - $orderId = Cashier::stripe()->checkout->sessions->retrieve($sessionId)['metadata']['order_id'] ?? null; + if ($sessionId === null) { + return; + } + + $session = Cashier::stripe()->checkout->sessions->retrieve($sessionId); + + if ($session->payment_status !== 'paid') { + return; + } + + $orderId = $session['metadata']['order_id'] ?? null; $order = Order::findOrFail($orderId); From 0243c75da429aba74561be27afcf308d69f59ef8 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Feb 2024 19:37:20 +0000 Subject: [PATCH 1356/2609] [11.x] Rewrites `providers` (#9332) * Rewrites `providers` for L11 * Adds `[tl! add]` * formatting --------- Co-authored-by: Taylor Otwell --- providers.md | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/providers.md b/providers.md index d44fb77393d..e17d9c15635 100644 --- a/providers.md +++ b/providers.md @@ -14,9 +14,9 @@ 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. 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. +Laravel uses dozens of service providers internally to bootstrap its core services, 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. +All user-defined service providers are registered in the `bootstrap/providers.php` file. In the following documentation, you will learn how to write your own service providers and register them with your Laravel application. > [!NOTE] > 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). @@ -145,15 +145,26 @@ You may type-hint dependencies for your service provider's `boot` method. The [s ## Registering Providers -All service providers are registered in the `config/app.php` configuration file. This file contains a `providers` array where you can list the class names of your service providers. By default, a set of Laravel core service providers are registered in this array. The default providers bootstrap the core Laravel components, such as the mailer, queue, cache, and others. +All service providers are registered in the `bootstrap/providers.php` configuration file. This file returns an array that contains the class names of your application's service providers: -To register your provider, add it to the array: + ServiceProvider::defaultProviders()->merge([ - // Other Service Providers + // This file is automatically generated by Laravel... - App\Providers\ComposerServiceProvider::class, - ])->toArray(), + return [ + App\Providers\AppServiceProvider::class, + App\Providers\ComposerServiceProvider::class, // [tl! add] + ]; ## Deferred Providers From ab45e8db22c27f4a711c06a6b398225bf13ad2b2 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Feb 2024 19:39:36 +0000 Subject: [PATCH 1357/2609] Rewrites `controllers` for L11 (#9337) --- controllers.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/controllers.md b/controllers.md index 28e87f27b8d..d358edf67ca 100644 --- a/controllers.md +++ b/controllers.md @@ -341,10 +341,10 @@ When using a custom keyed implicit binding as a nested route parameter, Laravel ### Localizing Resource URIs -By default, `Route::resource` will create resource URIs using English verbs and plural rules. If you need to localize the `create` and `edit` action verbs, you may use the `Route::resourceVerbs` method. This may be done at the beginning of the `boot` method within your application's `App\Providers\RouteServiceProvider`: +By default, `Route::resource` will create resource URIs using English verbs and plural rules. If you need to localize the `create` and `edit` action verbs, you may use the `Route::resourceVerbs` method. This may be done at the beginning of the `boot` method within your application's `App\Providers\AppServiceProvider`: /** - * Define your route model bindings, pattern filters, etc. + * Bootstrap any application services. */ public function boot(): void { @@ -352,8 +352,6 @@ By default, `Route::resource` will create resource URIs using English verbs and 'create' => 'crear', 'edit' => 'editar', ]); - - // ... } Laravel's pluralizer supports [several different languages which you may configure based on your needs](/docs/{{version}}/localization#pluralization-language). Once the verbs and pluralization language have been customized, a resource route registration such as `Route::resource('publicacion', PublicacionController::class)` will produce the following URIs: From 665d970276a37eb1e7b3121168cc382c27db5ed9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Feb 2024 19:41:09 +0000 Subject: [PATCH 1358/2609] [11.x] Rewrites `csrf` for L11 (#9336) * Rewrites `csrf` for L11 * Update csrf.md --------- Co-authored-by: Taylor Otwell --- csrf.md | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/csrf.md b/csrf.md index 19aa3f04788..e87608501e2 100644 --- a/csrf.md +++ b/csrf.md @@ -72,27 +72,15 @@ If you are building a SPA that is utilizing Laravel as an API backend, you shoul Sometimes you may wish to exclude a set of URIs from CSRF protection. For example, if you are using [Stripe](https://stripe.com) to process payments and are utilizing their webhook system, you will need to exclude your Stripe webhook handler route from CSRF protection since Stripe will not know what CSRF token to send to your routes. -Typically, you should place these kinds of routes outside of the `web` middleware group that the `App\Providers\RouteServiceProvider` applies to all routes in the `routes/web.php` file. However, you may also exclude the routes by adding their URIs to the `$except` property of the `VerifyCsrfToken` middleware: +Typically, you should place these kinds of routes outside of the `web` middleware group that Laravel applies to all routes in the `routes/web.php` file. However, you may also exclude specific routes by providing their URIs to the `validateCsrfTokens` method in your application's `bootstrap/app.php` file: - withMiddleware(function (Middleware $middleware) { + $middleware->validateCsrfTokens(except: [ 'stripe/*', 'http://example.com/foo/bar', 'http://example.com/foo/*', - ]; - } + ]); + }) > [!NOTE] > For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). From 7811ca8c336b0e605e7c354b696c932ce5f18713 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Feb 2024 19:53:02 +0000 Subject: [PATCH 1359/2609] [11.x] Rewrites `routing` for L11 (#9333) * Rewrites `routing` for L11 * Adjusts prefix example * customize cors * Uses `throttleWithRedis` * Updates service provider comment * formatting * Update routing.md * Update routing.md * Update routing.md --------- Co-authored-by: Taylor Otwell --- routing.md | 70 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/routing.md b/routing.md index 56eafe552ce..d0c3bfa72f3 100644 --- a/routing.md +++ b/routing.md @@ -42,7 +42,7 @@ The most basic Laravel routes accept a URI and a closure, providing a very simpl #### 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 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. +All Laravel routes are defined in your route files, which are located in the `routes` directory. These files are automatically loaded by Laravel using the configuration specified in your application's `bootstrap/app.php` file. 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. 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: @@ -50,7 +50,28 @@ For most applications, you will begin by defining routes in your `routes/web.php Route::get('/user', [UserController::class, 'index']); -Routes defined in the `routes/api.php` file are nested within a route group by the `RouteServiceProvider`. Within this group, the `/api` URI prefix is automatically applied so you do not need to manually apply it to every route in the file. You may modify the prefix and other route group options by modifying your `RouteServiceProvider` class. + +#### API Routes + +If your application will also offer a stateless API, you may enable API routing using the `install:api` Artisan command: + +```shell +php artisan install:api +``` + +The `install:api` command installs [Laravel Sanctum](/docs/{{version}}/sanctum), which provides a robust, yet simple API token authentication guard which can be used to authenticate third-party API consumers, SPAs, or mobile applications. In addition, the `install:api` command creates the `routes/api.php` file: + + Route::get('/user', function (Request $request) { + return $request->user(); + })->middleware(Authenticate::using('sanctum')); + +The routes in `routes/api.php` are stateless and are assigned to the `api` middleware group. Additionally, the `/api` URI prefix is automatically applied to these routes, so you do not need to manually apply it to every route in the file. You may change the prefix by modifying your application's `bootstrap/app.php` file: + + ->withRouting( + api: __DIR__.'/../routes/api.php', + apiPrefix: 'api/admin', + // ... + ) #### Available Router Methods @@ -252,10 +273,12 @@ If the incoming request does not match the route pattern constraints, a 404 HTTP #### Global Constraints -If you would like a route parameter to always be constrained by a given regular expression, you may use the `pattern` method. You should define these patterns in the `boot` method of your `App\Providers\RouteServiceProvider` class: +If you would like a route parameter to always be constrained by a given regular expression, you may use the `pattern` method. You should define these patterns in the `boot` method of your application's `App\Providers\AppServiceProvider` class: + + use Illuminate\Support\Facades\Route; /** - * Define your route model bindings, pattern filters, etc. + * Bootstrap any application services. */ public function boot(): void { @@ -574,19 +597,17 @@ Route::get('/categories/{category}', function (Category $category) { ### Explicit Binding -You are not required to use Laravel's implicit, convention based model resolution in order to use model binding. You can also explicitly define how route parameters correspond to models. To register an explicit binding, use the router's `model` method to specify the class for a given parameter. You should define your explicit model bindings at the beginning of the `boot` method of your `RouteServiceProvider` class: +You are not required to use Laravel's implicit, convention based model resolution in order to use model binding. You can also explicitly define how route parameters correspond to models. To register an explicit binding, use the router's `model` method to specify the class for a given parameter. You should define your explicit model bindings at the beginning of the `boot` method of your `AppServiceProvider` class: use App\Models\User; use Illuminate\Support\Facades\Route; /** - * Define your route model bindings, pattern filters, etc. + * Bootstrap any application services. */ public function boot(): void { Route::model('user', User::class); - - // ... } Next, define a route that contains a `{user}` parameter: @@ -604,21 +625,19 @@ If a matching model instance is not found in the database, a 404 HTTP response w #### Customizing the Resolution Logic -If you wish to define your own model binding resolution logic, you may use the `Route::bind` method. The closure you pass to the `bind` method will receive the value of the URI segment and should return the instance of the class that should be injected into the route. Again, this customization should take place in the `boot` method of your application's `RouteServiceProvider`: +If you wish to define your own model binding resolution logic, you may use the `Route::bind` method. The closure you pass to the `bind` method will receive the value of the URI segment and should return the instance of the class that should be injected into the route. Again, this customization should take place in the `boot` method of your application's `AppServiceProvider`: use App\Models\User; use Illuminate\Support\Facades\Route; /** - * Define your route model bindings, pattern filters, etc. + * Bootstrap any application services. */ public function boot(): void { Route::bind('user', function (string $value) { return User::where('name', $value)->firstOrFail(); }); - - // ... } Alternatively, you may override the `resolveRouteBinding` method on your Eloquent model. This method will receive the value of the URI segment and should return the instance of the class that should be injected into the route: @@ -670,7 +689,7 @@ Using the `Route::fallback` method, you may define a route that will be executed Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. -Typically, rate limiters are defined within the `boot` method of your application's `App\Providers\RouteServiceProvider` class. In fact, this class already includes a rate limiter definition that is applied to the routes in your application's `routes/api.php` file: +Rate limiters may be defined within the `boot` method of your application's `App\Providers\AppServiceProvider` class: ```php use Illuminate\Cache\RateLimiting\Limit; @@ -678,15 +697,13 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; /** - * Define your route model bindings, pattern filters, and other route configuration. + * Bootstrap any application services. */ protected function boot(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); - - // ... } ``` @@ -697,15 +714,13 @@ Rate limiters are defined using the `RateLimiter` facade's `for` method. The `fo use Illuminate\Support\Facades\RateLimiter; /** - * Define your route model bindings, pattern filters, and other route configuration. + * Bootstrap any application services. */ protected function boot(): void { RateLimiter::for('global', function (Request $request) { return Limit::perMinute(1000); }); - - // ... } If the incoming request exceeds the specified rate limit, a response with a 429 HTTP status code will automatically be returned by Laravel. If you would like to define your own response that should be returned by a rate limit, you may use the `response` method: @@ -773,9 +788,12 @@ Rate limiters may be attached to routes or route groups using the `throttle` [mi #### Throttling With Redis -Typically, the `throttle` middleware is mapped to the `Illuminate\Routing\Middleware\ThrottleRequests` class. This mapping is defined in your application's HTTP kernel (`App\Http\Kernel`). However, if you are using Redis as your application's cache driver, you may wish to change this mapping to use the `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` class. This class is more efficient at managing rate limiting using Redis: +By default, the `throttle` middleware is mapped to the `Illuminate\Routing\Middleware\ThrottleRequests` class. However, if you are using Redis as your application's cache driver, you may wish to instruct Laravel to use Redis to manage rate limiting. To do so, you should use the `throttleWithRedis` method in your application's `bootstrap/app.php` file. This method maps the `throttle` middleware to the `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` middleware class: - 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, + ->withMiddleware(function (Middleware $middleware) { + $middleware->throttleWithRedis(); + // ... + }) ## Form Method Spoofing @@ -810,7 +828,15 @@ You may refer to the API documentation for both the [underlying class of the Rou ## Cross-Origin Resource Sharing (CORS) -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`). +Laravel can automatically respond to CORS `OPTIONS` HTTP requests with values that you configure. The `OPTIONS` requests will automatically be handled by the `HandleCors` [middleware](/docs/{{version}}/middleware) that is automatically included in your application's global middleware stack. + +Sometimes, you may need to customize the CORS configuration values for your application. You may do so by publishing the CORS configuration file using the `config:publish` Artisan command: + +```shell +php artisan config:publish cors +``` + +This command will place a `cors.php` configuration file within your application's `config` directory. > [!NOTE] > 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). From ecd29e7fdb7024a0f20adcb43dc93095efce51bc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 14 Feb 2024 14:10:15 -0600 Subject: [PATCH 1360/2609] formatting --- middleware.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware.md b/middleware.md index 20f99bd7665..c68c98d0965 100644 --- a/middleware.md +++ b/middleware.md @@ -15,7 +15,7 @@ Middleware provide a convenient mechanism for inspecting and filtering HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to your application's login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. -Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application. There are several middleware included in the Laravel framework, including middleware for authentication and CSRF protection. All of these middleware are located in the `app/Http/Middleware` directory. +Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application. There are several middleware included in Laravel, including middleware for authentication and CSRF protection. All of these middleware are located in the `app/Http/Middleware` directory. ## Defining Middleware From f4e7191a4c5b3b112411ddfc5e52410f47124d4e Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Feb 2024 20:11:03 +0000 Subject: [PATCH 1361/2609] [11.x] Rewrites `middleware` for L11 (#9335) * Rewrites `middleware` for L11 * formatting * formatting --------- Co-authored-by: Taylor Otwell --- middleware.md | 144 ++++++++++++++++++++++++++++---------------------- 1 file changed, 81 insertions(+), 63 deletions(-) diff --git a/middleware.md b/middleware.md index c68c98d0965..2522a062728 100644 --- a/middleware.md +++ b/middleware.md @@ -15,7 +15,7 @@ Middleware provide a convenient mechanism for inspecting and filtering HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to your application's login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. -Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application. There are several middleware included in Laravel, including middleware for authentication and CSRF protection. All of these middleware are located in the `app/Http/Middleware` directory. +Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application. A variety of middleware are included in Laravel, including middleware for authentication and CSRF protection; however, all user-defined middleware are typically located in your application's `app/Http/Middleware` directory. ## Defining Middleware @@ -112,7 +112,15 @@ However, this middleware would perform its task **after** the request is handled ### Global Middleware -If you want a middleware to run during every HTTP request to your application, list the middleware class in the `$middleware` property of your `app/Http/Kernel.php` class. +If you want a middleware to run during every HTTP request to your application, you may append it to the global middleware stack in your application's `bootstrap/app.php` file: + + use App\Http\Middleware\EnsureTokenIsValid; + + ->withMiddleware(function (Middleware $middleware) { + $middleware->append(EnsureTokenIsValid::class); + }) + +The `$middleware` object provided to the `withMiddleware` closure is an instance of `Illuminate\Foundation\Configuration\Middleware` and is responsible for managing the middleware assigned to your application's routes. The `append` method adds the middleware to the end of the list of global middleware. If you would like to add a middleware to the beginning of the list, you should use the `prepend` method. ### Assigning Middleware to Routes @@ -131,27 +139,21 @@ You may assign multiple middleware to the route by passing an array of middlewar // ... })->middleware([First::class, Second::class]); -For convenience, you may assign aliases to middleware in your application's `app/Http/Kernel.php` file. By default, the `$middlewareAliases` property of this class contains entries for the middleware included with Laravel. You may add your own middleware to this list and assign it an alias of your choosing: +For convenience, you may assign aliases to middleware in your application's `bootstrap/app.php` file. This allows you to define a short alias for the middleware, which can be especially useful for middleware with long class names: - // Within App\Http\Kernel class... + use App\Http\Middleware\EnsureUserIsSubscribed; - protected $middlewareAliases = [ - 'auth' => \App\Http\Middleware\Authenticate::class, - 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, - 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, - 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, - 'can' => \Illuminate\Auth\Middleware\Authorize::class, - 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, - 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, - 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, - 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - ]; + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'subscribed' => EnsureUserIsSubscribed::class + ]); + }) -Once the middleware alias has been defined in the HTTP kernel, you may use the alias when assigning middleware to routes: +Once the middleware alias has been defined in your application's `bootstrap/app.php` file, you may use the alias when assigning middleware to routes: Route::get('/profile', function () { // ... - })->middleware('auth'); + })->middleware('subscribed'); #### Excluding Middleware @@ -185,68 +187,84 @@ The `withoutMiddleware` method can only remove route middleware and does not app ### Middleware Groups -Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish this using the `$middlewareGroups` property of your HTTP kernel. +Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish this using the `appendToGroup` method within your application's `bootstrap/app.php` file: -Laravel includes predefined `web` and `api` middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, these middleware groups are automatically applied by your application's `App\Providers\RouteServiceProvider` service provider to routes within your corresponding `web` and `api` route files: + use App\Http\Middleware\First; + use App\Http\Middleware\Second; - /** - * The application's route middleware groups. - * - * @var array - */ - protected $middlewareGroups = [ - 'web' => [ - \App\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \App\Http\Middleware\VerifyCsrfToken::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], + ->withMiddleware(function (Middleware $middleware) { + $middleware->appendToGroup('group-name', [ + First::class, + Second::class, + ]); + }) - 'api' => [ - \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - ]; +You may prepend middleware to a group using the `prependToGroup` method. + +Laravel includes predefined `web` and `api` middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, Laravel automatically applies these middleware groups to the corresponding `routes/web.php` and `routes/api.php` files: + +| The `web` Middleware Group +|-------------- +| `Illuminate\Cookie\Middleware\EncryptCookies` +| `Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse` +| `Illuminate\Session\Middleware\StartSession` +| `Illuminate\View\Middleware\ShareErrorsFromSession` +| `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` +| `Illuminate\Routing\Middleware\SubstituteBindings` +| `Illuminate\Session\Middleware\AuthenticateSession` -Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware. Again, middleware groups make it more convenient to assign many middleware to a route at once: +| The `api` Middleware Group +|-------------- +| `Illuminate\Routing\Middleware\ThrottleRequests:api` +| `Illuminate\Routing\Middleware\SubstituteBindings` + +If you would like to append or prepend middleware to these groups, you may use the `web` and `api` methods within your application's `bootstrap/app.php` file. The `web` and `api` methods are convenient alternatives to the `appendToGroup` method: + + use App\Http\Middleware\EnsureTokenIsValid; + use App\Http\Middleware\EnsureUserIsSubscribed; + + ->withMiddleware(function (Middleware $middleware) { + $middleware->web(append: [ + EnsureUserIsSubscribed::class, + ]); + + $middleware->api(append: [ + EnsureTokenIsValid::class, + ]); + }) + +Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware: Route::get('/', function () { // ... - })->middleware('web'); + })->middleware('group-name'); - Route::middleware(['web'])->group(function () { + Route::middleware(['group-name'])->group(function () { // ... }); > [!NOTE] -> Out of the box, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `App\Providers\RouteServiceProvider`. +> By default, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `bootstrap/app.php` file. ### Sorting Middleware -Rarely, you may need your middleware to execute in a specific order but not have control over their order when they are assigned to the route. In this case, you may specify your middleware priority using the `$middlewarePriority` property of your `app/Http/Kernel.php` file. This property may not exist in your HTTP kernel by default. If it does not exist, you may copy its default definition below: +Rarely, you may need your middleware to execute in a specific order but not have control over their order when they are assigned to the route. In these situations, you may specify your middleware priority using the `priority` method in your application's `bootstrap/app.php` file: - /** - * The priority-sorted list of middleware. - * - * This forces non-global middleware to always be in the given order. - * - * @var string[] - */ - protected $middlewarePriority = [ - \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, - \Illuminate\Cookie\Middleware\EncryptCookies::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, - \Illuminate\Routing\Middleware\ThrottleRequests::class, - \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, - \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - \Illuminate\Auth\Middleware\Authorize::class, - ]; + ->withMiddleware(function (Middleware $middleware) { + $middleware->priority([ + \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, + \Illuminate\Cookie\Middleware\EncryptCookies::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, + \Illuminate\Routing\Middleware\ThrottleRequests::class, + \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, + \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Illuminate\Auth\Middleware\Authorize::class, + ]); + }) ## Middleware Parameters @@ -327,7 +345,7 @@ Sometimes a middleware may need to do some work after the HTTP response has been } } -The `terminate` method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of routes or global middleware in the `app/Http/Kernel.php` file. +The `terminate` method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of routes or global middleware in your application's `bootstrap/app.php` file. When calling the `terminate` method on your middleware, Laravel will resolve a fresh instance of the middleware from the [service container](/docs/{{version}}/container). If you would like to use the same middleware instance when the `handle` and `terminate` methods are called, register the middleware with the container using the container's `singleton` method. Typically this should be done in the `register` method of your `AppServiceProvider`: From 3b39c1b5d1491d1a41fbfeb9bee51dcbe802ccf5 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Fri, 16 Feb 2024 00:39:50 +0900 Subject: [PATCH 1362/2609] [11.x] Fix typo (#9339) --- lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifecycle.md b/lifecycle.md index f565f64f405..e25ef3da79e 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -69,6 +69,6 @@ Finally, once the response travels back through the middleware, the HTTP kernel' Service providers are truly the key to bootstrapping a Laravel application. The application instance is created, the service providers are registered, and the request is handed to the bootstrapped application. It's really that simple! -Having a firm grasp of how a Laravel application is built and bootstrapped via service providers is very valuable. Your application's used-defined service providers are stored in the `app/Providers` directory. +Having a firm grasp of how a Laravel application is built and bootstrapped via service providers is very valuable. Your application's user-defined service providers are stored in the `app/Providers` directory. By default, the `AppServiceProvider` is fairly empty. This provider is a great place to add your application's own bootstrapping and service container bindings. For large applications, you may wish to create several service providers, each with more granular bootstrapping for specific services used by your application. From 9c6285831e3c6f08ddc96474dbbfe733626b2eef Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 15 Feb 2024 15:43:59 +0000 Subject: [PATCH 1363/2609] Removes references to console kernel (#9340) --- structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure.md b/structure.md index 9708675af64..e219866ab89 100644 --- a/structure.md +++ b/structure.md @@ -120,7 +120,7 @@ The `Broadcasting` directory contains all of the broadcast channel classes for y #### The Console Directory -The `Console` directory contains all of the custom Artisan commands for your application. These commands may be generated using the `make:command` command. This directory also houses your console kernel, which is where your custom Artisan commands are registered and your [scheduled tasks](/docs/{{version}}/scheduling) are defined. +The `Console` directory contains all of the custom Artisan commands for your application. These commands may be generated using the `make:command` command. #### The Events Directory From 7bc9d62c85d5195d61e62e2d1ae403afa511c80d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 16 Feb 2024 16:21:27 +0000 Subject: [PATCH 1364/2609] [11.x] Rewrites `views` (#9346) * Rewrites `views` for L11 * Update views.md --------- Co-authored-by: Taylor Otwell --- views.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/views.md b/views.md index a6ffa5f9b2e..6f9d3fc2ae2 100644 --- a/views.md +++ b/views.md @@ -150,7 +150,7 @@ Occasionally, you may need to share data with all views that are rendered by you 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. +Typically, view composers will be registered within one of your application's [service providers](/docs/{{version}}/providers). In this example, we'll assume that the `App\Providers\AppServiceProvider` will house this logic. 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/View/Composers` directory to house all of your application's view composers: @@ -163,7 +163,7 @@ We'll use the `View` facade's `composer` method to register the view composer. L use Illuminate\Support\ServiceProvider; use Illuminate\View\View; - class ViewServiceProvider extends ServiceProvider + class AppServiceProvider extends ServiceProvider { /** * Register any application services. @@ -192,9 +192,6 @@ We'll use the `View` facade's `composer` method to register the view composer. L } } -> [!WARNING] -> 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 `compose` method of the `App\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: Date: Fri, 16 Feb 2024 16:22:22 +0000 Subject: [PATCH 1365/2609] [11.x] Rewrites `responses` (#9344) * Rewrites `responses` for L11 * Update responses.md --------- Co-authored-by: Taylor Otwell --- responses.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/responses.md b/responses.md index a6009034ccc..30435afc62e 100644 --- a/responses.md +++ b/responses.md @@ -138,16 +138,13 @@ If you do not yet have an instance of the outgoing response, you may use the `Co ### Cookies and 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: +By default, thanks to the `Illuminate\Cookie\Middleware\EncryptCookies` middleware, 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 `encryptCookies` method in your application's `bootstrap/app.php` file: - /** - * The names of the cookies that should not be encrypted. - * - * @var array - */ - protected $except = [ - 'cookie_name', - ]; + ->withMiddleware(function (Middleware $middleware) { + $middleware->encryptCookies(except: [ + 'cookie_name', + ]); + }) ## Redirects From 43fc2028a539845fa2921e4cedcba8d57d46e69b Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 16 Feb 2024 17:37:17 +0100 Subject: [PATCH 1366/2609] Fix Laravel Sail Shell Alias not passing arguments (#9338) * [Fix] Adjust Laravel Sail shell alias missing arguments * Update sail.md --------- Co-authored-by: Patrick Henninger Co-authored-by: Taylor Otwell --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index a4adf18f645..404987d4754 100644 --- a/sail.md +++ b/sail.md @@ -96,7 +96,7 @@ By default, Sail commands are invoked using the `vendor/bin/sail` script that is However, instead of repeatedly typing `vendor/bin/sail` to execute Sail commands, you may wish to configure a shell alias that allows you to execute Sail's commands more easily: ```shell -alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail' +alias sail='sh $([ -f sail ] && echo sail || echo vendor/bin/sail)' ``` To make sure this is always available, you may add this to your shell configuration file in your home directory, such as `~/.zshrc` or `~/.bashrc`, and then restart your shell. From 6d4575f9d8dfcf52b88ec094765e44e656c3320b Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 16 Feb 2024 21:52:28 +0000 Subject: [PATCH 1367/2609] [11.x] Rewrites `sessions` for L11 (#9348) * Rewrites `sessions` * formatting --------- Co-authored-by: Taylor Otwell --- session.md | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/session.md b/session.md index 8545e13212c..50018ba57d7 100644 --- a/session.md +++ b/session.md @@ -24,9 +24,15 @@ Laravel ships with a variety of session backends that are accessed through an ex ### Configuration -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. +By default, Laravel is configured to use the `database` session driver, which stores user session information in your application's database and will work well for many production applications. -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: +Laravel's complete `session.php` configuration file is not published by default, as you can specify your application's session driver using the `SESSION_DRIVER` environment variable. However, if necessary, you may publish the configuration file using the `config:publish` Artisan command: + +```shell +php artisan config:publish session +``` + +Laravel includes a variety of great session drivers:
    @@ -48,21 +54,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 records. An example `Schema` declaration for the table may be found below: - - use Illuminate\Database\Schema\Blueprint; - use Illuminate\Support\Facades\Schema; - - Schema::create('sessions', function (Blueprint $table) { - $table->string('id')->primary(); - $table->foreignId('user_id')->nullable()->index(); - $table->string('ip_address', 45)->nullable(); - $table->text('user_agent')->nullable(); - $table->text('payload'); - $table->integer('last_activity')->index(); - }); - -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): +When using the `database` session driver, you will need to ensure that you have a database table to contain the session data. Typically, this is included in Laravel's default `0001_01_01_000000_create_users_table.php` [database migration](/docs/{{version}}/migrations); however, if for any reason you do not have a `sessions` table, you may use the `session:table` Artisan command to generate this migration: ```shell php artisan session:table @@ -76,7 +68,7 @@ php artisan migrate 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). > [!NOTE] -> In the `session` configuration file, the `connection` option may be used to specify which Redis connection is used by the session. +> The `SESSION_CONNECTION` environment variable, or the `connection` option in the `session.php` configuration file, may be used to specify which Redis connection is used for session storage. ## Interacting With the Session @@ -357,4 +349,4 @@ Once your driver has been implemented, you are ready to register it with Laravel } } -Once the session driver has been registered, you may use the `mongo` driver in your `config/session.php` configuration file. +Once the session driver has been registered, you may specify the `mongo` driver as your application's session driver using the `SESSION_DRIVER` environment variable or within the application's `config/session.php` configuration file. From cd504d98e8f7f7ed165e0b0f88df266c04818054 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Fri, 16 Feb 2024 22:30:14 +0000 Subject: [PATCH 1368/2609] [11.x] Documents Reverb (#9341) * wip * add to nav * wip * wip * wip * wip * document restarts * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * formatting --------- Co-authored-by: Taylor Otwell --- broadcasting.md | 51 ++++++++- documentation.md | 1 + reverb.md | 271 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 reverb.md diff --git a/broadcasting.md b/broadcasting.md index 68de72b5f6a..235ac802dc7 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -3,10 +3,12 @@ - [Introduction](#introduction) - [Server Side Installation](#server-side-installation) - [Configuration](#configuration) + - [Reverb](#reverb) - [Pusher Channels](#pusher-channels) - [Ably](#ably) - [Open Source Alternatives](#open-source-alternatives) - [Client Side Installation](#client-side-installation) + - [Reverb](#client-reverb) - [Pusher Channels](#client-pusher-channels) - [Ably](#client-ably) - [Concept Overview](#concept-overview) @@ -52,7 +54,7 @@ The core concepts behind broadcasting are simple: clients connect to named chann #### 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.com). However, community driven packages such as [soketi](https://docs.soketi.app/) provide additional broadcasting drivers that do not require commercial broadcasting providers. +By default, Laravel includes three server-side broadcasting drivers for you to choose from: [Laravel Reverb](https://reverb.laravel.com), [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.com). You may also be interested in community driven packages such as [soketi](https://docs.soketi.app/). > [!NOTE] > Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). @@ -67,7 +69,7 @@ Event broadcasting is accomplished by a server-side broadcasting driver that bro ### 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 during testing. 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: [Laravel Reverb](/docs/{{version}}/reverb), [Pusher Channels](https://pusher.com/channels), [Ably](https://ably.com), [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 @@ -79,6 +81,23 @@ Before broadcasting any events, you will first need to register the `App\Provide 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. + +### Reverb + +You may use the Composer package manager to install Laravel Reverb into your Laravel project. Since Reverb is currently in beta, you will need to explicitly install the beta release: + +```sh +composer require laravel/reverb:@beta +``` + +Once the package is installed, you may run Reverb's installation command to publish the configuration, add Reverb's required environment variables, and enable event broadcasting in your application: + +```sh +php artisan reverb:install +``` + +You can find detailed Reverb installation and usage instructions in the [Reverb documentation](/docs/{{version}}/reverb). + ### Pusher Channels @@ -149,6 +168,34 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst ## Client Side Installation + +### Reverb + +[Laravel Echo](https://github.com/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 the Reverb broadcaster will leverage the Pusher SDK: + +```shell +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: + +```js +import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; + +window.Pusher = Pusher; + +window.Echo = new Echo({ + broadcaster: 'reverb', + key: import.meta.env.VITE_REVERB_APP_KEY, + wsHost: import.meta.env.VITE_REVERB_HOST, + wsPort: import.meta.env.VITE_REVERB_PORT, + wssPort: import.meta.env.VITE_REVERB_PORT, + forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', + enabledTransports: ['ws', 'wss'], +}); +``` + ### Pusher Channels diff --git a/documentation.md b/documentation.md index 04b970aaddc..2447a08eab7 100644 --- a/documentation.md +++ b/documentation.md @@ -96,6 +96,7 @@ - [Precognition](/docs/{{version}}/precognition) - [Prompts](/docs/{{version}}/prompts) - [Pulse](/docs/{{version}}/pulse) + - [Reverb](/docs/{{version}}/reverb) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) - [Scout](/docs/{{version}}/scout) diff --git a/reverb.md b/reverb.md new file mode 100644 index 00000000000..2ec66259c35 --- /dev/null +++ b/reverb.md @@ -0,0 +1,271 @@ +# Laravel Reverb + +- [Introduction](#introduction) +- [Installation](#installation) +- [Configuration](#configuration) + - [Application Credentials](#application-credentials) + - [Allowed Origins](#allowed-origins) + - [Additional Applications](#additional-applications) + - [SSL](#ssl) +- [Running the Server](#running-server) + - [Debugging](#debugging) + - [Restarting](#restarting) +- [Running Reverb in Production](#production) + - [Open Files](#open-files) + - [Web Server](#web-server) + - [Ports](#ports) + - [Process Management](#process-management) + - [Scaling](#scaling) + + +## Introduction + +[Laravel Reverb](https://github.com/laravel/reverb) brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, and provides seamless integration with Laravel’s existing suite of event broadcasting tools. + + +## Installation + +> **Warning** +> Laravel Reveb requires PHP 8.2+. + +You may use the Composer package manager to install Reverb into your Laravel project. Since Reverb is currently in beta, you will need to explicitly install the beta release: + +```sh +composer require laravel/reverb:@beta +``` + +Once the package is installed, you may run Reverb's installation command to publish the configuration, add Reverb's required environment variables, and enable event broadcasting in your application: + +```sh +php artisan reverb:install +``` + + +## Configuration + +The `reverb:install` command will automatically start Reverb using a sensible set of default configuration options. If you would like to make any configuration changes, you may do so by updating Reverb's environment variables or by updating the `config/reverb.php` configuration file. + + +### Application Credentials + +In order establish a connection to Reverb, a set of Reverb "application" credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You may define these credentials using the following environment variables: + +```ini +REVERB_APP_ID=my-app-id +REVERB_APP_KEY=my-app-key +REVERB_APP_SECRET=my-app-secret +``` + + +### Allowed Origins + +You may also define the origins from which client requests may originate by updating the value of the `allowed_origins` configuration value within the `apps` section of the `config/reverb.php` configuration file. Any requests from an origin not listed in your allowed origins will be rejected. You may allow all origins using `*`: + +```php +'apps' => [ + [ + 'id' => 'my-app-id', + 'allowed_origins' => ['laravel.com'], + // ... + ] +] +``` + + +### Additional Applications + +Typically, Reverb provides a WebSocket server for the application in which it is installed. However, it is possible to serve more than one application using a single Reverb installation. + +For example, you may wish to maintain a single Laravel application which, via Reverb, provides WebSocket connectivity for multiple applications. This can be achieved by defining multiple `apps` in your application's `config/reverb.php` configuration file: + +```php +'apps' => [ + [ + 'id' => 'my-app-one', + // ... + ], + [ + 'id' => 'my-app-two', + // ... + ], +], +``` + + +### SSL + +In most cases, secure WebSocket connections are likely to be handled by the upstream web server (Nginx, etc.) before the request is proxied to your Reverb server. + +However, it can sometimes be useful, such as in local development, for the Reverb server to handle secure connections directly. You may achieve this by defining `tls` options in your application's `config/reverb.php` configuration file. Within the array of `tls` options, you may provide any of the options supported by [PHP's SSL context options](https://www.php.net/manual/en/context.ssl.php): + +```php +'options' => [ + 'tls' => [ + 'local_cert' => '/path/to/cert.pem' + ], +], +``` + + +## Running the Server + +The Reverb server can be started using the `reverb:start` Artisan command: + +```sh +php artisan reverb:start +``` + +By default, the Reverb server will be started at `0.0.0.0:8080`, making it accessible from all network interfaces. + +Ify you need to specify a custom host or port, you may do so via the `--host` and `--port` options when starting the server: + +```sh +php artisan reverb:start --host=127.0.0.1 --port=9000 +``` + +Alternatively, you may define `REVERB_HOST` and `REVERB_PORT` environment variables in your application's `.env` configuration file. + + +### Debugging + +To improve performance, Reverb does not output any debug information by default. If you would like to see the stream of data passing through your Reverb server, you may provide the `--debug` option to the `reverb:start` command: + +```sh +php artisan reverb:start --debug +``` + + +### Restarting + +Since Reverb is a long-running process, changes to your code will not be reflected without restarting the server via the `reverb:restart` Artisan command. + +The `reverb:restart` command ensures all connections are gracefully terminated before stopping the server. If you are running Reverb with a process manager such as Supervisor, the server will be automatically restarted by the process manager after all connections have been terminated: + +```sh +php artisan reverb:restart +``` + + +## Running Reverb in Production + +Due to the long-running nature of WebSocket servers, you may need to make some optimizations to your server / hosting environment to ensure your Reverb server can effectively handle the optimal number of connections for the resources available on your server. + +> **Note** +> If your site is managed by [Laravel Forge](https://forge.laravel.com), you may automatically optimize your server for Reverb directly from the "Application" panel. + + +### Open Files + +Each WebSocket connection is held in memory until either the client or server disconnects. In Unix and Unix-like environments, each connection is represented by a file. However, there are often limits on the number of allowed open files at both the operating system and application level. + + +#### Operating System + +On a Unix based operating system, you make determine the allowed number of open files using the `ulimit` command: + +```sh +ulimit -n +``` + +This command will display the open file limits allowed for different users. You may update these values by editing the `/etc/security/limits.conf` file. For example, updating the maximum number of open files to 10,000 for the `forge` user would look like the following: + +```ini +# /etc/security/limits.conf +forge soft nofile 10000 +forge hard nofile 10000 +``` + + +#### Event Loop + +Under the hood, Reverb uses a ReactPHP event loop to manage WebSocket connections on the server. By default, this event loop is powered by `stream_select`, which doesn't require any additional extensions. However, `stream_select` is typically limited to 1,024 open files. As such, if you plan to handle more than 1,000 concurrent connections, you will need to use an alternative event loop not bound to the same restrictions. + +Reverb will automatically switch to an `ext-event`, `ext-ev`, or `ext-uv` powered loop when available. All of these PHP extensions are available for install via PECL: + +```sh +pecl install event +# or +pecl install ev +# or +pecl install uv +``` + + +### Web Server + +In most cases, Reverb runs on a non web-facing port on your server. So, in order to route traffic to Reverb, you should configure a reverse proxy. Assuming Reverb is running on host `0.0.0.0` and port `8080` and your server utilizes the Nginx web server, a reverse proxy can be defined for your Reverb server using the following Nginx site configuration: + +```nginx +server { + ... + + location / { + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header Scheme $scheme; + proxy_set_header SERVER_PORT $server_port; + proxy_set_header REMOTE_ADDR $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + proxy_pass http://0.0.0.0:8080; + } + + ... +``` + +Typically, web servers are configured to limit the number of allowed connections in order to prevent overloading the server. To increase the number of allowed connections on an Nginx web server to 10,000, the `worker_rlimit_nofile` and `worker_connections` values of the `nginx.conf` file should be updated: + +```nginx +user forge; +worker_processes auto; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; +worker_rlimit_nofile 10000; + +events { + worker_connections 10000; + multi_accept on; +} +``` + +The configuration above will allow up to 10,000 Nginx workers per process to be spawned. In addition, this configuration sets Nginx's open file limit to 10,000. + + +### Ports + +Unix-based operating systems typically limit the number of ports which can be opened on the server. You may see the current allowed range via the following command: + + ```sh + cat /proc/sys/net/ipv4/ip_local_port_range +# 32768 60999 +``` + +The output above shows the server can handle a maximum of 28,231 (60,999 - 32,768) connections since each connection requires a free port. Although we recommend [horizontal scaling](#scaling) to increase the number of allowed connections, you may increase the number of available open ports by updating the allowed port range in your server's `/etc/sysctl.conf` configuration file. + + +### Process Management + +In most cases, you should use a process manager such as Supervisor to ensure the Reverb server is continually running. If you are using Supervisor to run Reverb, you should update the `minfds` setting of your server's `supervisor.conf` file to ensure Supervisor is able to open the files required to handle connections to your Reverb server: + +```ini +[supervisord] +... +minfds=10000 +``` + + +### Scaling + +If you need to handle more connections than a single server will allow, you may scale your Reverb server horizontally. Utilizing the publish / subscribe capabilities of Redis, Reverb is able to manage connections across multiple servers. When a message is received by one of your application's Reverb servers, the server will use Redis to publish the incoming message to all other servers. + +To enable horizontal scaling, you should set the `REVERB_SCALING_ENABLED` environment variable to `true` in your application's `.env` configuration file: + +```env +REVERB_SCALING_ENABLED=true +``` + +Next, you should have a dedicated, central Redis server to which all of the Reverb servers will communicate. Reverb will use the [default Redis connection configured for your application](/docs/{{version}}/redis#configuration) to publish messages to all of your Reverb servers. + +Once you have enabled Reverb's scaling option and configured a Redis server, you may simply invoke the `reverb:start` command on multiple servers that are able to communicate with your Redis server. These Reverb servers should be placed behind a load balancer that distributes incoming requests evenly among the servers. From 72983f7433eeb118724c3a58f66435ff44eab23b Mon Sep 17 00:00:00 2001 From: Mohammed Elhaouari Date: Mon, 19 Feb 2024 03:02:31 +0100 Subject: [PATCH 1369/2609] Fix typo in Arr::take() examples (#9353) --- helpers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index 17d0862289b..4bca5fa71d8 100644 --- a/helpers.md +++ b/helpers.md @@ -852,7 +852,7 @@ The `Arr::take` method returns a new array with the specified number of items: $array = [0, 1, 2, 3, 4, 5]; - $chunk = Arr::take(3); + $chunk = Arr::take($array, 3); // [0, 1, 2] @@ -860,7 +860,7 @@ You may also pass a negative integer to take the specified number of items from $array = [0, 1, 2, 3, 4, 5]; - $chunk = Arr::take(-2); + $chunk = Arr::take($array, -2); // [4, 5] From 678fa19281215a24e7692a68cc3779ea2a0419bc Mon Sep 17 00:00:00 2001 From: laser-hybiz <100562257+laser-hybiz@users.noreply.github.com> Date: Mon, 19 Feb 2024 09:25:55 -0500 Subject: [PATCH 1370/2609] Update eloquent.md (#9356) Co-authored-by: lazer-hybiz <100562257+lazer-hybiz@users.noreply.github.com> --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index fea48c128a1..53e1270468f 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1542,7 +1542,7 @@ To register an observer, you may place the `ObservedBy` attribute on the corresp // } -Or, you may manually register an observing by calling the `observe` method on the model you wish to observe. You may register observers in the `boot` method of your application's `App\Providers\EventServiceProvider` service provider: +Or, you may manually register an observer by calling the `observe` method on the model you wish to observe. You may register observers in the `boot` method of your application's `App\Providers\EventServiceProvider` service provider: use App\Models\User; use App\Observers\UserObserver; From 1aca84db838dd464d45dc0a9689be648cd808a74 Mon Sep 17 00:00:00 2001 From: Vimal Gorasiya Date: Mon, 19 Feb 2024 19:56:46 +0530 Subject: [PATCH 1371/2609] Update helpers.md (#9354) Added missing namespace import for Arr::toCssStyles() in array helpers documentation for consistency. --- helpers.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helpers.md b/helpers.md index 4bca5fa71d8..3615e1ea383 100644 --- a/helpers.md +++ b/helpers.md @@ -888,6 +888,8 @@ The `Arr::toCssClasses` method conditionally compiles a CSS class string. The me The `Arr::toCssStyles` conditionally compiles a CSS style string. The method accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list: ```php +use Illuminate\Support\Arr; + $hasColor = true; $array = ['background-color: blue', 'color: blue' => $hasColor]; From 80451760bb8014b573a691ac2350e097745149c5 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Mon, 19 Feb 2024 14:29:45 +0000 Subject: [PATCH 1372/2609] [11.x] Updates Reverb x Forge integration details (#9357) * update forge integration * reword * Update reverb.md --------- Co-authored-by: Taylor Otwell --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index 2ec66259c35..d9b678cbc89 100644 --- a/reverb.md +++ b/reverb.md @@ -151,7 +151,7 @@ php artisan reverb:restart Due to the long-running nature of WebSocket servers, you may need to make some optimizations to your server / hosting environment to ensure your Reverb server can effectively handle the optimal number of connections for the resources available on your server. > **Note** -> If your site is managed by [Laravel Forge](https://forge.laravel.com), you may automatically optimize your server for Reverb directly from the "Application" panel. +> If your site is managed by [Laravel Forge](https://forge.laravel.com), you may automatically optimize your server for Reverb directly from the "Application" panel. By enabling the Reverb integration, Forge will ensure your server is production-ready, including installing any required extensions and increasing the allowed number of connections. ### Open Files From d550c75b56fe72277b9a35f3daa4ba3f194af1cb Mon Sep 17 00:00:00 2001 From: Oussama Mater Date: Mon, 19 Feb 2024 15:35:27 +0100 Subject: [PATCH 1373/2609] [10.x] Added missing `toBase64` docs (#9352) * Added missing toBase64 docs * Update strings.md * Update strings.md --------- Co-authored-by: Oussama Mater Co-authored-by: Taylor Otwell --- strings.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/strings.md b/strings.md index 29590de2f16..01855fdca32 100644 --- a/strings.md +++ b/strings.md @@ -95,6 +95,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::swap](#method-str-swap) [Str::take](#method-take) [Str::title](#method-title-case) +[Str::toBase64](#method-str-to-base64) [Str::toHtmlString](#method-str-to-html-string) [Str::ucfirst](#method-str-ucfirst) [Str::ucsplit](#method-str-ucsplit) @@ -194,6 +195,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [tap](#method-fluent-str-tap) [test](#method-fluent-str-test) [title](#method-fluent-str-title) +[toBase64](#method-fluent-str-to-base64) [trim](#method-fluent-str-trim) [ucfirst](#method-fluent-str-ucfirst) [ucsplit](#method-fluent-str-ucsplit) @@ -1145,6 +1147,17 @@ The `Str::title` method converts the given string to `Title Case`: // A Nice Title Uses The Correct Case + +#### `Str::toBase64()` {.collection-method} + +The `Str::toBase64` method converts the given string to Base64: + + use Illuminate\Support\Str; + + $base64 = Str::toBase64('Laravel'); + + // TGFyYXZlbA== + #### `Str::toHtmlString()` {.collection-method} @@ -2417,6 +2430,17 @@ The `title` method converts the given string to `Title Case`: // A Nice Title Uses The Correct Case + +#### `toBase64()` {.collection-method} + +The `toBase64` method converts the given string to Base64: + + use Illuminate\Support\Str; + + $base64 = Str::of('Laravel')->toBase64(); + + // TGFyYXZlbA== + #### `trim` {.collection-method} From 5b92ff21e72e0f3c993950533867fc77c9ec5d67 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Feb 2024 14:55:14 +0000 Subject: [PATCH 1374/2609] Rewrites `validations` (#9355) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index c4b64ed455f..085a8c6b523 100644 --- a/validation.md +++ b/validation.md @@ -254,7 +254,7 @@ Laravel also provides a global `old` helper. If you are displaying old input wit ### A Note on Optional Fields -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. Because of this, you will often need to mark your "optional" request fields as `nullable` if you do not want the validator to consider `null` values as invalid. For example: +By default, Laravel includes the `TrimStrings` and `ConvertEmptyStringsToNull` middleware in your application's global middleware stack. Because of this, you will often need to mark your "optional" request fields as `nullable` if you do not want the validator to consider `null` values as invalid. For example: $request->validate([ 'title' => 'required|unique:posts|max:255', From db4d336a48c2da0b16896d1eea9c76834936ed71 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Feb 2024 15:06:10 +0000 Subject: [PATCH 1375/2609] [11.x] Rewrites `requests` for L11 (#9343) * [11.x] Rewrites `requests` for L11 * Uses named arguments * formatting --------- Co-authored-by: Taylor Otwell --- requests.md | 118 +++++++++++++++++++++++----------------------------- 1 file changed, 51 insertions(+), 67 deletions(-) diff --git a/requests.md b/requests.md index 8d5a24099fb..da6a9652362 100644 --- a/requests.md +++ b/requests.md @@ -503,33 +503,33 @@ All cookies created by the Laravel framework are encrypted and signed with an au ## Input Trimming and Normalization -By default, Laravel includes the `App\Http\Middleware\TrimStrings` and `Illuminate\Foundation\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. +By default, Laravel includes the `Illuminate\Foundation\Http\Middleware\TrimStrings` and `Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull` middleware in your application's global middleware stack. 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. #### Disabling Input Normalization -If you would like to disable this behavior for all requests, 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. +If you would like to disable this behavior for all requests, you may remove the two middleware from your application's middleware stack by invoking the `$middleware->remove` method in your application's `bootstrap/app.php` file: -If you would like to disable string trimming and empty string conversion for a subset of requests to your application, you may use the `skipWhen` method offered by both middleware. This method accepts a closure which should return `true` or `false` to indicate if input normalization should be skipped. Typically, the `skipWhen` method should be invoked in the `boot` method of your application's `AppServiceProvider`. + use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; + use Illuminate\Foundation\Http\Middleware\TrimStrings; -```php -use App\Http\Middleware\TrimStrings; -use Illuminate\Http\Request; -use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; - -/** - * Bootstrap any application services. - */ -public function boot(): void -{ - TrimStrings::skipWhen(function (Request $request) { - return $request->is('admin/*'); - }); + ->withMiddleware(function (Middleware $middleware) { + $middleware->remove([ + ConvertEmptyStringsToNull::class, + TrimStrings::class, + ]); + }) - ConvertEmptyStringsToNull::skipWhen(function (Request $request) { - // ... - }); -} -``` +If you would like to disable string trimming and empty string conversion for a subset of requests to your application, you may use the `trimStrings` and `convertEmptyStringsToNull` middleware methods within your application's `bootstrap/app.php` file. Both methods accept an array of closures, which should return `true` or `false` to indicate whether input normalization should be skipped: + + ->withMiddleware(function (Middleware $middleware) { + $middleware->convertEmptyStringsToNull(except: [ + fn (Request $request) => $request->is('admin/*'), + ]); + + $middleware->trimStrings(except: [ + fn (Request $request) => $request->is('admin/*'), + ]); + }) ## Files @@ -599,70 +599,54 @@ If you do not want a filename to be automatically generated, you may use the `st 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: - - withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(at: [ '192.168.1.1', '192.168.1.2', - ]; + ]); + }) - /** - * The headers that should be used to detect proxies. - * - * @var int - */ - protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO; - } +In addition to configuring the trusted proxies, you may also configure the proxy headers that should be trusted: + + ->withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB + ); + }) > [!NOTE] -> If you are using AWS Elastic Load Balancing, your `$headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `$headers` property, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). +> If you are using AWS Elastic Load Balancing, your `headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `headers` value, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). #### Trusting All Proxies If you are using Amazon AWS or another "cloud" load balancer provider, you may not know the IP addresses of your actual balancers. In this case, you may use `*` to trust all proxies: - /** - * The trusted proxies for this application. - * - * @var string|array - */ - protected $proxies = '*'; + ->withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(at: '*'); + }) ## Configuring Trusted Hosts By default, Laravel will respond to all requests it receives regardless of the content of the HTTP request's `Host` header. In addition, the `Host` header's value will be used when generating absolute URLs to your application during a web request. -Typically, you should configure your web server, such as Nginx or Apache, to only send requests to your application that match a given host name. However, if you do not have the ability to customize your web server directly and need to instruct Laravel to only respond to certain host names, you may do so by enabling the `App\Http\Middleware\TrustHosts` middleware for your application. +Typically, you should configure your web server, such as Nginx or Apache, to only send requests to your application that match a given host name. However, if you do not have the ability to customize your web server directly and need to instruct Laravel to only respond to certain host names, you may do so by enabling the `Illuminate\Http\Middleware\TrustHosts` middleware for your application. -The `TrustHosts` middleware is already included in the `$middleware` stack of your application; however, you should uncomment it so that it becomes active. Within this middleware's `hosts` method, you may specify the host names that your application should respond to. Incoming requests with other `Host` value headers will be rejected: +To enable the `TrustHosts` middleware, you should use the `trustHosts` middleware method in your application's `bootstrap/app.php` file. Using the `at` argument of this method, you may specify the host names that your application should respond to. Incoming requests with other `Host` value headers will be rejected: - /** - * Get the host patterns that should be trusted. - * - * @return array - */ - public function hosts(): array - { - return [ - 'laravel.test', - $this->allSubdomainsOfApplicationUrl(), - ]; - } + ->withMiddleware(function (Middleware $middleware) { + $middleware->trustHosts(at: ['laravel.test']); + }) + +By default, requests coming from subdomains of the application's URL are also automatically trusted. If you would like to disable this behavior, you may use the `subdomains` argument: + + ->withMiddleware(function (Middleware $middleware) { + $middleware->trustHosts(at: ['laravel.test'], subdomains: false); + }) -The `allSubdomainsOfApplicationUrl` helper method will return a regular expression matching all subdomains of your application's `app.url` configuration value. This helper method provides a convenient way to allow all of your application's subdomains when building an application that utilizes wildcard subdomains. From a2e72a1e86c74eed43aeda539d2ecb6df1a9ce8f Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Feb 2024 15:08:10 +0000 Subject: [PATCH 1376/2609] [11.x] Rewrites `urls` for L11 (#9347) * Rewrites `urls` for L11 * Explicit priority * formattingg --------- Co-authored-by: Taylor Otwell --- middleware.md | 4 +++- urls.md | 53 +++++++++++++++++++++------------------------------ 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/middleware.md b/middleware.md index 2522a062728..16da7b74f44 100644 --- a/middleware.md +++ b/middleware.md @@ -255,12 +255,14 @@ Rarely, you may need your middleware to execute in a specific order but not have $middleware->priority([ \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, \Illuminate\Cookie\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, - \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class, ]); diff --git a/urls.md b/urls.md index 27a6b0ed47f..10eaf68ef3f 100644 --- a/urls.md +++ b/urls.md @@ -130,20 +130,7 @@ Sometimes, you may need to allow your application's frontend to append data to a abort(401); } -Instead of validating signed URLs using the incoming request instance, you may assign the `Illuminate\Routing\Middleware\ValidateSignature` [middleware](/docs/{{version}}/middleware) to the route. If it is not already present, you may assign this middleware an alias in your HTTP kernel's `$middlewareAliases` array: - - /** - * The application's middleware aliases. - * - * Aliases may be used to conveniently assign middleware to routes and groups. - * - * @var array - */ - protected $middlewareAliases = [ - '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` HTTP response: +Instead of validating signed URLs using the incoming request instance, you may assign the `signed` (`Illuminate\Routing\Middleware\ValidateSignature`) [middleware](/docs/{{version}}/middleware) to the 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) { // ... @@ -225,20 +212,24 @@ Once the default value for the `locale` parameter has been set, you are no longe #### URL Defaults and Middleware Priority -Setting URL default values can interfere with Laravel's handling of implicit model bindings. Therefore, you should [prioritize your middleware](/docs/{{version}}/middleware#sorting-middleware) that set URL defaults to be executed before Laravel's own `SubstituteBindings` middleware. You can accomplish this by making sure your middleware occurs before the `SubstituteBindings` middleware within the `$middlewarePriority` property of your application's HTTP kernel. - -The `$middlewarePriority` property is defined in the base `Illuminate\Foundation\Http\Kernel` class. You may copy its definition from that class and overwrite it in your application's HTTP kernel in order to modify it: - - /** - * The priority-sorted list of middleware. - * - * This forces non-global middleware to always be in the given order. - * - * @var array - */ - protected $middlewarePriority = [ - // ... - \App\Http\Middleware\SetDefaultLocaleForUrls::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - // ... - ]; +Setting URL default values can interfere with Laravel's handling of implicit model bindings. Therefore, you should [prioritize your middleware](/docs/{{version}}/middleware#sorting-middleware) that set URL defaults to be executed before Laravel's own `SubstituteBindings` middleware. You can accomplish this using the `priority` middleware method in your application's `bootstrap/app.php` file: + +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->priority([ + \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, + \Illuminate\Cookie\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, + \Illuminate\Routing\Middleware\ThrottleRequests::class, + \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, + \App\Http\Middleware\SetDefaultLocaleForUrls::class, // [tl! add] + \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Illuminate\Auth\Middleware\Authorize::class, + ]); +}) +``` From e847a7d30a6d73bd84184bc1225b924cbbb04e9d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Feb 2024 15:31:33 +0000 Subject: [PATCH 1377/2609] [11.x] Rewrites `errors` for L11 (#9358) * Rewrites `errors` * formatting --------- Co-authored-by: Taylor Otwell --- errors.md | 281 +++++++++++++++++++++--------------------------------- 1 file changed, 109 insertions(+), 172 deletions(-) diff --git a/errors.md b/errors.md index 3a8baabeb29..d0eb61df726 100644 --- a/errors.md +++ b/errors.md @@ -2,7 +2,7 @@ - [Introduction](#introduction) - [Configuration](#configuration) -- [The Exception Handler](#the-exception-handler) +- [Handling Exceptions](#exceptions) - [Reporting Exceptions](#reporting-exceptions) - [Exception Log Levels](#exception-log-levels) - [Ignoring Exceptions by Type](#ignoring-exceptions-by-type) @@ -15,46 +15,44 @@ ## Introduction -When you start a new Laravel project, error and exception handling is already configured for you. The `App\Exceptions\Handler` class is where all exceptions thrown by your application are logged and then rendered to the user. We'll dive deeper into this class throughout this documentation. +When you start a new Laravel project, error and exception handling is already configured for you; however, at any point, you may use the `withExceptions` method in your application's `bootstrap/app.php` to manage how exceptions are reported and rendered by your application. + +The `$exceptions` object provided to the `withExceptions` closure is an instance of `Illuminate\Foundation\Configuration\Exceptions` and is responsible for managing exception handling in your application. We'll dive deeper into this object throughout this documentation. ## Configuration -The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. +The `debug` option in your `config/app.php` configuration file, which you may optionally publish using the `config:publish` Artisan command, determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. During local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** - -## The Exception Handler + +## Handling Exceptions ### Reporting Exceptions -All exceptions are handled by the `App\Exceptions\Handler` class. This class contains a `register` method where you may register custom exception reporting and rendering callbacks. We'll examine each of these concepts in detail. Exception reporting is used to log exceptions or send them to an external service like [Flare](https://flareapp.io), [Bugsnag](https://bugsnag.com), or [Sentry](https://github.com/getsentry/sentry-laravel). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. - -If you need to report different types of exceptions in different ways, you may use the `reportable` method to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will determine what type of exception the closure reports by examining the type-hint of the closure: +In Laravel, exception reporting is used to log exceptions or send them to an external service [Sentry](https://github.com/getsentry/sentry-laravel) or [Flare](https://flareapp.io). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. - use App\Exceptions\InvalidOrderException; +If you need to report different types of exceptions in different ways, you may use the `reportable` exception method in your application's `bootstrap/app.php` to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will determine what type of exception the closure reports by examining the type-hint of the closure: - /** - * Register the exception handling callbacks for the application. - */ - public function register(): void - { - $this->reportable(function (InvalidOrderException $e) { + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->reportable(function (InvalidOrderException $e) { // ... }); - } + }) When you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application. If you wish to stop the propagation of the exception to the default logging stack, you may use the `stop` method when defining your reporting callback or return `false` from the callback: - $this->reportable(function (InvalidOrderException $e) { - // ... - })->stop(); + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->reportable(function (InvalidOrderException $e) { + // ... + })->stop(); - $this->reportable(function (InvalidOrderException $e) { - return false; - }); + $exceptions->reportable(function (InvalidOrderException $e) { + return false; + }); + }) > [!NOTE] > To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). @@ -62,19 +60,13 @@ When you register a custom exception reporting callback using the `reportable` m #### Global Log Context -If available, Laravel automatically adds the current user's ID to every exception's log message as contextual data. You may define your own global contextual data by defining a `context` method on your application's `App\Exceptions\Handler` class. This information will be included in every exception's log message written by your application: +If available, Laravel automatically adds the current user's ID to every exception's log message as contextual data. You may define your own global contextual data using the `context` exception method in your application's `bootstrap/app.php` file. This information will be included in every exception's log message written by your application: - /** - * Get the default context variables for logging. - * - * @return array - */ - protected function context(): array - { - return array_merge(parent::context(), [ + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->context(fn () => [ 'foo' => 'bar', ]); - } + }) #### Exception Log Context @@ -105,7 +97,7 @@ While adding context to every log message can be useful, sometimes a particular #### The `report` Helper -Sometimes you may need to report an exception but continue handling the current request. The `report` helper function allows you to quickly report an exception via the exception handler without rendering an error page to the user: +Sometimes you may need to report an exception but continue handling the current request. The `report` helper function allows you to quickly report an exception without rendering an error page to the user: public function isValid(string $value): bool { @@ -123,25 +115,11 @@ Sometimes you may need to report an exception but continue handling the current If you are using the `report` function throughout your application, you may occasionally report the same exception multiple times, creating duplicate entries in your logs. -If you would like to ensure that a single instance of an exception is only ever reported once, you may set the `$withoutDuplicates` property to `true` within your application's `App\Exceptions\Handler` class: - -```php -namespace App\Exceptions; +If you would like to ensure that a single instance of an exception is only ever reported once, you may invoke the `dontReportDuplicates` exception method in your application's `bootstrap/app.php` file: -use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; - -class Handler extends ExceptionHandler -{ - /** - * Indicates that an exception instance should only be reported once. - * - * @var bool - */ - protected $withoutDuplicates = true; - - // ... -} -``` + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->dontReportDuplicates(); + }) Now, when the `report` helper is called with the same instance of an exception, only the first call will be reported: @@ -167,93 +145,71 @@ When messages are written to your application's [logs](/docs/{{version}}/logging As noted above, even when you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application; however, since the log level can sometimes influence the channels on which a message is logged, you may wish to configure the log level that certain exceptions are logged at. -To accomplish this, you may define a `$levels` property on your application's exception handler. This property should contain an array of exception types and their associated log levels: +To accomplish this, you may use the `level` exception method in your application's `bootstrap/app.php` file. This method receives the exception type as its first argument and the log level as its second argument: use PDOException; use Psr\Log\LogLevel; - /** - * A list of exception types with their corresponding custom log levels. - * - * @var array, \Psr\Log\LogLevel::*> - */ - protected $levels = [ - PDOException::class => LogLevel::CRITICAL, - ]; + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->level(PDOException::class, LogLevel::CRITICAL); + }) ### Ignoring Exceptions by Type -When building your application, there will be some types of exceptions you never want to report. To ignore these exceptions, define a `$dontReport` property on your application's exception handler. Any classes that you add to this property will never be reported; however, they may still have custom rendering logic: +When building your application, there will be some types of exceptions you never want to report. To ignore these exceptions, you may use the `dontReport` exception method in your application's `boostrap/app.php` file. Any class provided to this method will never be reported; however, they may still have custom rendering logic: use App\Exceptions\InvalidOrderException; - /** - * A list of the exception types that are not reported. - * - * @var array> - */ - protected $dontReport = [ - InvalidOrderException::class, - ]; + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->dontReport([ + InvalidOrderException::class, + ]); + }) -Internally, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP errors or 419 HTTP responses generated by invalid CSRF tokens. If you would like to instruct Laravel to stop ignoring a given type of exception, you may invoke the `stopIgnoring` method within your exception handler's `register` method: +Internally, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP errors or 419 HTTP responses generated by invalid CSRF tokens. If you would like to instruct Laravel to stop ignoring a given type of exception, you may use the `stopIgnoring` exception method in your application's `boostrap/app.php` file: use Symfony\Component\HttpKernel\Exception\HttpException; - /** - * Register the exception handling callbacks for the application. - */ - public function register(): void - { - $this->stopIgnoring(HttpException::class); - - // ... - } + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->stopIgnoring(HttpException::class); + }) ### Rendering Exceptions -By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by invoking the `renderable` method within your exception handler. +By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by using the `renderable` exception method in your application's `boostrap/app.php` file. The closure passed to the `renderable` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will determine what type of exception the closure renders by examining the type-hint of the closure: use App\Exceptions\InvalidOrderException; use Illuminate\Http\Request; - /** - * Register the exception handling callbacks for the application. - */ - public function register(): void - { - $this->renderable(function (InvalidOrderException $e, Request $request) { + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->renderable(function (InvalidOrderException $e, Request $request) { return response()->view('errors.invalid-order', [], 500); }); - } + }) You may also use the `renderable` method to override the rendering behavior for built-in Laravel or Symfony exceptions such as `NotFoundHttpException`. If the closure given to the `renderable` method does not return a value, Laravel's default exception rendering will be utilized: use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - /** - * Register the exception handling callbacks for the application. - */ - public function register(): void - { - $this->renderable(function (NotFoundHttpException $e, Request $request) { + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->renderable(function (NotFoundHttpException $e, Request $request) { if ($request->is('api/*')) { return response()->json([ 'message' => 'Record not found.' ], 404); } }); - } + }) ### Reportable and Renderable Exceptions -Instead of defining custom reporting and rendering behavior in your exception handler's `register` method, you may define `report` and `render` methods directly on your application's exceptions. When these methods exist, they will automatically be called by the framework: +Instead of defining custom reporting and rendering behavior in your application's `boostrap/app.php` file, you may define `report` and `render` methods directly on your application's exceptions. When these methods exist, they will automatically be called by the framework: withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable Throwable) { + return Lottery::odds(1, 1000); + }); + }) It is also possible to conditionally sample based on the exception type. If you would like to only sample instances of a specific exception class, you may return a `Lottery` instance only for that class: -```php -use App\Exceptions\ApiMonitoringException; -use Illuminate\Support\Lottery; -use Throwable; - -/** - * Throttle incoming exceptions. - */ -protected function throttle(Throwable $e): mixed -{ - if ($e instanceof ApiMonitoringException) { - return Lottery::odds(1, 1000); - } -} -``` + use App\Exceptions\ApiMonitoringException; + use Illuminate\Support\Lottery; + use Throwable; + + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable Throwable) { + if ($e instanceof ApiMonitoringException) { + return Lottery::odds(1, 1000); + } + }); + }) You may also rate limit exceptions logged or sent to an external error tracking service by returning a `Limit` instance instead of a `Lottery`. This is useful if you want to protect against sudden bursts of exceptions flooding your logs, for example, when a third-party service used by your application is down: -```php -use Illuminate\Broadcasting\BroadcastException; -use Illuminate\Cache\RateLimiting\Limit; -use Throwable; - -/** - * Throttle incoming exceptions. - */ -protected function throttle(Throwable $e): mixed -{ - if ($e instanceof BroadcastException) { - return Limit::perMinute(300); - } -} -``` + use Illuminate\Broadcasting\BroadcastException; + use Illuminate\Cache\RateLimiting\Limit; + use Throwable; + + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable Throwable) { + if ($e instanceof BroadcastException) { + return Limit::perMinute(300); + } + }); + }) By default, limits will use the exception's class as the rate limit key. You can customize this by specifying your own key using the `by` method on the `Limit`: -```php -use Illuminate\Broadcasting\BroadcastException; -use Illuminate\Cache\RateLimiting\Limit; -use Throwable; - -/** - * Throttle incoming exceptions. - */ -protected function throttle(Throwable $e): mixed -{ - if ($e instanceof BroadcastException) { - return Limit::perMinute(300)->by($e->getMessage()); - } -} -``` + use Illuminate\Broadcasting\BroadcastException; + use Illuminate\Cache\RateLimiting\Limit; + use Throwable; + + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable Throwable) { + if ($e instanceof BroadcastException) { + return Limit::perMinute(300)->by($e->getMessage()); + } + }); + }) + Of course, you may return a mixture of `Lottery` and `Limit` instances for different exceptions: -```php -use App\Exceptions\ApiMonitoringException; -use Illuminate\Broadcasting\BroadcastException; -use Illuminate\Cache\RateLimiting\Limit; -use Illuminate\Support\Lottery; -use Throwable; - -/** - * Throttle incoming exceptions. - */ -protected function throttle(Throwable $e): mixed -{ - return match (true) { - $e instanceof BroadcastException => Limit::perMinute(300), - $e instanceof ApiMonitoringException => Lottery::odds(1, 1000), - default => Limit::none(), - }; -} -``` + use App\Exceptions\ApiMonitoringException; + use Illuminate\Broadcasting\BroadcastException; + use Illuminate\Cache\RateLimiting\Limit; + use Illuminate\Support\Lottery; + use Throwable; + + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable Throwable) { + return match (true) { + $e instanceof BroadcastException => Limit::perMinute(300), + $e instanceof ApiMonitoringException => Lottery::odds(1, 1000), + default => Limit::none(), + }; + }); + }) ## HTTP Exceptions From f4039f39f7470aef369e5634b3be7efd694f878f Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Feb 2024 15:33:47 +0000 Subject: [PATCH 1378/2609] Fixes ancor --- errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors.md b/errors.md index d0eb61df726..9be0b0df69d 100644 --- a/errors.md +++ b/errors.md @@ -2,7 +2,7 @@ - [Introduction](#introduction) - [Configuration](#configuration) -- [Handling Exceptions](#exceptions) +- [Handling Exceptions](#handling-exceptions) - [Reporting Exceptions](#reporting-exceptions) - [Exception Log Levels](#exception-log-levels) - [Ignoring Exceptions by Type](#ignoring-exceptions-by-type) From d6c188f47e1cbcc4a7368a53c3afd3db3e173f96 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Feb 2024 12:16:28 -0600 Subject: [PATCH 1379/2609] working on release notes --- releases.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/releases.md b/releases.md index e112e2e2394..b5ffa50a2e5 100644 --- a/releases.md +++ b/releases.md @@ -49,9 +49,103 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe ## Laravel 11 -To be determined... +Laravel 11 continues the improvements made in Laravel 10.x by introducing a streamlined application structure, per-second rate limiting, health routing, graceful encryption key rotation, queue testing improvements, [Resend](https://resend.com) mail transport, Prompt validator integration, new Artisan commands, and more. In addition, Laravel Reverb, a first-party, scalable WebSocket server has been introduced to provide robust real-time capabilities to your applications. ### PHP 8.2 Laravel 11.x requires a minimum PHP version of 8.2. + + +### Streamlined Application Structure + +Laravel 11 introduces a streamlined application structure for **new** Laravel applications, without requiring any changes to existing applications. The new application structure is intended to provide a leaner, more modern experience, while retaining many of the concepts that Laravel developers are already familiar with. Below we will discuss the highlights of Laravel's new application structure. + +#### The Application Bootstrap File + +The `bootstrap/app.php` file has been revitalized as a code-first application configuration file. From this file, you may now customize your application's routing, middleware, service providers, exception handling, and more. This file unifies a variety of high-level application behavior settings that were previously scattered throughout your application's file structure. + +```php +return Application::configure(basePath: dirname(__DIR__)) + ->withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware) { + // + }) + ->withExceptions(function (Exceptions $exceptions) { + // + })->create(); +``` + +#### Service Providers + +Instead of the default Laravel application structure containing five service providers, Laravel 11 only includes a single `AppServiceProvider`. The functionality of the previous service providers has been incorporated into the `bootstrap/app.php`, is handled automatically by the framework, or may be placed in your application's `AppServiceProvider`. + +For example, event discovery is now enabled by default, largely eliminating the need for manual registration of events and their listeners. However, if you do need to manually register events, you may simply do so in the `AppServiceProvider`. Similarly, route model bindings or authorization gates you may have previously registered in the `AuthServiceProvider` may also be registered in the `AppServiceProvider`. + +#### Configuration Files + +The use of environment variables has been expanded, with additional place-holders being added to the `.env.example` file included with new Laravel applications. Because of this, almost all core framework functionality can be configured via your application's `.env` file instead of individual configuration files. Therefore, the `config` directory no longer contains any configuration files by default. + +Instead, configuration files can be published using the new `config:publish` Artisan command, which allows you to publish only the configuration files you would like to customize: + +```shell +php artisan config:publish +``` + +Of course, you may easily publish all of the framework's configuration files: + +```shell +php artisan config:publish --all +``` + +#### Opt-in API and Broadcast Routing + +The `api.php` and `channels.php` route files are no longer present by default, as many applications do not require these files. Instead, they may be created using simple Artisan commands: + +```shell +php artisan install:api + +php artisan install:broadcasting +``` + +#### Middleware + +Previously, new Laravel applications included nine middleware. These middleware performed a variety of tasks such as authenticating requests, trimming input strings, and validating CSRF tokens. + +In Laravel 11, these middleware have been moved into the framework itself, so that they do not add bulk to your application's structure. New methods of customizing the behavior of these middleware have been added to the framework and may be invoked from your application's `bootstrap/app.php` file: + +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->validateCsrfTokens( + except: ['stripe/*'] + ); + + $middleware->web(append: [ + EnsureUserIsSubscribed::class, + ]) +}) +``` + +Since all middleware can be easily customized via your application's `bootstrap/app.php`, the need for a separate HTTP "kernel" class has been eliminated. + +#### Scheduling + +Using a new `Schedule` facade, scheduled tasks may now be defined directly in your application's `routes/console.php` file, eliminating the need for a separate console "kernel" class: + +```php +use Illuminate\Support\Facades\Schedule; + +Schedule::command('emails:send')->daily(); +``` + +#### Exception Handling + +Like routing and middleware, exception handling can now be customized from your application's `bootstrap/app.php` file instead of a separate exception handler class, reducing the overall number of files included in a new Laravel application: + + + +#### Application Defaults From 06961b24b18764632b27c5c57955abb721f55af8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Feb 2024 12:38:30 -0600 Subject: [PATCH 1380/2609] working on release notes --- releases.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/releases.md b/releases.md index b5ffa50a2e5..4d2397717df 100644 --- a/releases.md +++ b/releases.md @@ -146,6 +146,31 @@ Schedule::command('emails:send')->daily(); Like routing and middleware, exception handling can now be customized from your application's `bootstrap/app.php` file instead of a separate exception handler class, reducing the overall number of files included in a new Laravel application: +```php +->withExceptions(function (Exceptions $exceptions) { + $exceptions->dontReport(MissedFlightException::class); + $exceptions->reportable(function (InvalidOrderException $e) { + // ... + }); +}) +``` #### Application Defaults + +By default, new Laravel applications use SQLite for database storage, as well as the `database` driver for Laravel's session, cache, and queue. This allows you to begin building your application immediately after creating a new Laravel application, without being required to install additional software or create additional database migrations. + +In addition, over time, the `database` drivers for these Laravel services have become robust enough for production usage in many application contexts; therefore, they provide a sensible, unified choice for both local and production applications. + + +### Laravel Reverb + +[Laravel Reverb](https://reverb.laravel.com) brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, and provides seamless integration with Laravel’s existing suite of event broadcasting tools, such as Laravel Echo. + +```shell +php artisan reverb:start +``` + +In addition, Reverb supports horizontal scaling via Redis's publish / subscribe capabilities, allowing you to distribute your WebSocket traffic across multiple backend Reverb servers all supporting a single, high-demand application. + +For more information on Laravel Reverb, please consult the complete [Reverb documentation](/docs/{{version}}/reverb). From 60c612629c8d66e984ea27e3d3cee44c0510f7ab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Feb 2024 12:48:57 -0600 Subject: [PATCH 1381/2609] wip --- releases.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/releases.md b/releases.md index 4d2397717df..73dd71b19d2 100644 --- a/releases.md +++ b/releases.md @@ -59,6 +59,8 @@ Laravel 11.x requires a minimum PHP version of 8.2. ### Streamlined Application Structure +_Laravel's streamlined application structure was developed by [Taylor Otwell](https://github.com/taylorotwell) and [Nuno Maduro](https://github.com/nunomaduro)_. + Laravel 11 introduces a streamlined application structure for **new** Laravel applications, without requiring any changes to existing applications. The new application structure is intended to provide a leaner, more modern experience, while retaining many of the concepts that Laravel developers are already familiar with. Below we will discuss the highlights of Laravel's new application structure. #### The Application Bootstrap File @@ -165,6 +167,8 @@ In addition, over time, the `database` drivers for these Laravel services have b ### Laravel Reverb +_Laravel Reverb was developed by [Joe Dixon](https://github.com/joedixon)_. + [Laravel Reverb](https://reverb.laravel.com) brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, and provides seamless integration with Laravel’s existing suite of event broadcasting tools, such as Laravel Echo. ```shell @@ -174,3 +178,40 @@ php artisan reverb:start In addition, Reverb supports horizontal scaling via Redis's publish / subscribe capabilities, allowing you to distribute your WebSocket traffic across multiple backend Reverb servers all supporting a single, high-demand application. For more information on Laravel Reverb, please consult the complete [Reverb documentation](/docs/{{version}}/reverb). + + +### Per-Second Rate Limiting + +_Per-second rate limiting was contributed by [Tim MacDonald](https://github.com/timacdonald)_. + +Laravel now supports "per-second" rate limiting for all rate limiters, including those for HTTP requests and queued jobs. Previously, Laravel's rate limiters were limited to "per-minute" granularity: + +```php +RateLimiter::for('invoices', function (Request $request) { + return Limit::perSecond(1); +}); +``` + +For more information on rate limiting in Laravel, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). + + +### Health Routing + +_Health routing was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. + +New Laravel 11 applications include a `health` routing directive, which instructs Laravel to define a simple health-check endpoint that may be invoked by third-party application health monitoring services or orchestration systems like Kubernetes. By default, this route is served at `/up`: + +```php +->withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', +) +``` + +When HTTP requests are made to this route, Laravel will also dispatch a `DiagnosingHealth` event, allowing you to perform additional health checks that are relevant to your application. + + +### Graceful Encryption Key Rotation + +... From 279cb743219c79207c0ef897c11c28b914fa1091 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Feb 2024 12:57:29 -0600 Subject: [PATCH 1382/2609] wip --- releases.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 73dd71b19d2..9218d8fddee 100644 --- a/releases.md +++ b/releases.md @@ -214,4 +214,12 @@ When HTTP requests are made to this route, Laravel will also dispatch a `Diagnos ### Graceful Encryption Key Rotation -... +_Graceful encryption key rotation was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. + +Since Laravel encrypts all cookies, including your application's session cookie, essentially every request to a Laravel application relies on encryption. However, because of this, rotating your application's encryption key would log all users out of your application. In addition, decrypting data that was encrypted by the previous encryption key becomes impossible. + +Laravel 11 allows you to define your application's previous encryption keys as a comma-delimited list via the `APP_PREVIOUS_KEYS` environment variable. + +When encrypting values, Laravel will always use the "current" encryption key, which is within the `APP_KEY` environment variable. When decrypting values, Laravel will first try the current key. If decryption fails using the current key, Laravel will try all previous keys until one of the keys is able to decrypt the value. + +This approach to graceful decryption allows users to keep using your application uninterrupted even if your encryption key rotated. From e66052b29bc29c1ca71a977a7efa8bb10addee95 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Feb 2024 13:02:08 -0600 Subject: [PATCH 1383/2609] wip --- releases.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/releases.md b/releases.md index 9218d8fddee..2bea1dd6306 100644 --- a/releases.md +++ b/releases.md @@ -223,3 +223,31 @@ Laravel 11 allows you to define your application's previous encryption keys as a When encrypting values, Laravel will always use the "current" encryption key, which is within the `APP_KEY` environment variable. When decrypting values, Laravel will first try the current key. If decryption fails using the current key, Laravel will try all previous keys until one of the keys is able to decrypt the value. This approach to graceful decryption allows users to keep using your application uninterrupted even if your encryption key rotated. + + +### Prompt Validation + +_Prompt validator integration was contributed by [Andrea Marco Sartori](https://github.com/cerbero90)_. + +[Laravel Prompts](/docs/{{version}}/prompts) is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. + +Laravel Prompts supports input validation via closures: + +```php +$name = text( + label: 'What is your name?', + validate: fn (string $value) => match (true) { + strlen($value) < 3 => 'The name must be at least 3 characters.', + strlen($value) > 255 => 'The name must not exceed 255 characters.', + default => null + } +); +``` + +However, this can become cumbersome when dealing with many inputs or complicated validation scenarios. Therefore, in Laravel 11, you may utilize the full power of Laravel's [validator](/docs/{{version}}/validation) when validating prompt inputs: + +```php +$name = text('What is your name?', validate: [ + 'name' => 'required|min:3|max:255', +]); +``` From 2ffeaa11fb7d47bdc54fc8236f0e7d1d935efaa8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Feb 2024 13:05:43 -0600 Subject: [PATCH 1384/2609] wip --- releases.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/releases.md b/releases.md index 2bea1dd6306..77f484b4d28 100644 --- a/releases.md +++ b/releases.md @@ -224,6 +224,8 @@ When encrypting values, Laravel will always use the "current" encryption key, wh This approach to graceful decryption allows users to keep using your application uninterrupted even if your encryption key rotated. +For more information on encryption in Laravel, check out the [encryption documentation](/docs/{{version}}/encryption). + ### Prompt Validation @@ -251,3 +253,22 @@ $name = text('What is your name?', validate: [ 'name' => 'required|min:3|max:255', ]); ``` + + +### Queue Interaction Testing + +_Queue interaction testing was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. + +Previously, attempting to test that a queued job was released, deleted, or manually failed was cumbersome and required the definition of custom queue fakes and stubs. However, in Laravel 11, you may easily test for these queue interactions using the `withFakeQueueInteractions` method: + +```php +use App\Jobs\ProcessPodcast; + +$job = (new ProcessPodcast)->withFakeQueueInteractions(); + +$job->handle(); + +$job->assertReleased(delay: 30); +``` + +For more information on testing queued jobs, check out the [queue documentation](/docs/{{version}}/queues#testing). From 0e06dff4e8288715256fcc84a04b1a6995e278dc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Feb 2024 13:10:23 -0600 Subject: [PATCH 1385/2609] wip --- releases.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/releases.md b/releases.md index 77f484b4d28..0e943b2bc6d 100644 --- a/releases.md +++ b/releases.md @@ -272,3 +272,15 @@ $job->assertReleased(delay: 30); ``` For more information on testing queued jobs, check out the [queue documentation](/docs/{{version}}/queues#testing). + +### New Artisan Commands + +_Class creation Artisan commands were contributed by [Taylor Otwell](https://github.com/taylorotwell)_. + +New Artisan commands have been added to allow the quick creation of classes, interfaces, and traits: + +```shell +php artisan make:class +php artisan make:interface +php artisan make:trait +``` From e20d02d996858ce94542311bb21c8f302775a307 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Feb 2024 16:55:28 -0600 Subject: [PATCH 1386/2609] wip --- queues.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 5010b3f9e47..26bd1166556 100644 --- a/queues.md +++ b/queues.md @@ -1263,7 +1263,9 @@ To dispatch a batch of jobs, you should use the `batch` method of the `Bus` faca new ImportCsv(201, 300), new ImportCsv(301, 400), new ImportCsv(401, 500), - ])->progress(function (Batch $batch) { + ])->before(function (Batch $batch) { + // The batch has been created but no jobs have been added... + })->progress(function (Batch $batch) { // A single job has completed successfully... })->then(function (Batch $batch) { // All jobs completed successfully... From 9e4a5cc9a717a811ba72830108c546dfefe8ffb4 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 21 Feb 2024 14:12:17 +0000 Subject: [PATCH 1387/2609] Fixes symfony docs versioning (#9372) --- artisan.md | 4 ++-- contracts.md | 2 +- mail.md | 2 +- processes.md | 2 +- prompts.md | 2 +- requests.md | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/artisan.md b/artisan.md index 701860c49bc..505dcd60998 100644 --- a/artisan.md +++ b/artisan.md @@ -648,8 +648,8 @@ Sometimes, you may need more manual control over how a progress bar is advanced. $bar->finish(); -> [!NOTE] -> For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html). +> [!NOTE] +> For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/7.0/components/console/helpers/progressbar.html). ## Registering Commands diff --git a/contracts.md b/contracts.md index 5038f111c9e..de852b6a7bb 100644 --- a/contracts.md +++ b/contracts.md @@ -11,7 +11,7 @@ Laravel's "contracts" are a set of interfaces that define the core services provided by the framework. For example, an `Illuminate\Contracts\Queue\Queue` contract defines the methods needed for queueing jobs, while the `Illuminate\Contracts\Mail\Mailer` contract defines the methods needed for sending e-mail. -Each contract has a corresponding implementation provided by the framework. For example, Laravel provides a queue implementation with a variety of drivers, and a mailer implementation that is powered by [Symfony Mailer](https://symfony.com/doc/6.0/mailer.html). +Each contract has a corresponding implementation provided by the framework. For example, Laravel provides a queue implementation with a variety of drivers, and a mailer implementation that is powered by [Symfony Mailer](https://symfony.com/doc/7.0/mailer.html). All of the Laravel contracts live in [their own GitHub repository](https://github.com/illuminate/contracts). This provides a quick reference point for all available contracts, as well as a single, decoupled package that may be utilized when building packages that interact with Laravel services. diff --git a/mail.md b/mail.md index e0304c55c08..7b86458206e 100644 --- a/mail.md +++ b/mail.md @@ -36,7 +36,7 @@ ## Introduction -Sending email doesn't have to be complicated. Laravel provides a clean, simple email API powered by the popular [Symfony Mailer](https://symfony.com/doc/6.2/mailer.html) component. Laravel and Symfony Mailer provide drivers for sending email via SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. +Sending email doesn't have to be complicated. Laravel provides a clean, simple email API powered by the popular [Symfony Mailer](https://symfony.com/doc/7.0/mailer.html) component. Laravel and Symfony Mailer provide drivers for sending email via SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. ### Configuration diff --git a/processes.md b/processes.md index 15666a6d86d..0ad4de84126 100644 --- a/processes.md +++ b/processes.md @@ -22,7 +22,7 @@ ## Introduction -Laravel provides an expressive, minimal API around the [Symfony Process component](https://symfony.com/doc/current/components/process.html), allowing you to conveniently invoke external processes from your Laravel application. Laravel's process features are focused on the most common use cases and a wonderful developer experience. +Laravel provides an expressive, minimal API around the [Symfony Process component](https://symfony.com/doc/7.0/components/process.html), allowing you to conveniently invoke external processes from your Laravel application. Laravel's process features are focused on the most common use cases and a wonderful developer experience. ## Invoking Processes diff --git a/prompts.md b/prompts.md index d17be47da33..b249e57c056 100644 --- a/prompts.md +++ b/prompts.md @@ -722,7 +722,7 @@ For any prompts that accept the `scroll` argument, the configured value will aut Laravel Prompts supports macOS, Linux, and Windows with WSL. Due to limitations in the Windows version of PHP, it is not currently possible to use Laravel Prompts on Windows outside of WSL. -For this reason, Laravel Prompts supports falling back to an alternative implementation such as the [Symfony Console Question Helper](https://symfony.com/doc/current/components/console/helpers/questionhelper.html). +For this reason, Laravel Prompts supports falling back to an alternative implementation such as the [Symfony Console Question Helper](https://symfony.com/doc/7.0/components/console/helpers/questionhelper.html). > [!NOTE] > When using Laravel Prompts with the Laravel framework, fallbacks for each prompt have been configured for you and will be automatically enabled in unsupported environments. diff --git a/requests.md b/requests.md index da6a9652362..1568c92e451 100644 --- a/requests.md +++ b/requests.md @@ -620,7 +620,7 @@ In addition to configuring the trusted proxies, you may also configure the proxy }) > [!NOTE] -> If you are using AWS Elastic Load Balancing, your `headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `headers` value, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). +> If you are using AWS Elastic Load Balancing, your `headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `headers` value, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/7.0/deployment/proxies.html). #### Trusting All Proxies From 4dcb6ca392f8c34d5042eb1b59bbc3be25b2d424 Mon Sep 17 00:00:00 2001 From: Michael <12945959+9mdv@users.noreply.github.com> Date: Wed, 21 Feb 2024 22:13:54 +0800 Subject: [PATCH 1388/2609] [10.x] Remove repeated words (#9368) * Update testing.md * Update testing.md --------- Co-authored-by: Taylor Otwell --- testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing.md b/testing.md index 69ac8ac9284..bb79c4ff2f7 100644 --- a/testing.md +++ b/testing.md @@ -22,7 +22,7 @@ An `ExampleTest.php` file is provided in both the `Feature` and `Unit` test dire ## Environment -When running tests, 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. +When running tests, 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 so that 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! From 3fba69012c2d4d3138245f9665825fe9dc3b2b42 Mon Sep 17 00:00:00 2001 From: Bryan te Beek Date: Wed, 21 Feb 2024 17:01:28 +0100 Subject: [PATCH 1389/2609] Update reverb.md --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index d9b678cbc89..0bfd34e9afb 100644 --- a/reverb.md +++ b/reverb.md @@ -26,7 +26,7 @@ ## Installation > **Warning** -> Laravel Reveb requires PHP 8.2+. +> Laravel Reverb requires PHP 8.2+. You may use the Composer package manager to install Reverb into your Laravel project. Since Reverb is currently in beta, you will need to explicitly install the beta release: From ac8ad150525538f8bfaec290bfcccc87515a08b7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 21 Feb 2024 11:04:33 -0600 Subject: [PATCH 1390/2609] working on upgrade guide --- upgrade.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/upgrade.md b/upgrade.md index 8a48ce73235..9703c4d95a7 100644 --- a/upgrade.md +++ b/upgrade.md @@ -9,6 +9,8 @@ - [Updating Dependencies](#updating-dependencies) - [Updating Minimum Stability](#updating-minimum-stability) +- [Modifying Columns](#modifying-columns) +- [Floating-Point Types](#floating-point-types) - [SQLite Minimum Version](#sqlite-minimum-version)
    @@ -18,8 +20,7 @@
    -- [Modifying Columns](#modifying-columns) -- [Floating-Point Types](#floating-point-types) +- [Per-Second Rate Limiting](#per-second-rate-limiting)
    @@ -29,6 +30,7 @@
    - [The `Enumerable` Contract](#the-enumerable-contract) +- [Eloquent Model `casts` Method](#eloquent-model-casts-method) - [Spatial Types](#spatial-types) - [Doctrine DBAL Removal](#doctrine-dbal-removal) @@ -38,7 +40,7 @@ ## Upgrading To 11.0 From 10.x -#### Estimated Upgrade Time: ?? Minutes +#### Estimated Upgrade Time: 15 Minutes > [!NOTE] > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. @@ -64,6 +66,29 @@ You should update the following dependencies in your application's `composer.jso In addition, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. + +### Authentication + +#### The `UserProvider` Contract + +The `Illuminate\Contracts\Auth\UserProvider` contract has received a new `rehashPasswordIfRequired` method. This method is responsible for re-hashing and storing the user's password in storage when the application's hashing algorithm work factor has changed. + +If your application or package defines a class that implements this interface, you should add the new `rehashPasswordIfRequired` method to your implementation. A reference implementation can be found within the `Illuminate\Auth\EloquentUserProvider` class: + +```php +public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false); +``` + + +### Cache + + +#### Cache Key Prefixes + +**Likelihood Of Impact: Very Low** + +Previously, if a cache key prefix was defined for the DynamoDB, Memcached, or Redis cache stores, Laravel would append a `:` to the prefix. In Laravel 11, the cache key prefix does not receive the `:` suffix. If you would like to maintain the previous prefixing behavior, you can manually add the `:` suffix to your cache key prefix. + ### Collections @@ -88,10 +113,17 @@ public function dump(...$args); If your application is utilizing an SQLite database, SQLite 3.35.0 or greater is required. + +#### Eloquent Model `casts` Method + +**Likelihood Of Impact: Low** + +The base Eloquent model class now defines a `casts` method in order to support the definition of attribute casts. If one of your application's models is defining a `casts` relationship, it may conflict with the `casts` method now present on the base Eloquent model class. + #### Modifying Columns -**Likelihood Of Impact: Medium** +**Likelihood Of Impact: High** When modifying a column, you must now explicitly include all the modifiers you want to keep on the column definition after it is changed. Any missing attributes will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column, even if those attributes have been assigned to the column by a previous migration: @@ -114,7 +146,7 @@ $table->char('postal_code', 10)->unique(false)->change(); #### Floating-Point Types -**Likelihood Of Impact: Medium** +**Likelihood Of Impact: High** The `double` and `float` migration column types have been rewritten to be consistent across all databases. @@ -208,3 +240,73 @@ Schema::connection('database')->hasTable('schema.table'); **Likelihood Of Impact: Very Low** The `Schema::getColumnType()` method now always returns actual type of the given column, not the Doctrine DBAL equivalent type. + + +#### Database Connection Interface + +**Likelihood Of Impact: Very Low** + +The `Illuminate\Database\ConnectionInterface` interface has received a new `scalar` method. If you are defining your own implementation of this interface, you should add the `scalar` method to your implementation: + +```php +public function scalar($query, $bindings = [], $useReadPdo = true); +``` + + +### Mail + + +#### The `Mailer` Contract + +**Likelihood Of Impact: Very Low** + +The `Illuminate\Contracts\Mail\Mailer` contract has received a new `sendNow` method. If your application or package is manually implementing this contract, you should add the new `sendNow` method to your implementation: + +```php +public function sendNow($mailable, array $data = [], $callback = null); +``` + + +### Queues + + +#### The `BatchRepository` Interface + +**Likelihood Of Impact: Very Low** + +The `Illuminate\Bus\BatchRepository` interface has received a new `rollBack` method. If you are implementing this interface within your own package or application, you should add this method to your implementation: + +```php +public function rollBack(); +``` + + +### Rate Limiting + + +#### Per-Second Rate Limiting + +**Likelihood Of Impact: Medium** + +Laravel 11 supports per-second rate limiting instead of being limited to per-minute granularity. There are a variety of potential breaking changes you should be aware of related to this change. + +The `GlobalLimit` class constructor now accepts seconds instead of minutes. This class is not documented and would not typically be used by your application: + +```php +new GlobalLimit($attempts, 2 * 60); +``` + +The `Limit` class constructor now accepts seconds instead of minutes. All documented usages of this class are limited to static constructors such as `Limit::perMinute` and `Limit::perSecond`. However, if you are instantiating this class manually, you should update your application to provide seconds to the class's constructor: + +```php +new Limit($key, $attempts, 2 * 60); +``` + +The `Limit` class's `decayMinutes` property has been renamed to `decaySeconds` and now contains seconds instead of minutes. + +The `Illuminate\Queue\Middleware\ThrottlesExceptions` and `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` class constructors now accept seconds instead of minutes: + +```php +new ThrottlesExceptions($attempts, 2 * 60); +new ThrottlesExceptionsWithRedis($attempts, 2 * 60); +``` From 2a50cf34e1dfad6db42db44a40de0273b9593caa Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 21 Feb 2024 11:18:21 -0600 Subject: [PATCH 1391/2609] adding to upgrade guide --- upgrade.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/upgrade.md b/upgrade.md index 9703c4d95a7..4bdc9fa7a86 100644 --- a/upgrade.md +++ b/upgrade.md @@ -9,6 +9,7 @@ - [Updating Dependencies](#updating-dependencies) - [Updating Minimum Stability](#updating-minimum-stability) +- [Application Structure](#application-structure) - [Modifying Columns](#modifying-columns) - [Floating-Point Types](#floating-point-types) - [SQLite Minimum Version](#sqlite-minimum-version) @@ -66,6 +67,13 @@ You should update the following dependencies in your application's `composer.jso In addition, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. + +### Application Structure + +Laravel 11 introduces a new default application structure with fewer default files. Namely, new Laravel applications contain fewer service providers, middleware, and configuration files. + +However, we do **not recommend** that Laravel 10 applications upgrading to Laravel 11 attempt to migrate their application structure, as Laravel 11 has been carefully tuned to also support the Laravel 10 application structure. + ### Authentication From 63fa5f361175777f6355410c6bd19abeaa3ef84a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 21 Feb 2024 11:36:36 -0600 Subject: [PATCH 1392/2609] note on upgrade --- upgrade.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/upgrade.md b/upgrade.md index 4bdc9fa7a86..264c2aa33bb 100644 --- a/upgrade.md +++ b/upgrade.md @@ -74,6 +74,12 @@ Laravel 11 introduces a new default application structure with fewer default fil However, we do **not recommend** that Laravel 10 applications upgrading to Laravel 11 attempt to migrate their application structure, as Laravel 11 has been carefully tuned to also support the Laravel 10 application structure. +If you choose to attempt to migrate your application's structure and also choose to remove your application's configuration files, you should install the following compatibility package via Composer to ensure that Laravel continues to function correctly: + +```shell +composer require laravel/previously-on-laravel-10 +``` + ### Authentication From ea659a39446475a1e09328b2b94496e9c0fe064c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 21 Feb 2024 11:37:08 -0600 Subject: [PATCH 1393/2609] wip --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 264c2aa33bb..8d8f8381468 100644 --- a/upgrade.md +++ b/upgrade.md @@ -74,7 +74,7 @@ Laravel 11 introduces a new default application structure with fewer default fil However, we do **not recommend** that Laravel 10 applications upgrading to Laravel 11 attempt to migrate their application structure, as Laravel 11 has been carefully tuned to also support the Laravel 10 application structure. -If you choose to attempt to migrate your application's structure and also choose to remove your application's configuration files, you should install the following compatibility package via Composer to ensure that Laravel continues to function correctly: +Nevertheless, if you choose to attempt to migrate your application's structure and also choose to remove your application's configuration files, you should install the following compatibility package via Composer to ensure that Laravel continues to function correctly: ```shell composer require laravel/previously-on-laravel-10 From b5517e0a640ce2fb8d14f8821107355088ee25a0 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 21 Feb 2024 19:03:21 +0000 Subject: [PATCH 1394/2609] [11.x] Adds `Health Check Endpoint` documentation (#9377) * Adds `Health Check Endpoint` documentation * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- deployment.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index 6fb3b810812..69744be6823 100644 --- a/deployment.md +++ b/deployment.md @@ -11,6 +11,7 @@ - [Caching Routes](#optimizing-route-loading) - [Caching Views](#optimizing-view-loading) - [Debug Mode](#debug-mode) +- [The Health Route](#the-health-route) - [Easy Deployment With Forge / Vapor](#deploying-with-forge-or-vapor) @@ -150,11 +151,27 @@ This command precompiles all your Blade views so they are not compiled on demand ## Debug Mode -The debug option in your config/app.php configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your application's `.env` file. +The debug option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your application's `.env` file. > [!WARNING] > **In your production environment, this value should always be `false`. If the `APP_DEBUG` variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** + +## The Health Route + +Laravel includes a built-in health check route that can be used to monitor the status of your application. In production, this route may be used to report the status of your application to an uptime monitor, load balancer, or orchestration system such as Kubernetes. + +By default, the health check route is served at `/up` and will return a 200 HTTP response if the application has booted without exceptions. Otherwise, a 500 HTTP response will be returned. You may configure the URI for this route in your application's `bootstrap/app` file: + + ->withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', // [tl! remove] + health: '/status', // [tl! add] + ) + +When HTTP requests are made to this route, Laravel will also dispatch a `Illuminate\Foundation\Events\DiagnosingHealth` event, allowing you to perform additional health checks relevant to your application. Within a [listener](/docs/{{version}}/events) for this event, you may check your application's database or cache status. If you detect a problem with your application, you may simply throw an exception from the listener. + ## Easy Deployment With Forge / Vapor From 074db52aaba0b975c2901d989d98b2a61ffe8dd5 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 21 Feb 2024 19:04:00 +0000 Subject: [PATCH 1395/2609] Documents `literal` helper (#9374) --- helpers.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/helpers.md b/helpers.md index c30079a623c..4aca7f1b86c 100644 --- a/helpers.md +++ b/helpers.md @@ -167,6 +167,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [fake](#method-fake) [filled](#method-filled) [info](#method-info) +[literal](#method-literal) [logger](#method-logger) [method_field](#method-method-field) [now](#method-now) @@ -1785,6 +1786,19 @@ An array of contextual data may also be passed to the function: info('User login attempt failed.', ['id' => $user->id]); + +#### `literal()` {.collection-method} + +"The `literal` function creates a new [stdClass](https://www.php.net/manual/en/class.stdclass.php) instance with the given named arguments as properties: + + $obj = literal( + name: 'Joe', + languages: ['PHP', 'Ruby'], + ); + + $obj->name; // 'Joe' + $obj->languages; // ['PHP', 'Ruby'] + #### `logger()` {.collection-method} From 7046ba72f357c5e9577ab654ede3f9a9c30f1841 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 21 Feb 2024 19:11:23 +0000 Subject: [PATCH 1396/2609] [11.x] Documents `once` helper (#9371) * Adds `once` documentation Co-Authored-By: Milwad <98118400+milwad-dev@users.noreply.github.com> * Update helpers.md * formatting --------- Co-authored-by: Milwad <98118400+milwad-dev@users.noreply.github.com> Co-authored-by: Taylor Otwell --- helpers.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/helpers.md b/helpers.md index 4aca7f1b86c..00a46bd98a2 100644 --- a/helpers.md +++ b/helpers.md @@ -172,6 +172,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [method_field](#method-method-field) [now](#method-now) [old](#method-old) +[once](#method-once) [optional](#method-optional) [policy](#method-policy) [redirect](#method-redirect) @@ -1847,6 +1848,45 @@ Since the "default value" provided as the second argument to the `old` function {{ old('name', $user) }} + +#### `once()` {.collection-method} + +The `once` function executes the given callback and caches the result in memory for the duration of the request. Any subsequent calls to the `once` function with the same callback will return the previously cached result: + + function random(): int + { + return once(function () { + return random_int(1, 1000); + }); + } + + random(); // 123 + random(); // 123 (cached result) + random(); // 123 (cached result) + +When the `once` function is executed from within an object instance, the cached result will be unique to that object instance: + +```php + [1, 2, 3]); + } +} + +$service = new NumberService; + +$service->all(); +$service->all(); // (cached result) + +$secondService = new NumberService; + +$secondService->all(); +$secondService->all(); // (cached result) +``` #### `optional()` {.collection-method} From 90bf98fe2cc579dbcf4506f840fd513c9fc8bad6 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 21 Feb 2024 19:13:34 +0000 Subject: [PATCH 1397/2609] [11.x] Rewrites `artisan` for L11 (#9362) * Rewrites `artisan` * Adjusts * formatting --------- Co-authored-by: Taylor Otwell --- artisan.md | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/artisan.md b/artisan.md index 505dcd60998..31dbe1f4e97 100644 --- a/artisan.md +++ b/artisan.md @@ -161,17 +161,9 @@ Let's take a look at an example command. Note that we are able to request any de ### 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. - /** - * Register the closure based commands for the application. - */ - protected function commands(): void - { - 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 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 command's arguments and options: +Even though the `routes/console.php` file 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 command's arguments and options: Artisan::command('mail:send {user}', function (string $user) { $this->info("Sending email to: {$user}!"); @@ -416,9 +408,9 @@ If Laravel needs to gather a required argument from the user, it will automatica /** * Prompt for missing input arguments using the returned questions. * - * @return array + * @return array */ - protected function promptForMissingArgumentsUsing() + protected function promptForMissingArgumentsUsing(): array { return [ 'user' => 'Which user ID should receive the mail?', @@ -461,12 +453,8 @@ If you wish to prompt the user to select or enter [options](#options), you may i /** * Perform actions after the user was prompted for missing arguments. - * - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output - * @return void */ - protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void { $input->setOption('queue', confirm( label: 'Would you like to queue the mail?', @@ -654,24 +642,21 @@ Sometimes, you may need more manual control over how a progress bar is advanced. ## Registering 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: +By default, Laravel automatically registers all commands within the `app/Console/Commands` directory. However, you can instruct Laravel to scan other directories for Artisan commands using the `withCommands` method in your application's `bootstrap/app.php` file: - /** - * Register the commands for the application. - */ - protected function commands(): void - { - $this->load(__DIR__.'/Commands'); - $this->load(__DIR__.'/../Domain/Orders/Commands'); + ->withCommands([ + __DIR__.'../app/Domain/Orders/Commands', + ]) - // ... - } +If necessary, you may also manually register commands by providing the command's class name to the `withCommands` method: -If necessary, you may manually register commands by adding the command's class name to a `$commands` property within your `App\Console\Kernel` class. If this property is not already defined on your kernel, you should define it manually. When Artisan boots, all the commands listed in this property will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan: + use App\Domain\Orders\Commands\SendEmails; - protected $commands = [ - Commands\SendEmails::class - ]; + ->withCommands([ + SendEmails::class, + ]) + + When Artisan boots, all the commands in your application will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan. ## Programmatically Executing Commands From fcbf240ad30b5db3145f4e39bf8c4cb6c57ac28a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 21 Feb 2024 19:20:23 +0000 Subject: [PATCH 1398/2609] [11.x] Rewrites `logging.php` in L11 (#9361) * Rewrites `logging.php` * formatting --------- Co-authored-by: Taylor Otwell --- logging.md | 81 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/logging.md b/logging.md index c63f8d868c9..b675ad2f4d2 100644 --- a/logging.md +++ b/logging.md @@ -30,25 +30,20 @@ Under the hood, Laravel utilizes the [Monolog](https://github.com/Seldaek/monolo ## Configuration -All of the configuration options for your application's logging behavior are housed in the `config/logging.php` configuration file. This file allows you to configure your application's log channels, so be sure to review each of the available channels and their options. We'll review a few common options below. +By default, Laravel uses the `stack` channel when logging messages. The `stack` channel is used to [aggregate multiple log channels](#building-log-stacks) into a single channel. -By default, Laravel will use the `stack` channel when logging messages. The `stack` channel is used to aggregate multiple log channels into a single channel. For more information on building stacks, check out the [documentation below](#building-log-stacks). +You can specify your application's log driver using the `LOG_DRIVER` environment variable. However, to customize some of the configuration options documented below that are not available via environment variables, you should publish Laravel's complete `logging` configuration file using the `config:publish` Artisan command: - -#### Configuring the Channel Name +``` +php artisan config:publish logging +``` -By default, Monolog is instantiated with a "channel name" that matches the current environment, such as `production` or `local`. To change this value, add a `name` option to your channel's configuration: - - 'stack' => [ - 'driver' => 'stack', - 'name' => 'channel-name', - 'channels' => ['single', 'slack'], - ], +If you choose to not publish the configuration file, you may wish to review the `vendor/laravel/framework/config/logging.php` file to understand all of the available logging channels and their options. ### Available Channel Drivers -Each log channel is powered by a "driver". The driver determines how and where the log message is actually recorded. The following log channel drivers are available in every Laravel application. An entry for most of these drivers is already present in your application's `config/logging.php` configuration file, so be sure to review this file to become familiar with its contents: +Each log channel is powered by a "driver". The driver determines how and where the log message is actually recorded. The following log channel drivers are available in every Laravel application:
    @@ -69,6 +64,17 @@ Name | Description > [!NOTE] > Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. + +#### Configuring the Channel Name + +By default, Monolog is instantiated with a "channel name" that matches the current environment, such as `production` or `local`. To change this value, you may add a `name` option to your channel's configuration: + + 'stack' => [ + 'driver' => 'stack', + 'name' => 'channel-name', + 'channels' => ['single', 'slack'], + ], + ### Channel Prerequisites @@ -87,7 +93,7 @@ Name | Description | Default
    -Additionally, the retention policy for the `daily` channel can be configured via the `days` option: +Additionally, the retention policy for the `daily` channel can be configured via the `LOG_DAILY_DAYS` environment variable or by setting the `days` configuration option.
    @@ -100,19 +106,19 @@ Name | Description | Defau #### Configuring the Papertrail Channel -The `papertrail` channel requires the `host` and `port` configuration options. You can obtain these values from [Papertrail](https://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-php-apps/#send-events-from-php-app). +The `papertrail` channel requires `host` and `port` configuration options. These may be defined via the `LOG_PAPERTRAIL_URL` and `LOG_PAPERTRAIL_PORT` environment variables. You can obtain these values from [Papertrail](https://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-php-apps/#send-events-from-php-app). #### Configuring the Slack Channel -The `slack` channel requires a `url` configuration option. This URL should match a URL for an [incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) that you have configured for your Slack team. +The `slack` channel requires a `url` configuration option. This value may be defined via the `LOG_SLACK_WEBHOOK_URL` environment variable. This URL should match a URL for an [incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) that you have configured for your Slack team. -By default, Slack will only receive logs at the `critical` level and above; however, you can adjust this in your `config/logging.php` configuration file by modifying the `level` configuration option within your Slack log channel's configuration array. +By default, Slack will only receive logs at the `critical` level and above; however, you can adjust this using the `LOG_LEVEL` environment variable or by modifying the `level` configuration option within your Slack log channel's configuration array. ### Logging Deprecation Warnings -PHP, Laravel, and other libraries often notify their users that some of their features have been deprecated and will be removed in a future version. If you would like to log these deprecation warnings, you may specify your preferred `deprecations` log channel in your application's `config/logging.php` configuration file: +PHP, Laravel, and other libraries often notify their users that some of their features have been deprecated and will be removed in a future version. If you would like to log these deprecation warnings, you may specify your preferred `deprecations` log channel using the `LOG_DEPRECATIONS_CHANNEL` environment variable, or within your application's `config/logging.php` configuration file: 'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), @@ -134,25 +140,31 @@ Or, you may define a log channel named `deprecations`. If a log channel with thi As mentioned previously, the `stack` driver allows you to combine multiple channels into a single log channel for convenience. To illustrate how to use log stacks, let's take a look at an example configuration that you might see in a production application: - 'channels' => [ - 'stack' => [ - 'driver' => 'stack', - 'channels' => ['syslog', 'slack'], - ], +```php +'channels' => [ + 'stack' => [ + 'driver' => 'stack', + 'channels' => ['syslog', 'slack'], // [tl! add] + 'ignore_exceptions' => false, + ], - 'syslog' => [ - 'driver' => 'syslog', - 'level' => 'debug', - ], + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], - 'slack' => [ - 'driver' => 'slack', - 'url' => env('LOG_SLACK_WEBHOOK_URL'), - 'username' => 'Laravel Log', - 'emoji' => ':boom:', - 'level' => 'critical', - ], + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, ], +], +``` Let's dissect this configuration. First, notice our `stack` channel aggregates two other channels via its `channels` option: `syslog` and `slack`. So, when logging messages, both of these channels will have the opportunity to log the message. However, as we will see below, whether these channels actually log the message may be determined by the message's severity / "level". @@ -339,7 +351,8 @@ To get started, define a `tap` array on the channel's configuration. The `tap` a 'driver' => 'single', 'tap' => [App\Logging\CustomizeFormatter::class], 'path' => storage_path('logs/laravel.log'), - 'level' => 'debug', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, ], Once you have configured the `tap` option on your channel, you're ready to define the class that will customize your Monolog instance. This class only needs a single method: `__invoke`, which receives an `Illuminate\Log\Logger` instance. The `Illuminate\Log\Logger` instance proxies all method calls to the underlying Monolog instance: From e22f2dff94009831d3a078e97143b9c3ed31dc72 Mon Sep 17 00:00:00 2001 From: Vimal Gorasiya Date: Thu, 22 Feb 2024 00:53:44 +0530 Subject: [PATCH 1399/2609] Update helpers.md (#9369) * Update helpers.md Reordered `Number` helper methods and added `Number::clamp` method which is missing from docs. * formatting --------- Co-authored-by: Taylor Otwell --- helpers.md | 96 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/helpers.md b/helpers.md index 3615e1ea383..e48d891d14f 100644 --- a/helpers.md +++ b/helpers.md @@ -90,12 +90,13 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct
    [Number::abbreviate](#method-number-abbreviate) -[Number::format](#method-number-format) -[Number::percentage](#method-number-percentage) +[Number::clamp](#method-number-clamp) [Number::currency](#method-number-currency) [Number::fileSize](#method-number-file-size) [Number::forHumans](#method-number-for-humans) +[Number::format](#method-number-format) [Number::ordinal](#method-number-ordinal) +[Number::percentage](#method-number-percentage) [Number::spell](#method-number-spell) [Number::useLocale](#method-number-use-locale) [Number::withLocale](#method-number-with-locale) @@ -1145,51 +1146,28 @@ The `Number::abbreviate` method returns the human-readable format of the provide // 1.23M - -#### `Number::format()` {.collection-method} - -The `Number::format` method formats the given number into a locale specific string: - - use Illuminate\Support\Number; - - $number = Number::format(100000); - - // 100,000 - - $number = Number::format(100000, precision: 2); - - // 100,000.00 - - $number = Number::format(100000.123, maxPrecision: 2); - - // 100,000.12 - - $number = Number::format(100000, locale: 'de'); - - // 100.000 + +#### `Number::clamp()` {.collection-method} - -#### `Number::percentage()` {.collection-method} - -The `Number::percentage` method returns the percentage representation of the given value as a string: +The `Number::clamp` method ensures a given number stays within a specified range. If the number is lower than the minimum, the minimum value is returned. If the number is higher than the maximum, the maximum value is returned: use Illuminate\Support\Number; - $percentage = Number::percentage(10); + $number = Number::clamp(105, min: 10, max: 100); - // 10% + // 100 - $percentage = Number::percentage(10, precision: 2); + $number = Number::clamp(5, min: 10, max: 100); - // 10.00% + // 10 - $percentage = Number::percentage(10.123, maxPrecision: 2); + $number = Number::clamp(10, min: 10, max: 100); - // 10.12% + // 10 - $percentage = Number::percentage(10, precision: 2, locale: 'de'); + $number = Number::clamp(20, min: 10, max: 100); - // 10,00% + // 20 #### `Number::currency()` {.collection-method} @@ -1248,6 +1226,29 @@ The `Number::forHumans` method returns the human-readable format of the provided // 1.23 million + +#### `Number::format()` {.collection-method} + +The `Number::format` method formats the given number into a locale specific string: + + use Illuminate\Support\Number; + + $number = Number::format(100000); + + // 100,000 + + $number = Number::format(100000, precision: 2); + + // 100,000.00 + + $number = Number::format(100000.123, maxPrecision: 2); + + // 100,000.12 + + $number = Number::format(100000, locale: 'de'); + + // 100.000 + #### `Number::ordinal()` {.collection-method} @@ -1267,6 +1268,29 @@ The `Number::ordinal` method returns a number's ordinal representation: // 21st + +#### `Number::percentage()` {.collection-method} + +The `Number::percentage` method returns the percentage representation of the given value as a string: + + use Illuminate\Support\Number; + + $percentage = Number::percentage(10); + + // 10% + + $percentage = Number::percentage(10, precision: 2); + + // 10.00% + + $percentage = Number::percentage(10.123, maxPrecision: 2); + + // 10.12% + + $percentage = Number::percentage(10, precision: 2, locale: 'de'); + + // 10,00% + #### `Number::spell()` {.collection-method} From ccff0aa0328b442737ed2626ec4c712f5f6c779f Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 22 Feb 2024 23:43:00 +0900 Subject: [PATCH 1400/2609] [11.x] Add a description for deployment optimization (#9383) * [11.x] Add description to the deployment optimization * Update deployment.md --------- Co-authored-by: Taylor Otwell --- deployment.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/deployment.md b/deployment.md index 69744be6823..f5ac7b8a103 100644 --- a/deployment.md +++ b/deployment.md @@ -5,7 +5,6 @@ - [Server Configuration](#server-configuration) - [Nginx](#nginx) - [Optimization](#optimization) - - [Autoloader Optimization](#autoloader-optimization) - [Caching Configuration](#optimizing-configuration-loading) - [Caching Events](#caching-events) - [Caching Routes](#optimizing-route-loading) @@ -91,18 +90,6 @@ server { ## Optimization - -### Autoloader Optimization - -When deploying to production, make sure that you are optimizing Composer's class autoloader map so Composer can quickly find the proper file to load for a given class: - -```shell -composer install --no-dev -``` - -> [!NOTE] -> In addition to optimizing the autoloader, you should always be sure to include a `composer.lock` file in your project's source control repository. Your project's dependencies can be installed much faster when a `composer.lock` file is present. - ### Caching Configuration From 5606937fba98f1ffc0f238d540bd5fe57913e08a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 22 Feb 2024 14:43:24 +0000 Subject: [PATCH 1401/2609] [11.x] Fixes session table command (#9381) * Update session.md * Update session.md --- session.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/session.md b/session.md index 50018ba57d7..f387baf7dda 100644 --- a/session.md +++ b/session.md @@ -54,10 +54,10 @@ Laravel includes a variety of great session drivers: #### Database -When using the `database` session driver, you will need to ensure that you have a database table to contain the session data. Typically, this is included in Laravel's default `0001_01_01_000000_create_users_table.php` [database migration](/docs/{{version}}/migrations); however, if for any reason you do not have a `sessions` table, you may use the `session:table` Artisan command to generate this migration: +When using the `database` session driver, you will need to ensure that you have a database table to contain the session data. Typically, this is included in Laravel's default `0001_01_01_000000_create_users_table.php` [database migration](/docs/{{version}}/migrations); however, if for any reason you do not have a `sessions` table, you may use the `make:session-table` Artisan command to generate this migration: ```shell -php artisan session:table +php artisan make:session-table php artisan migrate ``` From 513425ed61ca0d9dd12155ca2607cfbdc7e4e247 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 22 Feb 2024 08:50:11 -0600 Subject: [PATCH 1402/2609] update table commands --- cache.md | 4 ++-- notifications.md | 4 ++-- queues.md | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache.md b/cache.md index 95db89b7a3d..6334909b6a2 100644 --- a/cache.md +++ b/cache.md @@ -47,7 +47,7 @@ When using the `database` cache driver, you will need to set up a table to conta }); > [!NOTE] -> You may also use the `php artisan cache:table` Artisan command to generate a migration with the proper schema. +> You may also use the `php artisan make:cache-table` Artisan command to generate a migration with the proper schema. #### Memcached @@ -283,7 +283,7 @@ When using the `database` cache driver, you will need to setup a table to contai }); > [!NOTE] -> If you used the `cache:table` Artisan command to create the database driver's cache table, the migration created by that command already includes a definition for the `cache_locks` table. +> If you used the `make:cache-table` Artisan command to create the database driver's cache table, the migration created by that command already includes a definition for the `cache_locks` table. ### Managing Locks diff --git a/notifications.md b/notifications.md index 39004ee7413..8981f16c8e2 100644 --- a/notifications.md +++ b/notifications.md @@ -808,10 +808,10 @@ To customize the theme for an individual notification, you may call the `theme` 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](/docs/{{version}}/migrations) 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 `make:notifications-table` command to generate a [migration](/docs/{{version}}/migrations) with the proper table schema: ```shell -php artisan notifications:table +php artisan make:notifications-table php artisan migrate ``` diff --git a/queues.md b/queues.md index bec7bfb8296..f3a268a387b 100644 --- a/queues.md +++ b/queues.md @@ -92,10 +92,10 @@ php artisan queue:work --queue=high,default #### Database -In order to use the `database` queue driver, you will need a database table to hold the jobs. To generate a migration that creates this table, run the `queue:table` Artisan command. Once the migration has been created, you may migrate your database using the `migrate` command: +In order to use the `database` queue driver, you will need a database table to hold the jobs. To generate a migration that creates this table, run the `make:queue-table` Artisan command. Once the migration has been created, you may migrate your database using the `migrate` command: ```shell -php artisan queue:table +php artisan make:queue-table php artisan migrate ``` @@ -1204,10 +1204,10 @@ If you would like to mark your job as failed because of an exception that you ha ## 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 which will contain meta information about your job batches, such as their completion percentage. 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 which will contain meta information about your job batches, such as their completion percentage. This migration may be generated using the `make:queue-batches-table` Artisan command: ```shell -php artisan queue:batches-table +php artisan make:queue-batches-table php artisan migrate ``` @@ -1807,10 +1807,10 @@ For more information on Supervisor, consult the [Supervisor documentation](http: 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 an asynchronous job has exceeded this number of attempts, it will be inserted into the `failed_jobs` database table. [Synchronously dispatched jobs](/docs/{{version}}/queues#synchronous-dispatching) that fail are not stored in this table and their exceptions are immediately handled by the application. -A migration to create the `failed_jobs` table is typically already present in new Laravel applications. However, if your application does not contain a migration for this table, you may use the `queue:failed-table` command to create the migration: +A migration to create the `failed_jobs` table is typically already present in new Laravel applications. However, if your application does not contain a migration for this table, you may use the `make:queue-failed-table` command to create the migration: ```shell -php artisan queue:failed-table +php artisan make:queue-failed-table php artisan migrate ``` From 708b981017881098cce08fc1356708aeb78c038d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 22 Feb 2024 14:50:39 +0000 Subject: [PATCH 1403/2609] Uses link while first mentioning environment variable (#9382) --- session.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.md b/session.md index f387baf7dda..57eca3ee7c0 100644 --- a/session.md +++ b/session.md @@ -26,7 +26,7 @@ Laravel ships with a variety of session backends that are accessed through an ex By default, Laravel is configured to use the `database` session driver, which stores user session information in your application's database and will work well for many production applications. -Laravel's complete `session.php` configuration file is not published by default, as you can specify your application's session driver using the `SESSION_DRIVER` environment variable. However, if necessary, you may publish the configuration file using the `config:publish` Artisan command: +Laravel's complete `session.php` configuration file is not published by default, as you can specify your application's session driver using the `SESSION_DRIVER` [environment variable](/docs/{{version}}/configuration#environment-configuration). However, if necessary, you may publish the configuration file using the `config:publish` Artisan command: ```shell php artisan config:publish session From 2a3744be40f25124d750edfc81b25946de67605c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 22 Feb 2024 15:33:23 +0000 Subject: [PATCH 1404/2609] [11.x] Rewrites `cache` for L11 (#9385) * Rewrites `cache.php` * formatting --------- Co-authored-by: Taylor Otwell --- cache.md | 102 +++++++++++++++++++------------------------------------ 1 file changed, 35 insertions(+), 67 deletions(-) diff --git a/cache.md b/cache.md index 6334909b6a2..0be0f2fc416 100644 --- a/cache.md +++ b/cache.md @@ -10,7 +10,6 @@ - [Removing Items From the Cache](#removing-items-from-the-cache) - [The Cache Helper](#the-cache-helper) - [Atomic Locks](#atomic-locks) - - [Driver Prerequisites](#lock-driver-prerequisites) - [Managing Locks](#managing-locks) - [Managing Locks Across Processes](#managing-locks-across-processes) - [Adding Custom Cache Drivers](#adding-custom-cache-drivers) @@ -28,9 +27,17 @@ Thankfully, Laravel provides an expressive, unified API for various cache backen ## Configuration -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. +Laravel's complete `cache.php` configuration file is not published by default, as you can specify your application's cache driver using the `CACHE_DRIVER` [environment variable](/docs/{{version}}/configuration#environment-configuration). -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. +However, to customize some of the configuration options documented below that are not available via environment variables, you may need to publish the configuration file using the `config:publish` Artisan command: + +```shell +php artisan config:publish cache +``` + +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. + +By default, Laravel is configured to use the `database` cache driver, which stores the serialized, cached objects in your application's database. ### Driver Prerequisites @@ -38,16 +45,13 @@ The cache configuration file also contains various other options, which are docu #### Database -When using the `database` cache driver, you will need to set up a table to contain the cache items. You'll find an example `Schema` declaration for the table below: +When using the `database` session driver, you will need a database table to contain the cache data. Typically, this is included in Laravel's default `0001_01_01_000001_create_cache_table.php` [database migration](/docs/{{version}}/migrations); however, if your application does not contain this migration, you may use the `make:cache-table` Artisan command to create it: - Schema::create('cache', function (Blueprint $table) { - $table->string('key')->unique(); - $table->text('value'); - $table->integer('expiration'); - }); +```shell +php artisan make:cache-table -> [!NOTE] -> You may also use the `php artisan make:cache-table` Artisan command to generate a migration with the proper schema. +php artisan migrate +``` #### Memcached @@ -55,6 +59,8 @@ When using the `database` cache driver, you will need to set up a table to conta 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' => [ + // ... + 'servers' => [ [ 'host' => env('MEMCACHED_HOST', '127.0.0.1'), @@ -67,24 +73,28 @@ Using the Memcached driver requires the [Memcached PECL package](https://pecl.ph 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' => [ - [ - 'host' => '/var/run/memcached/memcached.sock', - 'port' => 0, - 'weight' => 100 + // ... + + 'servers' => [ + [ + 'host' => '/var/run/memcached/memcached.sock', + 'port' => 0, + 'weight' => 100 + ], ], ], #### 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. [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. +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 (~2.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). #### DynamoDB -Before using the [DynamoDB](https://aws.amazon.com/dynamodb) cache driver, you must create a DynamoDB table to store all of the cached data. Typically, this table should be named `cache`. However, you should name the table based on the value of the `stores.dynamodb.table` configuration value within your application's `cache` configuration file. +Before using the [DynamoDB](https://aws.amazon.com/dynamodb) cache driver, you must create a DynamoDB table to store all of the cached data. Typically, this table should be named `cache`. However, you should name the table based on the value of the `stores.dynamodb.table` configuration value within the `cache` configuration file. The table name may also be set via the `DYNAMODB_CACHE_TABLE` environment variable. This table should also have a string partition key with a name that corresponds to the value of the `stores.dynamodb.attributes.key` configuration item within your application's `cache` configuration file. By default, the partition key should be named `key`. @@ -268,23 +278,6 @@ When the `cache` function is called without any arguments, it returns an instanc > [!WARNING] > To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. - -### Driver Prerequisites - - -#### Database - -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 (Blueprint $table) { - $table->string('key')->primary(); - $table->string('owner'); - $table->integer('expiration'); - }); - -> [!NOTE] -> If you used the `make:cache-table` Artisan command to create the database driver's cache table, the migration created by that command already includes a definition for the `cache_locks` table. - ### Managing Locks @@ -427,41 +420,16 @@ To register the custom cache driver with Laravel, we will use the `extend` metho 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. +Once your extension is registered, update the `CACHE_DRIVER` environment variable or `driver` option within your application's `config/cache.php` configuration file 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 application's `App\Providers\EventServiceProvider` class: - - use App\Listeners\LogCacheHit; - use App\Listeners\LogCacheMissed; - use App\Listeners\LogKeyForgotten; - use App\Listeners\LogKeyWritten; - use Illuminate\Cache\Events\CacheHit; - use Illuminate\Cache\Events\CacheMissed; - use Illuminate\Cache\Events\KeyForgotten; - use Illuminate\Cache\Events\KeyWritten; - - /** - * The event listener mappings for the application. - * - * @var array - */ - protected $listen = [ - CacheHit::class => [ - LogCacheHit::class, - ], - - CacheMissed::class => [ - LogCacheMissed::class, - ], +To execute code on every cache operation, you may listen for various [events](/docs/{{version}}/events) dispatched by the cache: - KeyForgotten::class => [ - LogKeyForgotten::class, - ], - - KeyWritten::class => [ - LogKeyWritten::class, - ], - ]; +Event Name | +------------- | +`Illuminate\Cache\Events\CacheHit` | +`Illuminate\Cache\Events\CacheMissed` | +`Illuminate\Cache\Events\KeyForgotten` | +`Illuminate\Cache\Events\KeyWritten` | From 48faaaff53e6cb72a10fb8ce94e4fabfa4657ee6 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 22 Feb 2024 15:59:56 +0000 Subject: [PATCH 1405/2609] [11.x] Rewrites `broadcasting` for L11 (#9379) * [11.x] Rewrites `broadcasting` for L11 * Update broadcasting.md * formatting * formatting * fixes echo file * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- broadcasting.md | 142 +++++++++++++++++------------------------------- 1 file changed, 49 insertions(+), 93 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 235ac802dc7..23f23abb744 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -6,7 +6,6 @@ - [Reverb](#reverb) - [Pusher Channels](#pusher-channels) - [Ably](#ably) - - [Open Source Alternatives](#open-source-alternatives) - [Client Side Installation](#client-side-installation) - [Reverb](#client-reverb) - [Pusher Channels](#client-pusher-channels) @@ -69,22 +68,34 @@ Event broadcasting is accomplished by a server-side broadcasting driver that bro ### 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: [Laravel Reverb](/docs/{{version}}/reverb), [Pusher Channels](https://pusher.com/channels), [Ably](https://ably.com), [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. +Laravel's complete `broadcasting.php` configuration file is not published by default, as you can specify your application's broadcast driver using the `BROADCAST_CONNECTION` [environment variable](/docs/{{version}}/configuration#environment-configuration). However, to customize some of the configuration options documented below that are not available via environment variables, you may need to publish the `broadcasting.php` configuration file using the `config:publish` Artisan command: - -#### Broadcast Service Provider +```shell +php artisan config:publish broadcasting +``` + +Laravel supports several broadcast drivers: [Laravel Reverb](/docs/{{version}}/reverb), [Pusher Channels](https://pusher.com/channels), [Ably](https://ably.com), and a `log` driver for local development and debugging. Additionally, a `null` driver is included which allows you to totally disable broadcasting during testing. + + +#### Installation -Before broadcasting any events, you will first need to register the `App\Providers\BroadcastServiceProvider`. In 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. +by default, broadcasting is not enabled in new Laravel applications. You may enable broadcasting using the `install:broadcasting` Artisan command: + +```shell +php artisan install:broadcasting +``` + +The `install:broadcasting` command will create a `routes/channels.php` file where you may register your application's broadcast authorization routes and callbacks. #### Queue Configuration -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. +Before broadcasting any events, you should first 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. ### Reverb -You may use the Composer package manager to install Laravel Reverb into your Laravel project. Since Reverb is currently in beta, you will need to explicitly install the beta release: +When running the `install:broadcasting` command, you will be prompted to install [Laravel Reverb](/docs/{{version}}/reverb). Of course, you may also install Reverb manually using the Composer package manager. Since Reverb is currently in beta, you will need to explicitly install the beta release: ```sh composer require laravel/reverb:@beta @@ -107,30 +118,26 @@ If you plan to broadcast your events using [Pusher Channels](https://pusher.com/ 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, these values should be set via the `PUSHER_APP_KEY`, `PUSHER_APP_SECRET`, and `PUSHER_APP_ID` [environment variables](/docs/{{version}}/configuration#environment-configuration): +Next, you should configure your Pusher Channels credentials in your application's `.env` file: ```ini PUSHER_APP_ID=your-pusher-app-id PUSHER_APP_KEY=your-pusher-key PUSHER_APP_SECRET=your-pusher-secret +PUSHER_HOST= +PUSHER_PORT=443 +PUSHER_SCHEME=https PUSHER_APP_CLUSTER=mt1 ``` -The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Channels, such as the cluster. - -Next, you will need to change your broadcast driver to `pusher` in your `.env` file: +Next, you should set the `BROADCAST_CONNECTION` environment variable's value to `pusher` in your application's `.env` file: ```ini -BROADCAST_DRIVER=pusher +BROADCAST_CONNECTION=pusher ``` Finally, you are ready to install and configure [Laravel Echo](#client-side-installation), which will receive the broadcast events on the client-side. - -#### Open Source Pusher Alternatives - -[soketi](https://docs.soketi.app/) provides a Pusher compatible WebSocket server for Laravel, allowing you to leverage the full power of Laravel broadcasting without a commercial WebSocket provider. For more information on installing and using open source packages for broadcasting, please consult our documentation on [open source alternatives](#open-source-alternatives). - ### Ably @@ -143,28 +150,20 @@ If you plan to broadcast your events using [Ably](https://ably.com), you should composer require ably/ably-php ``` -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): +Next, you should configure your Ably credentials via the `ABLY_KEY` environment variable in your application's `.env` file: ```ini ABLY_KEY=your-ably-key ``` -Next, you will need to change your broadcast driver to `ably` in your `.env` file: +Next, you should set the `BROADCAST_CONNECTION` environment variable's value to `ably` in your application's `.env` file: ```ini -BROADCAST_DRIVER=ably +BROADCAST_CONNECTION=ably ``` Finally, you are ready to install and configure [Laravel Echo](#client-side-installation), which will receive the broadcast events on the client-side. - -### Open Source Alternatives - - -#### Node - -[Soketi](https://github.com/soketi/soketi) is a Node based, Pusher compatible WebSocket server for Laravel. Under the hood, Soketi utilizes µWebSockets.js for extreme scalability and speed. 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://docs.soketi.app/). - ## Client Side Installation @@ -177,12 +176,12 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst 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: +Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. The `install:broadcasting` Artisan command creates a `resources/js/echo.js` file that handles this for you: ```js import Echo from 'laravel-echo'; -import Pusher from 'pusher-js'; +import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ @@ -196,6 +195,15 @@ window.Echo = new Echo({ }); ``` +Next, you only need to compile your application's assets: + +```shell +npm run build +``` + +> [!NOTE] +> To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). + ### Pusher Channels @@ -205,12 +213,12 @@ window.Echo = new Echo({ 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: +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 within the `resources/js/echo.js` file that was created by the `install:broadcasting` Artisan command. By default, an example Echo configuration is already included in this file; however, the default configuration in the `echo.js` file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Pusher: ```js import Echo from 'laravel-echo'; -import Pusher from 'pusher-js'; +import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ @@ -221,7 +229,7 @@ window.Echo = new Echo({ }); ``` -Once you have uncommented and adjusted the Echo configuration according to your needs, you may compile your application's assets: +Once you have adjusted the Echo configuration according to your application's needs, you may compile your application's assets: ```shell npm run build @@ -266,12 +274,12 @@ 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: +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 within the `resources/js/echo.js` file that was created by the `install:broadcasting` Artisan command. By default, an example Echo configuration is already included in this file; however, the default configuration in the `echo.js` file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Ably: ```js import Echo from 'laravel-echo'; -import Pusher from 'pusher-js'; +import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ @@ -284,9 +292,9 @@ window.Echo = new Echo({ }); ``` -Note that our Ably Echo configuration references a `VITE_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. +You may have noticed our Ably Echo configuration references a `VITE_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: +Once you have adjusted the Echo configuration according to your needs, you may compile your application's assets: ```shell npm run dev @@ -302,9 +310,6 @@ Laravel's event broadcasting allows you to broadcast your server-side Laravel ev 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. -> [!NOTE] -> If you would like to explore open source alternatives to Pusher, check out the [open source alternatives](#open-source-alternatives). - ### Using an Example Application @@ -570,63 +575,14 @@ If your queue connection's `after_commit` configuration option is set to `false` ## Authorizing Channels -Private channels require you to authorize that the currently authenticated user can actually listen on the channel. This is accomplished by making an HTTP request to your Laravel application with the channel name and allowing your application to determine if the user can listen on that channel. When using [Laravel Echo](#client-side-installation), the HTTP request to authorize subscriptions to private channels will be made automatically; however, you do need to define the proper routes to respond to these requests. - - -### Defining Authorization Routes - -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(); +Private channels require you to authorize that the currently authenticated user can actually listen on the channel. This is accomplished by making an HTTP request to your Laravel application with the channel name and allowing your application to determine if the user can listen on that channel. When using [Laravel Echo](#client-side-installation), the HTTP request to authorize subscriptions to private channels will be made automatically. -The `Broadcast::routes` method will automatically place its routes within the `web` middleware group; however, you may pass an array of route attributes to the method if you would like to customize the assigned attributes: - - Broadcast::routes($attributes); - - -#### Customizing the Authorization Endpoint - -By default, Echo will use the `/broadcasting/auth` endpoint to authorize channel access. However, you may specify your own authorization endpoint by passing the `authEndpoint` configuration option to your Echo instance: - -```js -window.Echo = new Echo({ - broadcaster: 'pusher', - // ... - authEndpoint: '/custom/endpoint/auth' -}); -``` - - -#### Customizing the Authorization Request - -You can customize how Laravel Echo performs authorization requests by providing a custom authorizer when initializing Echo: - -```js -window.Echo = new Echo({ - // ... - authorizer: (channel, options) => { - return { - authorize: (socketId, callback) => { - axios.post('/api/broadcasting/auth', { - socket_id: socketId, - channel_name: channel.name - }) - .then(response => { - callback(null, response.data); - }) - .catch(error => { - callback(error); - }); - } - }; - }, -}) -``` +When broadcasting is enabled, Laravel automatically registers the `/broadcasting/auth` route to handle authorization requests. The `/broadcasting/auth` route is automatically placed within the `web` middleware group. ### Defining 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: +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 was created by the `install:broadcasting` Artisan command. In this file, you may use the `Broadcast::channel` method to register channel authorization callbacks: use App\Models\User; @@ -1170,4 +1126,4 @@ Echo.private(`App.Models.User.${userId}`) }); ``` -In this example, all notifications sent to `App\Models\User` instances via the `broadcast` channel would be received by the callback. A channel authorization callback for the `App.Models.User.{id}` channel is included in the default `BroadcastServiceProvider` that ships with the Laravel framework. +In this example, all notifications sent to `App\Models\User` instances via the `broadcast` channel would be received by the callback. A channel authorization callback for the `App.Models.User.{id}` channel is included in your application's `routes/channels.php` file. From 96011545f1fe0d38da40ed9390c41612c5e83426 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 22 Feb 2024 14:08:12 -0600 Subject: [PATCH 1406/2609] postgresql --- installation.md | 4 ++-- migrations.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/installation.md b/installation.md index da524332104..41f1a48e153 100644 --- a/installation.md +++ b/installation.md @@ -100,7 +100,7 @@ Now that you have created your Laravel application, you probably want to store s During the creation of the project, Laravel created a `database/database.sqlite` file for you, and ran the necessary migrations to create the application's database tables. -If you prefer to use another database driver such as MySQL or Postgres, you can update your `.env` configuration file to use the appropriate database. For example, if you wish to use MySQL, update your `.env` configuration file's `DB_*` variables like so: +If you prefer to use another database driver such as MySQL or PostgreSQL, you can update your `.env` configuration file to use the appropriate database. For example, if you wish to use MySQL, update your `.env` configuration file's `DB_*` variables like so: ```ini DB_CONNECTION=mysql @@ -118,7 +118,7 @@ php artisan migrate ``` > [!NOTE] -> If you are developing on macOS and need to install MySQL, Postgres, or Redis locally, consider using [DBngin](https://dbngin.com/). +> If you are developing on macOS and need to install MySQL, PostgreSQL, or Redis locally, consider using [DBngin](https://dbngin.com/). ### Directory Configuration diff --git a/migrations.md b/migrations.md index 206d93642cf..87f2a7647e3 100644 --- a/migrations.md +++ b/migrations.md @@ -311,7 +311,7 @@ The `temporary` method may be used to indicate that the table should be "tempora // ... }); -If you would like to add a "comment" to a database table, you may invoke the `comment` method on the table instance. Table comments are currently only supported by MySQL and Postgres: +If you would like to add a "comment" to a database table, you may invoke the `comment` method on the table instance. Table comments are currently only supported by MySQL and PostgreSQL: Schema::create('calculations', function (Blueprint $table) { $table->comment('Business calculations'); @@ -618,7 +618,7 @@ The `ipAddress` method creates a `VARCHAR` equivalent column: $table->ipAddress('visitor'); -When using Postgres, an `INET` column will be created. +When using PostgreSQL, an `INET` column will be created. #### `json()` {.collection-method} From 5046f90d8159e094783738f83b8f3d824591425f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 22 Feb 2024 16:17:17 -0600 Subject: [PATCH 1407/2609] updating wording --- broadcasting.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 23f23abb744..6bd8f5d5455 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -68,7 +68,7 @@ Event broadcasting is accomplished by a server-side broadcasting driver that bro ### Configuration -Laravel's complete `broadcasting.php` configuration file is not published by default, as you can specify your application's broadcast driver using the `BROADCAST_CONNECTION` [environment variable](/docs/{{version}}/configuration#environment-configuration). However, to customize some of the configuration options documented below that are not available via environment variables, you may need to publish the `broadcasting.php` configuration file using the `config:publish` Artisan command: +Laravel's complete `broadcasting.php` configuration file is not published by default, as you can specify your application's broadcast driver using the `BROADCAST_CONNECTION` [environment variable](/docs/{{version}}/configuration#environment-configuration). However, to customize some of the configuration options documented below that are not available via environment variables, you may publish the `broadcasting.php` configuration file using the `config:publish` Artisan command: ```shell php artisan config:publish broadcasting @@ -130,7 +130,7 @@ PUSHER_SCHEME=https PUSHER_APP_CLUSTER=mt1 ``` -Next, you should set the `BROADCAST_CONNECTION` environment variable's value to `pusher` in your application's `.env` file: +Then, set the `BROADCAST_CONNECTION` environment variable to `pusher` in your application's `.env` file: ```ini BROADCAST_CONNECTION=pusher @@ -156,7 +156,7 @@ Next, you should configure your Ably credentials via the `ABLY_KEY` environment ABLY_KEY=your-ably-key ``` -Next, you should set the `BROADCAST_CONNECTION` environment variable's value to `ably` in your application's `.env` file: +Then, set the `BROADCAST_CONNECTION` environment variable to `ably` in your application's `.env` file: ```ini BROADCAST_CONNECTION=ably @@ -213,7 +213,7 @@ npm run build 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 within the `resources/js/echo.js` file that was created by the `install:broadcasting` Artisan command. By default, an example Echo configuration is already included in this file; however, the default configuration in the `echo.js` file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Pusher: +Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. The `install:broadcasting` command creates an Echo configuration file at `resources/js/echo.js`; however, the default configuration in this file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Pusher: ```js import Echo from 'laravel-echo'; @@ -274,7 +274,7 @@ 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 within the `resources/js/echo.js` file that was created by the `install:broadcasting` Artisan command. By default, an example Echo configuration is already included in this file; however, the default configuration in the `echo.js` file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Ably: +Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. The `install:broadcasting` command creates an Echo configuration file at `resources/js/echo.js`; however, the default configuration in this file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Ably: ```js import Echo from 'laravel-echo'; From bc6503548a9aed3363504ab6447adcdd3e9f1805 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 22 Feb 2024 16:21:28 -0600 Subject: [PATCH 1408/2609] adjusting wording --- cache.md | 2 +- errors.md | 2 +- logging.md | 2 -- routing.md | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cache.md b/cache.md index 0be0f2fc416..a7f1a47768e 100644 --- a/cache.md +++ b/cache.md @@ -29,7 +29,7 @@ Thankfully, Laravel provides an expressive, unified API for various cache backen Laravel's complete `cache.php` configuration file is not published by default, as you can specify your application's cache driver using the `CACHE_DRIVER` [environment variable](/docs/{{version}}/configuration#environment-configuration). -However, to customize some of the configuration options documented below that are not available via environment variables, you may need to publish the configuration file using the `config:publish` Artisan command: +However, to customize some of the configuration options documented below that are not available via environment variables, you may publish the configuration file using the `config:publish` Artisan command: ```shell php artisan config:publish cache diff --git a/errors.md b/errors.md index 9be0b0df69d..2f6ff31a079 100644 --- a/errors.md +++ b/errors.md @@ -22,7 +22,7 @@ The `$exceptions` object provided to the `withExceptions` closure is an instance ## Configuration -The `debug` option in your `config/app.php` configuration file, which you may optionally publish using the `config:publish` Artisan command, determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. +The `APP_DEBUG` environment variable determines how much information about an error is actually displayed to the user. During local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** diff --git a/logging.md b/logging.md index b675ad2f4d2..2a0e3196861 100644 --- a/logging.md +++ b/logging.md @@ -38,8 +38,6 @@ You can specify your application's log driver using the `LOG_DRIVER` environment php artisan config:publish logging ``` -If you choose to not publish the configuration file, you may wish to review the `vendor/laravel/framework/config/logging.php` file to understand all of the available logging channels and their options. - ### Available Channel Drivers diff --git a/routing.md b/routing.md index d0c3bfa72f3..21dc5236d32 100644 --- a/routing.md +++ b/routing.md @@ -830,7 +830,7 @@ You may refer to the API documentation for both the [underlying class of the Rou Laravel can automatically respond to CORS `OPTIONS` HTTP requests with values that you configure. The `OPTIONS` requests will automatically be handled by the `HandleCors` [middleware](/docs/{{version}}/middleware) that is automatically included in your application's global middleware stack. -Sometimes, you may need to customize the CORS configuration values for your application. You may do so by publishing the CORS configuration file using the `config:publish` Artisan command: +Sometimes, you may need to customize the CORS configuration values for your application. You may do so by publishing the `cors` configuration file using the `config:publish` Artisan command: ```shell php artisan config:publish cors From 8a90469ad74da61d78c5374c084b214906bead31 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 23 Feb 2024 15:30:08 +0000 Subject: [PATCH 1409/2609] Fixes default channel env variable (#9389) --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index 2a0e3196861..7e0c52428da 100644 --- a/logging.md +++ b/logging.md @@ -32,7 +32,7 @@ Under the hood, Laravel utilizes the [Monolog](https://github.com/Seldaek/monolo By default, Laravel uses the `stack` channel when logging messages. The `stack` channel is used to [aggregate multiple log channels](#building-log-stacks) into a single channel. -You can specify your application's log driver using the `LOG_DRIVER` environment variable. However, to customize some of the configuration options documented below that are not available via environment variables, you should publish Laravel's complete `logging` configuration file using the `config:publish` Artisan command: +You can specify your application's log channel using the `LOG_CHANNEL` environment variable. However, to customize some of the configuration options documented below that are not available via environment variables, you should publish Laravel's complete `logging` configuration file using the `config:publish` Artisan command: ``` php artisan config:publish logging From 3117bb2fcdf53b685e55fc976d47f84da53d464c Mon Sep 17 00:00:00 2001 From: hlipnick <5473499+hlipnick@users.noreply.github.com> Date: Fri, 23 Feb 2024 07:34:15 -0800 Subject: [PATCH 1410/2609] Adds selector to documentation for mouse events doubleClick, clickAndHold and controlClick (#9386) --- dusk.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dusk.md b/dusk.md index 8fb7f8e6ad8..f450babfa44 100644 --- a/dusk.md +++ b/dusk.md @@ -782,6 +782,8 @@ The `doubleClick` method may be used to simulate the double click of a mouse: $browser->doubleClick(); + $browser->doubleClick('.selector'); + The `rightClick` method may be used to simulate the right click of a mouse: $browser->rightClick(); @@ -790,6 +792,8 @@ The `rightClick` method may be used to simulate the right click of a mouse: The `clickAndHold` method may be used to simulate a mouse button being clicked and held down. A subsequent call to the `releaseMouse` method will undo this behavior and release the mouse button: + $browser->clickAndHold('.selector'); + $browser->clickAndHold() ->pause(1000) ->releaseMouse(); @@ -798,6 +802,8 @@ The `controlClick` method may be used to simulate the `ctrl+click` event within $browser->controlClick(); + $browser->controlClick('.selector'); + #### Mouseover From 86314435e657cefd23f0b4e7d7d404b2cf5ef0a2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 23 Feb 2024 12:49:26 -0600 Subject: [PATCH 1411/2609] formatting --- logging.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/logging.md b/logging.md index 7e0c52428da..1262dba99a4 100644 --- a/logging.md +++ b/logging.md @@ -118,10 +118,13 @@ By default, Slack will only receive logs at the `critical` level and above; howe PHP, Laravel, and other libraries often notify their users that some of their features have been deprecated and will be removed in a future version. If you would like to log these deprecation warnings, you may specify your preferred `deprecations` log channel using the `LOG_DEPRECATIONS_CHANNEL` environment variable, or within your application's `config/logging.php` configuration file: - 'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], 'channels' => [ - ... + // ... ] Or, you may define a log channel named `deprecations`. If a log channel with this name exists, it will always be used to log deprecations: From d0d55c7d59e5dc0c644983bcb1897c5e8dff029a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 14:18:01 +0000 Subject: [PATCH 1412/2609] [11.x] Adjusts regarding config back to the skeleton (#9397) * Adjusts `broadcasting` * Adjusts `configuration` * Adjusts `cache` * Rewrites previous changes * Update cache.md * Update cache.md * Update configuration.md * Update configuration.md * Update installation.md * Update logging.md * Update session.md * Update session.md --------- Co-authored-by: Taylor Otwell --- broadcasting.md | 21 ++++++--------------- cache.md | 14 +++----------- configuration.md | 33 +++++++++++++++++---------------- errors.md | 2 +- installation.md | 14 ++++++++++---- logging.md | 10 +++------- releases.md | 16 ---------------- session.md | 10 ++-------- socialite.md | 6 ------ structure.md | 2 +- upgrade.md | 6 ------ 11 files changed, 43 insertions(+), 91 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 6bd8f5d5455..d98935987e1 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -50,12 +50,7 @@ To assist you in building these types of features, Laravel makes it easy to "bro The core concepts behind broadcasting are simple: clients connect to named channels on the frontend, while your Laravel application broadcasts events to these channels on the backend. These events can contain any additional data you wish to make available to the frontend. - -#### Supported Drivers - -By default, Laravel includes three server-side broadcasting drivers for you to choose from: [Laravel Reverb](https://reverb.laravel.com), [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.com). You may also be interested in community driven packages such as [soketi](https://docs.soketi.app/). - -> [!NOTE] +> [!NOTE] > Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). @@ -68,13 +63,7 @@ Event broadcasting is accomplished by a server-side broadcasting driver that bro ### Configuration -Laravel's complete `broadcasting.php` configuration file is not published by default, as you can specify your application's broadcast driver using the `BROADCAST_CONNECTION` [environment variable](/docs/{{version}}/configuration#environment-configuration). However, to customize some of the configuration options documented below that are not available via environment variables, you may publish the `broadcasting.php` configuration file using the `config:publish` Artisan command: - -```shell -php artisan config:publish broadcasting -``` - -Laravel supports several broadcast drivers: [Laravel Reverb](/docs/{{version}}/reverb), [Pusher Channels](https://pusher.com/channels), [Ably](https://ably.com), and a `log` driver for local development and debugging. Additionally, a `null` driver is included which allows you to totally disable broadcasting during testing. +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: [Laravel Reverb](/docs/{{version}}/reverb), [Pusher Channels](https://pusher.com/channels), [Ably](https://ably.com), 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. #### Installation @@ -118,7 +107,7 @@ If you plan to broadcast your events using [Pusher Channels](https://pusher.com/ composer require pusher/pusher-php-server ``` -Next, you should configure your Pusher Channels credentials in your application's `.env` file: +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 PUSHER_APP_ID=your-pusher-app-id @@ -130,6 +119,8 @@ PUSHER_SCHEME=https PUSHER_APP_CLUSTER=mt1 ``` +The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Channels, such as the cluster. + Then, set the `BROADCAST_CONNECTION` environment variable to `pusher` in your application's `.env` file: ```ini @@ -150,7 +141,7 @@ If you plan to broadcast your events using [Ably](https://ably.com), you should composer require ably/ably-php ``` -Next, you should configure your Ably credentials via the `ABLY_KEY` environment variable in your application's `.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): ```ini ABLY_KEY=your-ably-key diff --git a/cache.md b/cache.md index a7f1a47768e..c3072f003b2 100644 --- a/cache.md +++ b/cache.md @@ -27,17 +27,9 @@ Thankfully, Laravel provides an expressive, unified API for various cache backen ## Configuration -Laravel's complete `cache.php` configuration file is not published by default, as you can specify your application's cache driver using the `CACHE_DRIVER` [environment variable](/docs/{{version}}/configuration#environment-configuration). +Your application's cache configuration file is located at `config/cache.php`. In this file, you may specify which cache store 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. -However, to customize some of the configuration options documented below that are not available via environment variables, you may publish the configuration file using the `config:publish` Artisan command: - -```shell -php artisan config:publish cache -``` - -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. - -By default, Laravel is configured to use the `database` cache driver, which stores the serialized, cached objects in your application's database. +The cache configuration file also contains a variety of other options that you may review. By default, Laravel is configured to use the `database` cache driver, which stores the serialized, cached objects in your application's database. ### Driver Prerequisites @@ -45,7 +37,7 @@ By default, Laravel is configured to use the `database` cache driver, which stor #### Database -When using the `database` session driver, you will need a database table to contain the cache data. Typically, this is included in Laravel's default `0001_01_01_000001_create_cache_table.php` [database migration](/docs/{{version}}/migrations); however, if your application does not contain this migration, you may use the `make:cache-table` Artisan command to create it: +When using the `database` cache driver, you will need a database table to contain the cache data. Typically, this is included in Laravel's default `0001_01_01_000001_create_cache_table.php` [database migration](/docs/{{version}}/migrations); however, if your application does not contain this migration, you may use the `make:cache-table` Artisan command to create it: ```shell php artisan make:cache-table diff --git a/configuration.md b/configuration.md index 46531d2d4f1..18e0a384b54 100644 --- a/configuration.md +++ b/configuration.md @@ -8,28 +8,16 @@ - [Encrypting Environment Files](#encrypting-environment-files) - [Accessing Configuration Values](#accessing-configuration-values) - [Configuration Caching](#configuration-caching) +- [Configuration Publishing](#configuration-publishing) - [Debug Mode](#debug-mode) - [Maintenance Mode](#maintenance-mode) ## Introduction -While Laravel requires no additional configuration out of the box, over time you may need to configure various aspects of the framework, such as using a different database connection. +All of the configuration files for the Laravel framework are stored in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you. - -#### Publishing the Configuration Files - -Most aspects of the framework's behavior can be configured via your application's [environment file](#environment-configuration); however, some applications, such as those utilizing multiple database connections, may need to publish Laravel's full configuration files. - -Laravel's configuration files are not published by default. To publish the configuration files to your application's `config` directory, you may use the `config:publish` Artisan command: - -```shell -php artisan config:publish - -php artisan config:publish --all -``` - -Each configuration file is thoroughly documented, so feel free to review all published configuration files in order to get familiar with the options available to your application. +These configuration files allow you to configure things like your database connection information, your mail server information, as well as various other core configuration values such as your application timezone and encryption key. #### The `about` Command @@ -59,7 +47,7 @@ It is often helpful to have different configuration values based on the environm To make this a cinch, Laravel utilizes the [DotEnv](https://github.com/vlucas/phpdotenv) PHP library. In a fresh Laravel installation, the root directory of your application will contain a `.env.example` file that defines many common environment variables. During the Laravel installation process, this file will automatically be copied to `.env`. -Laravel's default `.env` file contains some common configuration values that may differ based on whether your application is running locally or on a production web server. These values are then read by Laravel's various configuration files within the `vendor/laravel/framework/config` and `config` directories using Laravel's `env` function. +Laravel's default `.env` file contains some common configuration values that may differ based on whether your application is running locally or on a production web server. These values are then read by the configuration files within the `config` directory using Laravel's `env` function. If you are developing with a team, you may wish to continue including and updating the `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. @@ -229,6 +217,19 @@ php artisan config:clear > [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. + +## Configuration Publishing + +Most of Laravel's configuration files are already published in your application's `config` directory; however, certain configuration files like `cors.php` and `view.php` are not published by default, as most applications will never need to modify them. + +However, you may use the `config:publish` Artisan command to publish any configuration files that are not published by default: + +```shell +php artisan config:publish + +php artisan config:publish --all +``` + ## Debug Mode diff --git a/errors.md b/errors.md index 2f6ff31a079..aeb5f47b07a 100644 --- a/errors.md +++ b/errors.md @@ -22,7 +22,7 @@ The `$exceptions` object provided to the `withExceptions` closure is an instance ## Configuration -The `APP_DEBUG` environment variable determines how much information about an error is actually displayed to the user. +The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. During local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** diff --git a/installation.md b/installation.md index 41f1a48e153..e05510943da 100644 --- a/installation.md +++ b/installation.md @@ -4,6 +4,7 @@ - [Why Laravel?](#why-laravel) - [Creating a Laravel Project](#creating-a-laravel-project) - [Initial Configuration](#initial-configuration) + - [Environment Based Configuration](#environment-based-configuration) - [Databases and Migrations](#databases-and-migrations) - [Directory Configuration](#directory-configuration) - [Docker Installation Using Sail](#docker-installation-using-sail) @@ -84,14 +85,19 @@ Once you have started the Artisan development server, your application will be a ## Initial Configuration -Laravel requires no additional configuration out of the box. However, you may wish to review the `.env` file that exists at the root of your application to explore some of the configuration options available to you. +All of the configuration files for the Laravel framework are stored in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you. -The `.env` file is where you may configure most of your application's settings, including your application's name and database connection information. +Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. -Many of Laravel's configuration options may vary depending on whether your application is running on your local machine or on a production web server, so your `.env` file should not be committed to your application's source control since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would be exposed. + +### Environment Based Configuration + +Since many of Laravel's configuration option values may vary depending on whether your application is running on your local machine or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. + +Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would be exposed. > [!NOTE] -> For more information about the `.env` file and the options that can be configured using environment variables, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). +> For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). ### Databases and Migrations diff --git a/logging.md b/logging.md index 1262dba99a4..da1fe24bba0 100644 --- a/logging.md +++ b/logging.md @@ -30,18 +30,14 @@ Under the hood, Laravel utilizes the [Monolog](https://github.com/Seldaek/monolo ## Configuration -By default, Laravel uses the `stack` channel when logging messages. The `stack` channel is used to [aggregate multiple log channels](#building-log-stacks) into a single channel. +All of the configuration options that control your application's logging behavior are housed in the `config/logging.php` configuration file. This file allows you to configure your application's log channels, so be sure to review each of the available channels and their options. We'll review a few common options below. -You can specify your application's log channel using the `LOG_CHANNEL` environment variable. However, to customize some of the configuration options documented below that are not available via environment variables, you should publish Laravel's complete `logging` configuration file using the `config:publish` Artisan command: - -``` -php artisan config:publish logging -``` +By default, Laravel will use the `stack` channel when logging messages. The `stack` channel is used to aggregate multiple log channels into a single channel. For more information on building stacks, check out the [documentation below](#building-log-stacks). ### Available Channel Drivers -Each log channel is powered by a "driver". The driver determines how and where the log message is actually recorded. The following log channel drivers are available in every Laravel application: +Each log channel is powered by a "driver". The driver determines how and where the log message is actually recorded. The following log channel drivers are available in every Laravel application. An entry for most of these drivers is already present in your application's `config/logging.php` configuration file, so be sure to review this file to become familiar with its contents:
    diff --git a/releases.md b/releases.md index 0e943b2bc6d..a7a34f9b65d 100644 --- a/releases.md +++ b/releases.md @@ -88,22 +88,6 @@ Instead of the default Laravel application structure containing five service pro For example, event discovery is now enabled by default, largely eliminating the need for manual registration of events and their listeners. However, if you do need to manually register events, you may simply do so in the `AppServiceProvider`. Similarly, route model bindings or authorization gates you may have previously registered in the `AuthServiceProvider` may also be registered in the `AppServiceProvider`. -#### Configuration Files - -The use of environment variables has been expanded, with additional place-holders being added to the `.env.example` file included with new Laravel applications. Because of this, almost all core framework functionality can be configured via your application's `.env` file instead of individual configuration files. Therefore, the `config` directory no longer contains any configuration files by default. - -Instead, configuration files can be published using the new `config:publish` Artisan command, which allows you to publish only the configuration files you would like to customize: - -```shell -php artisan config:publish -``` - -Of course, you may easily publish all of the framework's configuration files: - -```shell -php artisan config:publish --all -``` - #### Opt-in API and Broadcast Routing The `api.php` and `channels.php` route files are no longer present by default, as many applications do not require these files. Instead, they may be created using simple Artisan commands: diff --git a/session.md b/session.md index 57eca3ee7c0..c01fddf0b7a 100644 --- a/session.md +++ b/session.md @@ -24,15 +24,9 @@ Laravel ships with a variety of session backends that are accessed through an ex ### Configuration -By default, Laravel is configured to use the `database` session driver, which stores user session information in your application's database and will work well for many production 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 `database` session driver. -Laravel's complete `session.php` configuration file is not published by default, as you can specify your application's session driver using the `SESSION_DRIVER` [environment variable](/docs/{{version}}/configuration#environment-configuration). However, if necessary, you may publish the configuration file using the `config:publish` Artisan command: - -```shell -php artisan config:publish session -``` - -Laravel includes a variety of great session drivers: +The session `driver` configuration option defines where session data will be stored for each request. Laravel includes a variety of drivers:
    diff --git a/socialite.md b/socialite.md index a771f0ba571..f55cba04016 100644 --- a/socialite.md +++ b/socialite.md @@ -29,12 +29,6 @@ To get started with Socialite, use the Composer package manager to add the packa composer require laravel/socialite ``` -Next, you should publish the `config/services.php` configuration file using the `config:publish` Artisan command: - -```shell -php artisan config:publish services -``` - ## Upgrading Socialite diff --git a/structure.md b/structure.md index e219866ab89..5ba87f5441c 100644 --- a/structure.md +++ b/structure.md @@ -51,7 +51,7 @@ The `bootstrap` directory contains the `app.php` file which bootstraps the frame #### The Config Directory -The `config` directory, as the name implies, contains all of your application's configuration files. By default, the directory is empty; however, packages may place their configuration files in this directory. You may also create your own configuration files in this directory. +The `config` directory, as the name implies, contains all of your application's configuration files. It's a great idea to read through all of these files and familiarize yourself with all of the options available to you. #### The Database Directory diff --git a/upgrade.md b/upgrade.md index 8d8f8381468..4bdc9fa7a86 100644 --- a/upgrade.md +++ b/upgrade.md @@ -74,12 +74,6 @@ Laravel 11 introduces a new default application structure with fewer default fil However, we do **not recommend** that Laravel 10 applications upgrading to Laravel 11 attempt to migrate their application structure, as Laravel 11 has been carefully tuned to also support the Laravel 10 application structure. -Nevertheless, if you choose to attempt to migrate your application's structure and also choose to remove your application's configuration files, you should install the following compatibility package via Composer to ensure that Laravel continues to function correctly: - -```shell -composer require laravel/previously-on-laravel-10 -``` - ### Authentication From e2867f92f126dff4222b1718655b483dd96186f6 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:36:02 +0000 Subject: [PATCH 1413/2609] Adjusts `queues` (#9400) --- queues.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/queues.md b/queues.md index f3a268a387b..7f27405dfe9 100644 --- a/queues.md +++ b/queues.md @@ -92,7 +92,7 @@ php artisan queue:work --queue=high,default #### Database -In order to use the `database` queue driver, you will need a database table to hold the jobs. To generate a migration that creates this table, run the `make:queue-table` Artisan command. Once the migration has been created, you may migrate your database using the `migrate` command: +In order to use the `database` queue driver, you will need a database table to hold the jobs. Typically, this is included in Laravel's default `0001_01_01_000002_create_jobs_table.php` [database migration](/docs/{{version}}/migrations); however, if your application does not contain this migration, you may use the `make:queue-table` Artisan command to create it: ```shell php artisan make:queue-table @@ -100,10 +100,6 @@ php artisan make:queue-table php artisan migrate ``` -Finally, don't forget to instruct your application to use the `database` driver by updating the `QUEUE_CONNECTION` variable in your application's `.env` file: - - QUEUE_CONNECTION=database - #### Redis @@ -118,9 +114,11 @@ If your Redis queue connection uses a Redis Cluster, your queue names must conta 'redis' => [ 'driver' => 'redis', - 'connection' => 'default', - 'queue' => '{default}', - 'retry_after' => 90, + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', '{default}'), + 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, ], **Blocking** @@ -131,10 +129,11 @@ Adjusting this value based on your queue load can be more efficient than continu 'redis' => [ 'driver' => 'redis', - 'connection' => 'default', - 'queue' => 'default', - 'retry_after' => 90, + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), 'block_for' => 5, + 'after_commit' => false, ], > [!WARNING] @@ -149,7 +148,7 @@ The following dependencies are needed for the listed queue drivers. These depend - Amazon SQS: `aws/aws-sdk-php ~3.0` - Beanstalkd: `pda/pheanstalk ~4.0` -- Redis: `predis/predis ~1.0` or phpredis PHP extension +- Redis: `predis/predis ~2.0` or phpredis PHP extension
    From e0590f051944930ff16c46c3cf7564b54b7f7d2f Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:38:50 +0000 Subject: [PATCH 1414/2609] [11.x] Rewrites `notifications` for L11 (#9399) * Adjusts `notifications` * formatting --------- Co-authored-by: Taylor Otwell --- notifications.md | 59 +++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/notifications.md b/notifications.md index 8981f16c8e2..47136414836 100644 --- a/notifications.md +++ b/notifications.md @@ -1443,28 +1443,25 @@ By passing a closure as the second argument to the `assertSentOnDemand` method, #### Notification Sending Event -When a notification is sending, the `Illuminate\Notifications\Events\NotificationSending` [event](/docs/{{version}}/events) is dispatched by the notification system. This contains the "notifiable" entity and the notification instance itself. You may register listeners for this event in your application's `EventServiceProvider`: +When a notification is sending, the `Illuminate\Notifications\Events\NotificationSending` event is dispatched by the notification system. This contains the "notifiable" entity and the notification instance itself. You may create [event listeners](/docs/{{version}}/events) for this event within your application: - use App\Listeners\CheckNotificationStatus; use Illuminate\Notifications\Events\NotificationSending; - - /** - * The event listener mappings for the application. - * - * @var array - */ - protected $listen = [ - NotificationSending::class => [ - CheckNotificationStatus::class, - ], - ]; -The notification will not be sent if an event listener for the `NotificationSending` event returns `false` from its `handle` method: + class CheckNotificationStatus + { + /** + * Handle the given event. + */ + public function handle(NotificationSending $event): void + { + // ... + } + } - use Illuminate\Notifications\Events\NotificationSending; +The notification will not be sent if an event listener for the `NotificationSending` event returns `false` from its `handle` method: /** - * Handle the event. + * Handle the given event. */ public function handle(NotificationSending $event): bool { @@ -1474,7 +1471,7 @@ The notification will not be sent if an event listener for the `NotificationSend Within an event listener, you may access the `notifiable`, `notification`, and `channel` properties on the event to learn more about the notification recipient or the notification itself: /** - * Handle the event. + * Handle the given event. */ public function handle(NotificationSending $event): void { @@ -1486,29 +1483,25 @@ Within an event listener, you may access the `notifiable`, `notification`, and ` #### Notification Sent Event -When a notification is sent, the `Illuminate\Notifications\Events\NotificationSent` [event](/docs/{{version}}/events) is dispatched 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 dispatched by the notification system. This contains the "notifiable" entity and the notification instance itself. You may create [event listeners](/docs/{{version}}/events) for this event within your application: - use App\Listeners\LogNotification; use Illuminate\Notifications\Events\NotificationSent; - - /** - * The event listener mappings for the application. - * - * @var array - */ - protected $listen = [ - NotificationSent::class => [ - LogNotification::class, - ], - ]; -> [!NOTE] -> After registering listeners in your `EventServiceProvider`, use the `event:generate` Artisan command to quickly generate listener classes. + class LogNotification + { + /** + * Handle the given event. + */ + public function handle(NotificationSending $event): void + { + // ... + } + } Within an event listener, you may access the `notifiable`, `notification`, `channel`, and `response` properties on the event to learn more about the notification recipient or the notification itself: /** - * Handle the event. + * Handle the given event. */ public function handle(NotificationSent $event): void { From 0d14470b067f83cfb6e21b172ed7b860ee254e98 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:40:53 +0000 Subject: [PATCH 1415/2609] [11.x] Rewrites `mail` for L11 (#9398) * Adjusts `mail` * formatting --------- Co-authored-by: Taylor Otwell --- mail.md | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/mail.md b/mail.md index 7b86458206e..da8a1ef3be5 100644 --- a/mail.md +++ b/mail.md @@ -59,12 +59,13 @@ To use the Mailgun driver, install Symfony's Mailgun Mailer transport via Compos composer require symfony/mailgun-mailer symfony/http-client ``` -Next, set the `default` option in your application's `config/mail.php` configuration file to `mailgun`. After configuring your application's default mailer, verify that your `config/services.php` configuration file contains the following options: +Next, set the `default` option in your application's `config/mail.php` configuration file to `mailgun`. After configuring your application's default mailer, add the following options to your `config/services.php` configuration file: 'mailgun' => [ - 'transport' => '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: @@ -73,6 +74,7 @@ If you are not using the United States [Mailgun region](https://documentation.ma 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), 'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'), + 'scheme' => 'https', ], @@ -84,7 +86,7 @@ To use the Postmark driver, install Symfony's Postmark Mailer transport via Comp 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, verify that your `config/services.php` configuration file contains the following options: +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'), @@ -95,6 +97,9 @@ If you would like to specify the Postmark message stream that should be used by 'postmark' => [ 'transport' => 'postmark', 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], ], This way you are also able to set up multiple Postmark mailers with different message streams. @@ -1216,27 +1221,21 @@ Finally, you may specify a global "to" address by invoking the `alwaysTo` method ## Events -Laravel fires two events during the process of sending mail messages. The `MessageSending` event is fired prior to a message being sent, while the `MessageSent` event is fired after a message has been sent. Remember, these events are fired when the mail is being *sent*, not when it is queued. You may register event listeners for this event in your `App\Providers\EventServiceProvider` service provider: +Laravel dispatches two events while sending mail messages. The `MessageSending` event is dispatched prior to a message being sent, while the `MessageSent` event is dispatched after a message has been sent. Remember, these events are dispatched when the mail is being *sent*, not when it is queued. You may create [event listeners](/docs/{{version}}/events) for these events within your application: - use App\Listeners\LogSendingMessage; - use App\Listeners\LogSentMessage; use Illuminate\Mail\Events\MessageSending; - use Illuminate\Mail\Events\MessageSent; + // use Illuminate\Mail\Events\MessageSent; - /** - * The event listener mappings for the application. - * - * @var array - */ - protected $listen = [ - MessageSending::class => [ - LogSendingMessage::class, - ], - - MessageSent::class => [ - LogSentMessage::class, - ], - ]; + class LogMessage + { + /** + * Handle the given event. + */ + public function handle(MessageSending $event): void + { + // ... + } + } ## Custom Transports From 299f820473a1e6bf7fc321a9f50b7798e331db1d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:44:49 +0000 Subject: [PATCH 1416/2609] [11.x] Rewrites `packages` for L11 (#9394) * Rewrites `packages` * formatting * Update packages.md --------- Co-authored-by: Taylor Otwell --- packages.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages.md b/packages.md index 02c81502e20..4999c4e343d 100644 --- a/packages.md +++ b/packages.md @@ -33,7 +33,7 @@ When writing a Laravel application, it generally does not matter if you use cont ## Package Discovery -In a Laravel application's `config/app.php` configuration file, the `providers` option defines a list of service providers that should be loaded by Laravel. When someone installs your package, you will typically want your service provider to be included in this list. Instead of requiring users to manually add your service provider to the list, you may define the provider in the `extra` section of your package's `composer.json` file. In addition to service providers, you may also list any [facades](/docs/{{version}}/facades) you would like to be registered: +A Laravel application's `bootstrap/providers.php` file contains the list of service providers that should be loaded by Laravel. However, instead of requiring users to manually add your service provider to the list, you may define the provider in the `extra` section of your package's `composer.json` file so that it is automatically loaded by Laravel. In addition to service providers, you may also list any [facades](/docs/{{version}}/facades) you would like to be registered: ```json "extra": { @@ -145,18 +145,18 @@ If your package contains routes, you may load them using the `loadRoutesFrom` me ### Migrations -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: +If your package contains [database migrations](/docs/{{version}}/migrations), you may use the `publishesMigrations` method to inform Laravel that the given directory or file contains migrations. When Laravel publishes the migrations, it will automatically update the timestamp within their filename to reflect the current date and time: /** * Bootstrap any package services. */ public function boot(): void { - $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); + $this->publishesMigrations([ + __DIR__.'/../database/migrations' => database_path('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 `database/migrations` directory. - ### Language Files @@ -374,7 +374,7 @@ You may want to publish groups of package assets and resources separately. For i __DIR__.'/../config/package.php' => config_path('package.php') ], 'courier-config'); - $this->publishes([ + $this->publishesMigrations([ __DIR__.'/../database/migrations/' => database_path('migrations') ], 'courier-migrations'); } From f0773b3bea1d60f156f20a1a5dcf7327de711435 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:46:50 +0000 Subject: [PATCH 1417/2609] [11.x] Rewrites `localization` for L11 (#9393) * Rewrites `localization` for L11 * Update localization.md --------- Co-authored-by: Taylor Otwell --- localization.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/localization.md b/localization.md index c0fffeec92f..c001b512327 100644 --- a/localization.md +++ b/localization.md @@ -48,7 +48,9 @@ php artisan lang:publish ### Configuring the Locale -The default language for your application is stored in the `config/app.php` configuration file's `locale` configuration option. You are free to modify this value to suit the needs of your application. +The default language for your application is stored in the `config/app.php` configuration file's `locale` configuration option, which is typically set using the `APP_LOCALE` environment variable. You are free to modify this value to suit the needs of your application. + +You may also configure a "fallback language", which will be used when the default language does not contain a given translation string. Like the default language, the fallback language is also configured in the `config/app.php` configuration file, and its value is typically set using the `APP_FALLBACK_LOCALE` environment variable. You may modify the default language for a single HTTP request at runtime using the `setLocale` method provided by the `App` facade: @@ -64,10 +66,6 @@ You may modify the default language for a single HTTP request at runtime using t // ... }); -You may configure a "fallback language", which will be used when the active language does not contain a given translation string. Like the default language, the fallback language is also configured in the `config/app.php` configuration file: - - 'fallback_locale' => 'en', - #### Determining the Current Locale From 98b099dcd8bb86dd874fb8c7e6f4c1488463ca0a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:47:55 +0000 Subject: [PATCH 1418/2609] [11.x] Rewrites `http-client` for L11 (#9392) * Rewrites `http-client` * formatting --------- Co-authored-by: Taylor Otwell --- http-client.md | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/http-client.md b/http-client.md index 5908bbc611c..78dfc8255fe 100644 --- a/http-client.md +++ b/http-client.md @@ -23,12 +23,6 @@ Laravel provides an expressive, minimal API around the [Guzzle HTTP client](http://docs.guzzlephp.org/en/stable/), allowing you to quickly make outgoing HTTP requests to communicate with other web applications. Laravel's wrapper around Guzzle is focused on its most common use cases and a wonderful developer experience. -Before getting started, you should ensure that you have installed the Guzzle package as a dependency of your application. By default, Laravel automatically includes this dependency. However, if you have previously removed the package, you may install it again via Composer: - -```shell -composer require guzzlehttp/guzzle -``` - ## Making Requests @@ -673,21 +667,17 @@ $recorded = Http::recorded(function (Request $request, Response $response) { Laravel fires three events during the process of sending HTTP requests. The `RequestSending` event is fired prior to a request being sent, while the `ResponseReceived` event is fired after a response is received for a given request. The `ConnectionFailed` event is fired if no response is received for a given request. -The `RequestSending` and `ConnectionFailed` events both contain a public `$request` property that you may use to inspect the `Illuminate\Http\Client\Request` instance. Likewise, the `ResponseReceived` event contains a `$request` property as well as a `$response` property which may be used to inspect the `Illuminate\Http\Client\Response` instance. You may register event listeners for this event in your `App\Providers\EventServiceProvider` service provider: - - /** - * The event listener mappings for the application. - * - * @var array - */ - protected $listen = [ - 'Illuminate\Http\Client\Events\RequestSending' => [ - 'App\Listeners\LogRequestSending', - ], - 'Illuminate\Http\Client\Events\ResponseReceived' => [ - 'App\Listeners\LogResponseReceived', - ], - 'Illuminate\Http\Client\Events\ConnectionFailed' => [ - 'App\Listeners\LogConnectionFailed', - ], - ]; +The `RequestSending` and `ConnectionFailed` events both contain a public `$request` property that you may use to inspect the `Illuminate\Http\Client\Request` instance. Likewise, the `ResponseReceived` event contains a `$request` property as well as a `$response` property which may be used to inspect the `Illuminate\Http\Client\Response` instance. You may create [event listeners](/docs/{{version}}/events) for these events within your application: + + use Illuminate\Http\Client\Events\RequestSending; + + class LogRequest + { + /** + * Handle the given event. + */ + public function handle(RequestSending $event): void + { + // $event->request ... + } + } From fc6b749132f58c5c0c65246d4a2341c86ac1ff27 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:49:29 +0000 Subject: [PATCH 1419/2609] [11.x] Rewrites `helpers` for L11 (#9391) * Rewrites `helpers` * wip * Update helpers.md * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 00a46bd98a2..452705db0cb 100644 --- a/helpers.md +++ b/helpers.md @@ -1752,7 +1752,7 @@ The `fake` function resolves a [Faker](https://github.com/FakerPHP/Faker) single @endfor ``` -By default, the `fake` function will utilize the `app.faker_locale` configuration option in your `config/app.php` configuration file; however, you may also specify the locale by passing it to the `fake` function. Each locale will resolve an individual singleton: +By default, the `fake` function will utilize the `app.faker_locale` configuration option in your `config/app.php` configuration. Typically, this configuration option is set via the `APP_FAKER_LOCALE` environment variable. You may also specify the locale by passing it to the `fake` function. Each locale will resolve an individual singleton: fake('nl_NL')->name() From cfdc69613771131cdae4b8ade0ccea0a4c5d064c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 15:54:27 +0000 Subject: [PATCH 1420/2609] [11.x] Rewrites `storage` for L11 (#9390) * Rewrites `storage` * adjusts * formatting * formatting --------- Co-authored-by: Taylor Otwell --- filesystem.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/filesystem.md b/filesystem.md index f1ac8911449..659db7882a7 100644 --- a/filesystem.md +++ b/filesystem.md @@ -91,7 +91,17 @@ Before using the S3 driver, you will need to install the Flysystem S3 package vi composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies ``` -The S3 driver configuration information is located in your `config/filesystems.php` configuration file. This file contains an example configuration array for an S3 driver. You are free to modify this array with your own S3 configuration and credentials. For convenience, these environment variables match the naming convention used by the AWS CLI. +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: + +``` +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false +``` + +For convenience, these environment variables match the naming convention used by the AWS CLI. #### FTP Driver Configuration @@ -102,7 +112,7 @@ Before using the FTP driver, you will need to install the Flysystem FTP package 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 `filesystems.php` configuration file. If you need to configure an FTP filesystem, you may use the configuration example below: +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', @@ -127,7 +137,7 @@ Before using the SFTP driver, you will need to install the Flysystem SFTP packag 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 `filesystems.php` configuration file. If you need to configure an SFTP filesystem, you may use the configuration example below: +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', @@ -289,13 +299,14 @@ When using the `local` driver, all files that should be publicly accessible shou #### URL Host Customization -If you would like to pre-define the host for URLs generated using the `Storage` facade, you may add a `url` option to the disk's configuration array: +If you would like to modify the host for URLs generated using the `Storage` facade, you may add or change the `url` option in the disk's configuration array: 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'url' => env('APP_URL').'/storage', 'visibility' => 'public', + 'throw' => false, ], @@ -586,6 +597,7 @@ When using the `local` driver, `public` [visibility](#file-visibility) translate 'private' => 0700, ], ], + 'throw' => false, ], From 7c75d23b624f673f187f655b217270edfcb59ab8 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 16:11:40 +0000 Subject: [PATCH 1421/2609] [11.x] Rewrites `events` for L11 (#9387) * Rewrites `events` * More rewrites * wip * wip * formatting * formatting * fix link --------- Co-authored-by: Taylor Otwell --- deployment.md | 2 +- events.md | 195 +++++++++++++++++++++++--------------------------- 2 files changed, 89 insertions(+), 108 deletions(-) diff --git a/deployment.md b/deployment.md index f5ac7b8a103..ab4da9d8efd 100644 --- a/deployment.md +++ b/deployment.md @@ -107,7 +107,7 @@ This command will combine all of Laravel's configuration files into a single, ca ### Caching Events -If your application is utilizing [event discovery](/docs/{{version}}/events#event-discovery), you should cache your application's event to listener mappings during your deployment process. This can be accomplished by invoking the `event:cache` Artisan command during deployment: +You should cache your application's auto-discovered event to listener mappings during your deployment process. This can be accomplished by invoking the `event:cache` Artisan command during deployment: ```shell php artisan event:cache diff --git a/events.md b/events.md index ff25daa197a..d5b45e71259 100644 --- a/events.md +++ b/events.md @@ -1,10 +1,11 @@ # Events - [Introduction](#introduction) +- [Generating Events and Listeners](#generating-events-and-listeners) - [Registering Events and Listeners](#registering-events-and-listeners) - - [Generating Events and Listeners](#generating-events-and-listeners) - - [Manually Registering Events](#manually-registering-events) - [Event Discovery](#event-discovery) + - [Manually Registering Events](#manually-registering-events) + - [Closure Listeners](#closure-listeners) - [Defining Events](#defining-events) - [Defining Listeners](#defining-listeners) - [Queued Event Listeners](#queued-event-listeners) @@ -27,56 +28,74 @@ Laravel's events provide a simple observer pattern implementation, allowing you Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other. For example, you may wish to send a Slack notification to your user each time an order has shipped. Instead of coupling your order processing code to your Slack notification code, you can raise an `App\Events\OrderShipped` event which a listener can receive and use to dispatch a Slack notification. + +## Generating Events and Listeners + +To quickly generate events and listeners, you may use the `make:event` and `make:listener` Artisan commands: + +```shell +php artisan make:event PodcastProcessed + +php artisan make:listener SendPodcastNotification --event=PodcastProcessed +``` + +For convenience, you may also invoke the `make:event` and `make:listener` Artisan commands without additional arguments. When you do so, Laravel will automatically prompt you for the class name and, when creating a listener, the event it should listen to: + +```shell +php artisan make:event + +php artisan make:listener +``` + ## Registering Events and Listeners -The `App\Providers\EventServiceProvider` included with your Laravel application provides a convenient place to register all of your application's event listeners. The `listen` property contains an array of all events (keys) and their listeners (values). You may add as many events to this array as your application requires. For example, let's add an `OrderShipped` event: + +### Event Discovery - use App\Events\OrderShipped; - use App\Listeners\SendShipmentNotification; +By default, Laravel will automatically find and register your event listeners by scanning your application's `Listeners` directory. When Laravel finds any listener class method that begins with `handle` or `__invoke`, Laravel will register those methods as event listeners for the event that is type-hinted in the method's signature: - /** - * The event listener mappings for the application. - * - * @var array> - */ - protected $listen = [ - OrderShipped::class => [ - SendShipmentNotification::class, - ], - ]; + use App\Events\PodcastProcessed; -> [!NOTE] -> The `event:list` command may be used to display a list of all events and listeners registered by your application. + class SendPodcastNotification + { + /** + * Handle the given event. + */ + public function handle(PodcastProcessed $event): void + { + // ... + } + } - -### Generating Events and Listeners +If you plan to store your listeners in a different directory or within multiple directories, you may instruct Laravel to scan those directories using the `withEvents` method in your application's `bootstrap/app.php` file: + + ->withEvents(discover: [ + __DIR__.'/../app/Domain/Listeners', + ]) -Of course, manually creating the files for each event and listener is cumbersome. Instead, add listeners and events to your `EventServiceProvider` and use the `event:generate` Artisan command. This command will generate any events or listeners that are listed in your `EventServiceProvider` that do not already exist: +The `event:list` command may be used to list all of the listeners registered within your application: ```shell -php artisan event:generate +php artisan event:list ``` -Alternatively, you may use the `make:event` and `make:listener` Artisan commands to generate individual events and listeners: - -```shell -php artisan make:event PodcastProcessed + +#### Event Discovery in Production -php artisan make:listener SendPodcastNotification --event=PodcastProcessed -``` +To give your application a speed boost, you should cache a manifest of all of your application's listeners using the `event:cache` Artisan command. Typically, this command should be run as part of your application's [deployment process](/docs/{{version}}/deployment#caching-events). This manifest will be used by the framework to speed up the event registration process. The `event:clear` command may be used to destroy the cache. ### Manually Registering Events -Typically, events should be registered via the `EventServiceProvider` `$listen` array; however, you may also register class or closure based event listeners manually in the `boot` method of your `EventServiceProvider`: +Using the `Event` facade, you may manually register events and their corresponding listeners within the `boot` method of your application's `AppServiceProvider`: - use App\Events\PodcastProcessed; - use App\Listeners\SendPodcastNotification; + use App\Domain\Orders\Events\PodcastProcessed; + use App\Domain\Orders\Listeners\SendPodcastNotification; use Illuminate\Support\Facades\Event; /** - * Register any other events for your application. + * Bootstrap any application services. */ public function boot(): void { @@ -84,7 +103,27 @@ Typically, events should be registered via the `EventServiceProvider` `$listen` PodcastProcessed::class, SendPodcastNotification::class, ); + } + +The `event:list` command may be used to list all of the listeners registered within your application: + +```shell +php artisan event:list +``` + + +### Closure Listeners + +Typically, listeners are defined as classes; however, you may also manually register closure-based event listeners in the `boot` method of your application's `AppServiceProvider`: + + use App\Events\PodcastProcessed; + use Illuminate\Support\Facades\Event; + /** + * Bootstrap any application services. + */ + public function boot(): void + { Event::listen(function (PodcastProcessed $event) { // ... }); @@ -93,14 +132,14 @@ Typically, events should be registered via the `EventServiceProvider` `$listen` #### Queueable Anonymous Event Listeners -When registering closure based event listeners manually, you may wrap the listener closure within the `Illuminate\Events\queueable` function to instruct Laravel to execute the listener using the [queue](/docs/{{version}}/queues): +When registering closure based event listeners, you may wrap the listener closure within the `Illuminate\Events\queueable` function to instruct Laravel to execute the listener using the [queue](/docs/{{version}}/queues): use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; use Illuminate\Support\Facades\Event; /** - * Register any other events for your application. + * Bootstrap any application services. */ public function boot(): void { @@ -131,61 +170,12 @@ If you would like to handle anonymous queued listener failures, you may provide #### Wildcard Event Listeners -You may even register listeners using the `*` as a wildcard parameter, allowing you to catch multiple events on the same listener. Wildcard listeners receive the event name as their first argument and the entire event data array as their second argument: +You may also register listeners using the `*` character as a wildcard parameter, allowing you to catch multiple events on the same listener. Wildcard listeners receive the event name as their first argument and the entire event data array as their second argument: Event::listen('event.*', function (string $eventName, array $data) { // ... }); - -### Event Discovery - -Instead of registering events and listeners manually in the `$listen` array of the `EventServiceProvider`, you can enable automatic event discovery. When event discovery is enabled, Laravel will automatically find and register your events and listeners by scanning your application's `Listeners` directory. In addition, any explicitly defined events listed in the `EventServiceProvider` will still be registered. - -Laravel finds event listeners by scanning the listener classes using PHP's reflection services. When Laravel finds any listener class method that begins with `handle` or `__invoke`, Laravel will register those methods as event listeners for the event that is type-hinted in the method's signature: - - use App\Events\PodcastProcessed; - - class SendPodcastNotification - { - /** - * Handle the given event. - */ - public function handle(PodcastProcessed $event): void - { - // ... - } - } - -Event discovery is disabled by default, but you can enable it by overriding the `shouldDiscoverEvents` method of your application's `EventServiceProvider`: - - /** - * Determine if events and listeners should be automatically discovered. - */ - public function shouldDiscoverEvents(): bool - { - return true; - } - -By default, all listeners within your application's `app/Listeners` directory will be scanned. If you would like to define additional directories to scan, you may override the `discoverEventsWithin` method in your `EventServiceProvider`: - - /** - * Get the listener directories that should be used to discover events. - * - * @return array - */ - protected function discoverEventsWithin(): array - { - return [ - $this->app->path('Listeners'), - ]; - } - - -#### Event Discovery In Production - -In production, it is not efficient for the framework to scan all of your listeners on every request. Therefore, during your deployment process, you should run the `event:cache` Artisan command to cache a manifest of all of your application's events and listeners. This manifest will be used by the framework to speed up the event registration process. The `event:clear` command may be used to destroy the cache. - ## Defining Events @@ -217,7 +207,7 @@ As you can see, this event class contains no logic. It is a container for the `A ## Defining Listeners -Next, let's take a look at the listener for our example event. Event listeners receive event instances in their `handle` method. The `event:generate` and `make:listener` Artisan commands will automatically import the proper event class and type-hint the event on the `handle` method. Within the `handle` method, you may perform any actions necessary to respond to the event: +Next, let's take a look at the listener for our example event. Event listeners receive event instances in their `handle` method. The `make:listener` Artisan command, when invoked with the `--event` option, will automatically import the proper event class and type-hint the event in the `handle` method. Within the `handle` method, you may perform any actions necessary to respond to the event: [!NOTE] +> [!NOTE] > Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. @@ -257,7 +247,7 @@ Sometimes, you may wish to stop the propagation of an event to other listeners. Queueing listeners can be beneficial if your listener is going to perform a slow task such as sending an email or making an HTTP request. Before using queued listeners, make sure to [configure your queue](/docs/{{version}}/queues) and start a queue worker on your server or local development environment. -To specify that a listener should be queued, add the `ShouldQueue` interface to the listener class. Listeners generated by the `event:generate` and `make:listener` Artisan commands already have this interface imported into the current namespace so you can use it immediately: +To specify that a listener should be queued, add the `ShouldQueue` interface to the listener class. Listeners generated by the `make:listener` Artisan commands already have this interface imported into the current namespace so you can use it immediately: [!NOTE] +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -522,14 +512,14 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th return redirect('/orders'); } } - + If you would like to conditionally dispatch an event, you may use the `dispatchIf` and `dispatchUnless` methods: OrderShipped::dispatchIf($condition, $order); OrderShipped::dispatchUnless($condition, $order); -> [!NOTE] +> [!NOTE] > When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) make it a cinch. @@ -645,34 +635,25 @@ If your event listener methods are defined within the subscriber itself, you may ### Registering Event Subscribers -After writing the subscriber, you are ready to register it with the event dispatcher. You may register subscribers using the `$subscribe` property on the `EventServiceProvider`. For example, let's add the `UserEventSubscriber` to the list: +After writing the subscriber, you are ready to register it with the event dispatcher. You may register subscribers using the `subscribe` method of the `Event` facade. Typically, this should be done within the `boot` method of your application's `AppServiceProvider`: From e37a8ea854e9cca99770a22ee397cf31ac2136ef Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 10:15:53 -0600 Subject: [PATCH 1422/2609] formatting --- deployment.md | 14 ++++++++++++++ events.md | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index ab4da9d8efd..6bbc2c07f18 100644 --- a/deployment.md +++ b/deployment.md @@ -90,6 +90,20 @@ server { ## Optimization +When deploying your application to production, there are a variety of files that should be cached, including your configuration, events, routes, and views. Laravel provides a single, convenient `optimize` Artisan command that will cache all of these files. This command should typically be invoked as part of your application's deployment process: + +```shell +php artisan optimize +``` + +The `optimize:clear` method may be used to remove all of the cache files generated by the `optimize` command: + +```shell +php artisan optimize:clear +``` + +In the following documentation, we will discuss each of the granular optimization commands that are executed by the `optimize` command. + ### Caching Configuration diff --git a/events.md b/events.md index d5b45e71259..719b17954cf 100644 --- a/events.md +++ b/events.md @@ -83,7 +83,7 @@ php artisan event:list #### Event Discovery in Production -To give your application a speed boost, you should cache a manifest of all of your application's listeners using the `event:cache` Artisan command. Typically, this command should be run as part of your application's [deployment process](/docs/{{version}}/deployment#caching-events). This manifest will be used by the framework to speed up the event registration process. The `event:clear` command may be used to destroy the cache. +To give your application a speed boost, you should cache a manifest of all of your application's listeners using the `optimize` or `event:cache` Artisan commands. Typically, this command should be run as part of your application's [deployment process](/docs/{{version}}/deployment#optimization). This manifest will be used by the framework to speed up the event registration process. The `event:clear` command may be used to destroy the event cache. ### Manually Registering Events From 0da68310c4cec704d72978913a00982d5a871220 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Sun, 25 Feb 2024 17:23:15 +0100 Subject: [PATCH 1423/2609] [11.x] Clarify change behaviour in Laravel v11 (#9384) * Clarify change behaviour in Laravel v11 * Update upgrade.md * Update upgrade.md * Update upgrade.md * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- upgrade.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/upgrade.md b/upgrade.md index 4bdc9fa7a86..0c35d99befb 100644 --- a/upgrade.md +++ b/upgrade.md @@ -133,11 +133,34 @@ The base Eloquent model class now defines a `casts` method in order to support t **Likelihood Of Impact: High** -When modifying a column, you must now explicitly include all the modifiers you want to keep on the column definition after it is changed. Any missing attributes will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column, even if those attributes have been assigned to the column by a previous migration: +When modifying a column, you must now explicitly include all the modifiers you want to keep on the column definition after it is changed. Any missing attributes will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column, even if those attributes have been assigned to the column by a previous migration. + +For example, imagine you have a migration that creates a `votes` column with the `unsigned`, `default`, and `comment` attributes: + +```php +Schema::create('users', function (Blueprint $table) { + $table->integer('votes')->unsigned()->default(1)->comment('The vote count'); +}); +``` + +Later, you write a migration that changes the column to be `nullable` as well: + +```php +Schema::table('users', function (Blueprint $table) { + $table->integer('votes')->nullable()->change(); +}); +``` + +In Laravel 10, this migration would retain the `unsigned`, `default`, and `comment` attributes on the column. However, in Laravel 11, the migration must now also include all of the attributes that were previously defined on the column. Otherwise, they will be dropped: ```php Schema::table('users', function (Blueprint $table) { - $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); + $table->integer('votes') + ->unsigned() + ->default(1) + ->comment('The vote count') + ->nullable() + ->change(); }); ``` @@ -151,6 +174,14 @@ $table->bigIncrements('id')->primary()->change(); $table->char('postal_code', 10)->unique(false)->change(); ``` +If you do not want to update all of the existing "change" migrations in your application to retain the column's existing attributes, you may simply [squash your migrations](/docs/{{version}}/migrations#squashing-migrations): + +```bash +php artisan schema:dump +``` + +Once your migrations have been squashed, Laravel will "migrate" the database using your application's schema file before running any pending migrations. + #### Floating-Point Types From 8f6bacb4ab4695a50a5f7d93603676e457e9c76d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sun, 25 Feb 2024 16:56:47 +0000 Subject: [PATCH 1424/2609] [11.x] Adds more release notes (#9366) * Adds more release notes * Update releases.md Co-authored-by: Julius Kiekbusch * formatting * formatting * formatting * formatting * formatting --------- Co-authored-by: Julius Kiekbusch Co-authored-by: Taylor Otwell --- releases.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/releases.md b/releases.md index a7a34f9b65d..24d92f03755 100644 --- a/releases.md +++ b/releases.md @@ -65,7 +65,7 @@ Laravel 11 introduces a streamlined application structure for **new** Laravel ap #### The Application Bootstrap File -The `bootstrap/app.php` file has been revitalized as a code-first application configuration file. From this file, you may now customize your application's routing, middleware, service providers, exception handling, and more. This file unifies a variety of high-level application behavior settings that were previously scattered throughout your application's file structure. +The `bootstrap/app.php` file has been revitalized as a code-first application configuration file. From this file, you may now customize your application's routing, middleware, service providers, exception handling, and more. This file unifies a variety of high-level application behavior settings that were previously scattered throughout your application's file structure: ```php return Application::configure(basePath: dirname(__DIR__)) @@ -142,6 +142,19 @@ Like routing and middleware, exception handling can now be customized from your }) ``` +#### Base `Controller` Class + +The base controller included in new Laravel applications has been simplified. It no longer extends Laravel's internal `Controller` class, and the `AuthorizesRequests` and `ValidatesRequests` traits have been removed, as they may included on your application's individual controllers if desired: + + + */ + protected function casts(): array + { + return [ + 'options' => AsCollection::using(OptionCollection::class), + // AsEncryptedCollection::using(OptionCollection::class), + // AsEnumArrayObject::using(OptionEnum::class), + // AsEnumCollection::using(OptionEnum::class), + ]; + } + +For more information on attribute casting, review the [Eloquent documentation](/docs/{{version}}/eloquent-mutators#attribute-casting). + +### The `once` Function + +_The `once` helper was contributed by [Taylor Otwell](https://github.com/taylorotwell)_ and _[Nuno Maduro](https://github.com/nunomaduro)_. + +The `once` helper function executes the given callback and caches the result in memory for the duration of the request. Any subsequent calls to the `once` function with the same callback will return the previously cached result: + + function random(): int + { + return once(function () { + return random_int(1, 1000); + }); + } + + random(); // 123 + random(); // 123 (cached result) + random(); // 123 (cached result) + +For more information on the `once` helper, check out the [helpers documentation](/docs/{{version}}/helpers#method-once). + +### Improved Performance When Testing With In-Memory Databases + +_Improved in-memory database testing performance was contributed by [Anders Jenbo](https://github.com/AJenbo)_ + +Laravel 11 offers a significant speed boost when using the `:memory:` SQLite database during testing. To accomplish this, Laravel now maintains a reference to PHP's PDO object and reuses it across connections, often cutting total test run time in half. + +### Improved Support for MariaDB + +_Improved support for MariaDB was contributed by [Jonas Staudenmeir](https://github.com/staudenmeir) and [Julius Kiekbusch](https://github.com/Jubeki)_ + +Laravel 11 includes improved support for MariaDB. In previous Laravel releases, you could use MariaDB via Laravel's MySQL driver. However, Laravel 11 now includes a dedicated MariaDB driver which provides better defaults for this database system. + +For more information on Laravel's database drivers, check out the [database documentation](/docs/{{version}}/database). + +### Resend Mail Transport + +_The Resend mail transport was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. + +Laravel 11 includes a new `resend` mail transport that allows you to use the [Resend](https://resend.com) mail service to send your application's emails. + +For more information on sending emails in Laravel, check out the [mail documentation](/docs/{{version}}/mail). + From d3cfb8cd597e4260ca7b28cc91044fafd4cd0b8d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 14:04:37 -0600 Subject: [PATCH 1425/2609] document has middleware interface --- controllers.md | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/controllers.md b/controllers.md index d358edf67ca..bf1cef17c07 100644 --- a/controllers.md +++ b/controllers.md @@ -108,29 +108,49 @@ php artisan make:controller ProvisionServer --invokable Route::get('profile', [UserController::class, 'show'])->middleware('auth'); -Or, you may find it convenient to specify middleware within your controller's constructor. Using the `middleware` method within your controller's constructor, you can assign middleware to the controller's actions: +Or, you may find it convenient to specify middleware within your controller class. To do so, your controller should implement the `HasMiddleware` interface, which dictates that the controller should have a static `middleware` method. From this method, you may return an array of middleware that should be applied to the controller's actions: - class UserController extends Controller + middleware('auth'); - $this->middleware('log')->only('index'); - $this->middleware('subscribed')->except('store'); + return [ + 'auth', + new Middleware('log', only: ['index']), + new Middleware('subscribed', except: ['store']), + ]; } + + // ... } -Controllers also allow you to register middleware using a closure. This provides a convenient way to define an inline middleware for a single controller without defining an entire middleware class: +You may also define controller middleware as closures, which provides a convenient way to define an inline middleware without writing an entire middleware class: use Closure; use Illuminate\Http\Request; - $this->middleware(function (Request $request, Closure $next) { - return $next($request); - }); + /** + * Get the middleware that should be assigned to the controller. + */ + public static function middleware(): array + { + return [ + function (Request $request, Closure $next) { + return $next($request); + }, + ]; + } ## Resource Controllers From 745bab30b0c845db5bacadcc99309a8d1d6565c3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 14:12:58 -0600 Subject: [PATCH 1426/2609] document gate facade --- authorization.md | 60 ++++++++---------------------------------------- 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/authorization.md b/authorization.md index 52d30ff5ecc..32cc990d292 100644 --- a/authorization.md +++ b/authorization.md @@ -18,7 +18,7 @@ - [Policy Filters](#policy-filters) - [Authorizing Actions Using Policies](#authorizing-actions-using-policies) - [Via the User Model](#via-the-user-model) - - [Via Controller Helpers](#via-controller-helpers) + - [Via the Gate Facade](#via-the-gate-facade) - [Via Middleware](#via-middleware) - [Via Blade Templates](#via-blade-templates) - [Supplying Additional Context](#supplying-additional-context) @@ -564,10 +564,10 @@ Remember, some actions may correspond to policy methods like `create` that do no } } - -### Via Controller Helpers + +### Via the `Gate` Facade -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. +In addition to helpful methods provided to the `App\Models\User` model, you can always authorize actions via the `Gate` facade's `authorize` method. 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: @@ -579,6 +579,7 @@ Like the `can` method, this method accepts the name of the action you wish to au use App\Models\Post; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; + use Illuminate\Support\Facades\Gate; class PostController extends Controller { @@ -589,7 +590,7 @@ Like the `can` method, this method accepts the name of the action you wish to au */ public function update(Request $request, Post $post): RedirectResponse { - $this->authorize('update', $post); + Gate::authorize('update', $post); // The current user can update the blog post... @@ -605,6 +606,7 @@ As previously discussed, some policy methods like `create` do not require a mode use App\Models\Post; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; + use Illuminate\Support\Facades\Gate; /** * Create a new blog post. @@ -613,61 +615,17 @@ As previously discussed, some policy methods like `create` do not require a mode */ public function create(Request $request): RedirectResponse { - $this->authorize('create', Post::class); + Gate::authorize('create', Post::class); // The current user can create blog posts... return redirect('/posts'); } - -#### Authorizing Resource Controllers - -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 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. 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 | -| --- | --- | -| index | viewAny | -| show | view | -| create | create | -| store | create | -| edit | update | -| update | update | -| destroy | delete | - -
    - -> [!NOTE] -> 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: +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 application's `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; From 8a0e6147dfb07110e760fc081cc04bb7219bca3b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 14:16:31 -0600 Subject: [PATCH 1427/2609] wip --- upgrade.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/upgrade.md b/upgrade.md index 0c35d99befb..2055c7588ac 100644 --- a/upgrade.md +++ b/upgrade.md @@ -21,6 +21,7 @@
    +- [Carbon 3](#carbon-3) - [Per-Second Rate Limiting](#per-second-rate-limiting)
    @@ -291,6 +292,16 @@ The `Illuminate\Database\ConnectionInterface` interface has received a new `scal public function scalar($query, $bindings = [], $useReadPdo = true); ``` + +### Dates + + +#### Carbon 3 + +**Likelihood Of Impact: Medium** + +Laravel 11 supports both Carbon 2 and Carbon 3. Carbon is a date manipulation library utilized extensively by Laravel and packages throughout the ecosystem. If you install Carbon 3, you should review Carbon's [change log](https://github.com/briannesbitt/Carbon/releases/tag/3.0.0). + ### Mail From 005bf090553332bf5bd4a7921fa8531309e1de16 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 14:29:40 -0600 Subject: [PATCH 1428/2609] wip --- mail.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index da8a1ef3be5..7a42435fa0c 100644 --- a/mail.md +++ b/mail.md @@ -59,7 +59,16 @@ To use the Mailgun driver, install Symfony's Mailgun Mailer transport via Compos composer require symfony/mailgun-mailer symfony/http-client ``` -Next, set the `default` option in your application's `config/mail.php` configuration file to `mailgun`. After configuring your application's default mailer, add the following options to your `config/services.php` configuration file: +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: 'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), From 8b20622ce2a918a7af668f4c74e611c6cdb66ac0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 14:35:24 -0600 Subject: [PATCH 1429/2609] wip --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index 7a42435fa0c..7b572f0652d 100644 --- a/mail.md +++ b/mail.md @@ -59,7 +59,7 @@ To use the Mailgun driver, install Symfony's Mailgun Mailer transport via Compos 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: +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', From f0003ca1597701fc53048149d28ebbda87b249d6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 14:36:03 -0600 Subject: [PATCH 1430/2609] wip --- releases.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/releases.md b/releases.md index 24d92f03755..df687003020 100644 --- a/releases.md +++ b/releases.md @@ -339,11 +339,3 @@ Laravel 11 includes improved support for MariaDB. In previous Laravel releases, For more information on Laravel's database drivers, check out the [database documentation](/docs/{{version}}/database). -### Resend Mail Transport - -_The Resend mail transport was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. - -Laravel 11 includes a new `resend` mail transport that allows you to use the [Resend](https://resend.com) mail service to send your application's emails. - -For more information on sending emails in Laravel, check out the [mail documentation](/docs/{{version}}/mail). - From 04ee8c53b9799b4d9dc85a8b1b47d4fb12b6b039 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 14:37:33 -0600 Subject: [PATCH 1431/2609] wip --- releases.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/releases.md b/releases.md index df687003020..b84c393de32 100644 --- a/releases.md +++ b/releases.md @@ -82,12 +82,14 @@ return Application::configure(basePath: dirname(__DIR__)) })->create(); ``` + #### Service Providers Instead of the default Laravel application structure containing five service providers, Laravel 11 only includes a single `AppServiceProvider`. The functionality of the previous service providers has been incorporated into the `bootstrap/app.php`, is handled automatically by the framework, or may be placed in your application's `AppServiceProvider`. For example, event discovery is now enabled by default, largely eliminating the need for manual registration of events and their listeners. However, if you do need to manually register events, you may simply do so in the `AppServiceProvider`. Similarly, route model bindings or authorization gates you may have previously registered in the `AuthServiceProvider` may also be registered in the `AppServiceProvider`. + #### Opt-in API and Broadcast Routing The `api.php` and `channels.php` route files are no longer present by default, as many applications do not require these files. Instead, they may be created using simple Artisan commands: @@ -98,6 +100,7 @@ php artisan install:api php artisan install:broadcasting ``` + #### Middleware Previously, new Laravel applications included nine middleware. These middleware performed a variety of tasks such as authenticating requests, trimming input strings, and validating CSRF tokens. @@ -118,6 +121,7 @@ In Laravel 11, these middleware have been moved into the framework itself, so th Since all middleware can be easily customized via your application's `bootstrap/app.php`, the need for a separate HTTP "kernel" class has been eliminated. + #### Scheduling Using a new `Schedule` facade, scheduled tasks may now be defined directly in your application's `routes/console.php` file, eliminating the need for a separate console "kernel" class: @@ -128,6 +132,7 @@ use Illuminate\Support\Facades\Schedule; Schedule::command('emails:send')->daily(); ``` + #### Exception Handling Like routing and middleware, exception handling can now be customized from your application's `bootstrap/app.php` file instead of a separate exception handler class, reducing the overall number of files included in a new Laravel application: @@ -142,6 +147,7 @@ Like routing and middleware, exception handling can now be customized from your }) ``` + #### Base `Controller` Class The base controller included in new Laravel applications has been simplified. It no longer extends Laravel's internal `Controller` class, and the `AuthorizesRequests` and `ValidatesRequests` traits have been removed, as they may included on your application's individual controllers if desired: @@ -155,6 +161,7 @@ The base controller included in new Laravel applications has been simplified. It // } + #### Application Defaults By default, new Laravel applications use SQLite for database storage, as well as the `database` driver for Laravel's session, cache, and queue. This allows you to begin building your application immediately after creating a new Laravel application, without being required to install additional software or create additional database migrations. @@ -270,6 +277,7 @@ $job->assertReleased(delay: 30); For more information on testing queued jobs, check out the [queue documentation](/docs/{{version}}/queues#testing). + ### New Artisan Commands _Class creation Artisan commands were contributed by [Taylor Otwell](https://github.com/taylorotwell)_. @@ -283,6 +291,7 @@ php artisan make:interface php artisan make:trait ``` + ### Model Casts Improvements _Model casts improvements were contributed by [Nuno Maduro](https://github.com/nunomaduro)_. @@ -306,6 +315,7 @@ Laravel 11 supports defining your model's casts using a method instead of a prop For more information on attribute casting, review the [Eloquent documentation](/docs/{{version}}/eloquent-mutators#attribute-casting). + ### The `once` Function _The `once` helper was contributed by [Taylor Otwell](https://github.com/taylorotwell)_ and _[Nuno Maduro](https://github.com/nunomaduro)_. @@ -325,12 +335,14 @@ The `once` helper function executes the given callback and caches the result in For more information on the `once` helper, check out the [helpers documentation](/docs/{{version}}/helpers#method-once). + ### Improved Performance When Testing With In-Memory Databases _Improved in-memory database testing performance was contributed by [Anders Jenbo](https://github.com/AJenbo)_ Laravel 11 offers a significant speed boost when using the `:memory:` SQLite database during testing. To accomplish this, Laravel now maintains a reference to PHP's PDO object and reuses it across connections, often cutting total test run time in half. + ### Improved Support for MariaDB _Improved support for MariaDB was contributed by [Jonas Staudenmeir](https://github.com/staudenmeir) and [Julius Kiekbusch](https://github.com/Jubeki)_ From 71ba734947597e7b2084baf121bc388f5e52985b Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Mon, 26 Feb 2024 16:09:43 +0000 Subject: [PATCH 1432/2609] [11.x] Updates Reverb SSL documentation for Herd and Valet (#9404) * typos * update ssl docs for herd and valet * formatting --------- Co-authored-by: Taylor Otwell --- reverb.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/reverb.md b/reverb.md index 0bfd34e9afb..730864f4f58 100644 --- a/reverb.md +++ b/reverb.md @@ -96,7 +96,15 @@ For example, you may wish to maintain a single Laravel application which, via Re In most cases, secure WebSocket connections are likely to be handled by the upstream web server (Nginx, etc.) before the request is proxied to your Reverb server. -However, it can sometimes be useful, such as in local development, for the Reverb server to handle secure connections directly. You may achieve this by defining `tls` options in your application's `config/reverb.php` configuration file. Within the array of `tls` options, you may provide any of the options supported by [PHP's SSL context options](https://www.php.net/manual/en/context.ssl.php): +However, it can sometimes be useful, such as during local development, for the Reverb server to handle secure connections directly. If you are using [Laravel Herd](https://herd.laravel.com) and have secured the site or you are using [Laravel Valet](/docs/{{version}}/valet) and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may use the Herd / Valet certificate generated for your site to secure your Reverb connections. To do so, set the `REVERB_HOST` environment variable to your site's hostname or explicitly pass the hostname option when starting the Reverb server: + +```sh +php artisan reverb:start --host="0.0.0.0" --port=8080 --hostname="laravel.test" +``` + +Since Herd and Valet domains resolve to localhost, running the commmand above will result in your Reverb server being accessible via the secure WebSocket protocol (wss) at `wss://laravel.test:8080`. + +You may also manually choose a certificate by defining `tls` options in your application's `config/reverb.php` configuration file. Within the array of `tls` options, you may provide any of the options supported by [PHP's SSL context options](https://www.php.net/manual/en/context.ssl.php): ```php 'options' => [ @@ -117,7 +125,7 @@ php artisan reverb:start By default, the Reverb server will be started at `0.0.0.0:8080`, making it accessible from all network interfaces. -Ify you need to specify a custom host or port, you may do so via the `--host` and `--port` options when starting the server: +If you need to specify a custom host or port, you may do so via the `--host` and `--port` options when starting the server: ```sh php artisan reverb:start --host=127.0.0.1 --port=9000 @@ -161,7 +169,7 @@ Each WebSocket connection is held in memory until either the client or server di #### Operating System -On a Unix based operating system, you make determine the allowed number of open files using the `ulimit` command: +On a Unix based operating system, you may determine the allowed number of open files using the `ulimit` command: ```sh ulimit -n From bb3d1ed031493fd6347093d49b34eb5d1c8c923c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 26 Feb 2024 16:29:54 +0000 Subject: [PATCH 1433/2609] [11.x] Rewrites `authentification` for L11 (#9403) * Rewrites `authentification` * formatting --------- Co-authored-by: Taylor Otwell --- authentication.md | 119 +++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 76 deletions(-) diff --git a/authentication.md b/authentication.md index 2e5af7465dc..3b668bafa81 100644 --- a/authentication.md +++ b/authentication.md @@ -181,7 +181,7 @@ To determine if the user making the incoming HTTP request is authenticated, you ### 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 application's 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 is an alias for the `Illuminate\Auth\Middleware\Authenticate` middleware class. Since this middleware is already aliased internally by Laravel, all you need to do is attach the middleware to a route definition: Route::get('/flights', function () { // Only authenticated users may access this route... @@ -190,17 +190,16 @@ To determine if the user making the incoming HTTP request is authenticated, you #### Redirecting Unauthenticated Users -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: +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 using the method `redirectGuestsTo` of your application's `bootstrap/app.php` file: use Illuminate\Http\Request; - /** - * Get the path the user should be redirected to. - */ - protected function redirectTo(Request $request): string - { - return route('login'); - } + ->withMiddleware(function (Middleware $middleware) { + $middleware->redirectGuestsTo('/login'); + + // Using a closure... + $middleware->redirectGuestsTo(fn (Request $request) => route('login')); + }) #### Specifying a Guard @@ -458,7 +457,7 @@ In addition to calling the `logout` method, it is recommended that you invalidat 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 included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` route middleware alias as defined in your application's HTTP kernel: +Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` route middleware alias: Route::middleware(['auth', 'auth.session'])->group(function () { Route::get('/', function () { @@ -540,7 +539,7 @@ You should ensure that any route that performs an action which requires recent p ## Adding Custom Guards -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: +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 `AppServiceProvider`, we can place the code in that provider: getAuthPassword()` to the value of `$credentials['password']`. This method should return `true` or `false` indicating whether the password is valid. +The `rehashPasswordIfRequired` method should rehash the given `$user`'s password if required and supported. For example, this method will typically use the `Hash::needsRehash` method to determine if the `$credentials['password']` value needs to be rehashed. If the password needs to be rehashed, the method should use the `Hash::make` method to rehash the password and update the user's record in the underlying persistent storage. + ### The Authenticatable Contract @@ -699,72 +705,33 @@ Now that we have explored each of the methods on the `UserProvider`, let's take { public function getAuthIdentifierName(); public function getAuthIdentifier(); + public function getAuthPasswordName(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName(); } -This interface is simple. The `getAuthIdentifierName` method should return the name of the "primary key" field of the user and the `getAuthIdentifier` method should return the "primary key" of the user. When using a MySQL back-end, this would likely be the auto-incrementing primary key assigned to the user record. The `getAuthPassword` method should return the user's hashed password. +This interface is simple. The `getAuthIdentifierName` method should return the name of the "primary key" column for the user and the `getAuthIdentifier` method should return the "primary key" of the user. When using a MySQL back-end, this would likely be the auto-incrementing primary key assigned to the user record. The `getAuthPasswordName` method should return the name of the user's password column. The `getAuthPassword` method should return the user's hashed password. This interface allows the authentication system to work with any "user" class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes an `App\Models\User` class in the `app/Models` directory which implements this interface. ## Events -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. - * - * @var array - */ - protected $listen = [ - 'Illuminate\Auth\Events\Registered' => [ - 'App\Listeners\LogRegisteredUser', - ], - - 'Illuminate\Auth\Events\Attempting' => [ - 'App\Listeners\LogAuthenticationAttempt', - ], - - 'Illuminate\Auth\Events\Authenticated' => [ - 'App\Listeners\LogAuthenticated', - ], - - 'Illuminate\Auth\Events\Login' => [ - 'App\Listeners\LogSuccessfulLogin', - ], - - 'Illuminate\Auth\Events\Failed' => [ - 'App\Listeners\LogFailedLogin', - ], - - 'Illuminate\Auth\Events\Validated' => [ - 'App\Listeners\LogValidated', - ], - - 'Illuminate\Auth\Events\Verified' => [ - 'App\Listeners\LogVerified', - ], - - 'Illuminate\Auth\Events\Logout' => [ - 'App\Listeners\LogSuccessfulLogout', - ], - - 'Illuminate\Auth\Events\CurrentDeviceLogout' => [ - 'App\Listeners\LogCurrentDeviceLogout', - ], - - 'Illuminate\Auth\Events\OtherDeviceLogout' => [ - 'App\Listeners\LogOtherDeviceLogout', - ], - - 'Illuminate\Auth\Events\Lockout' => [ - 'App\Listeners\LogLockout', - ], - - 'Illuminate\Auth\Events\PasswordReset' => [ - 'App\Listeners\LogPasswordReset', - ], - ]; +Laravel dispatches a variety of [events](/docs/{{version}}/events) during the authentication process. You may [define listeners](/docs/{{version}}/events) for any of the following events: + +Event Name | +------------- | +`Illuminate\Auth\Events\Registered` | +`Illuminate\Auth\Events\Attempting` | +`Illuminate\Auth\Events\Authenticated` | +`Illuminate\Auth\Events\Login` | +`Illuminate\Auth\Events\Failed` | +`Illuminate\Auth\Events\Validated` | +`Illuminate\Auth\Events\Verified` | +`Illuminate\Auth\Events\Logout` | +`Illuminate\Auth\Events\CurrentDeviceLogout` | +`Illuminate\Auth\Events\OtherDeviceLogout` | +`Illuminate\Auth\Events\Lockout` | +`Illuminate\Auth\Events\PasswordRese` | From 99fca39089fde7361fa3ddb233f57077385bc002 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 26 Feb 2024 16:39:53 +0000 Subject: [PATCH 1434/2609] [11.x] Rewrites `scheduling` for L11 (#9402) * Rewrites `scheduling` * formatting * Update scheduling.md --------- Co-authored-by: Taylor Otwell --- scheduling.md | 192 ++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 98 deletions(-) diff --git a/scheduling.md b/scheduling.md index b9932b67dbe..147547f76c0 100644 --- a/scheduling.md +++ b/scheduling.md @@ -23,37 +23,25 @@ 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 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. +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 typically defined in your application's `routes/console.php` file. ## Defining Schedules -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: +You may define all of your scheduled tasks in your application's `routes/console.php` file. 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(function () { - DB::table('recent_users')->delete(); - })->daily(); - } - } + Schedule::call(function () { + DB::table('recent_users')->delete(); + })->daily(); In addition to scheduling using closures, you may also schedule [invokable objects](https://secure.php.net/manual/en/language.oop5.magic.php#object.invoke). Invokable objects are simple PHP classes that contain an `__invoke` method: - $schedule->call(new DeleteRecentUsers)->daily(); + Schedule::call(new DeleteRecentUsers)->daily(); If you would like to view an overview of your scheduled tasks and the next time they are scheduled to run, you may use the `schedule:list` Artisan command: @@ -69,10 +57,26 @@ In addition to scheduling closures, you may also schedule [Artisan commands](/do 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\SendEmailsCommand; + use Illuminate\Support\Facades\Schedule; + + Schedule::command('emails:send Taylor --force')->daily(); - $schedule->command('emails:send Taylor --force')->daily(); + Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily(); - $schedule->command(SendEmailsCommand::class, ['Taylor', '--force'])->daily(); + +#### Scheduling Artisan Closure Commands + +If you want to schedule an Artisan command defined by a closure, you may chain the scheduling related methods after the command's definition: + + Artisan::command('delete:recent-users', function () { + DB::table('recent_users')->delete(); + })->purpose('Delete recent users')->daily(); + +If you need to pass arguments to the closure command, you may provide them to the `schedule` method: + + Artisan::command('emails:send {user} {--force}', function ($user) { + // ... + })->purpose('Send emails to the specified user')->schedule(['Taylor', '--force'])->daily(); ### Scheduling Queued Jobs @@ -80,22 +84,26 @@ When scheduling Artisan commands using the command's class name, you may pass an 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; + use Illuminate\Support\Facades\Schedule; - $schedule->job(new Heartbeat)->everyFiveMinutes(); + Schedule::job(new Heartbeat)->everyFiveMinutes(); Optional second and third arguments may be provided to the `job` method which specifies the queue name and queue connection that should be used to queue the job: use App\Jobs\Heartbeat; + use Illuminate\Support\Facades\Schedule; // Dispatch the job to the "heartbeats" queue on the "sqs" connection... - $schedule->job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes(); + Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes(); ### Scheduling Shell Commands The `exec` method may be used to issue a command to the operating system: - $schedule->exec('node /home/forge/script.js')->daily(); + use Illuminate\Support\Facades\Schedule; + + Schedule::exec('node /home/forge/script.js')->daily(); ### Schedule Frequency Options @@ -149,13 +157,15 @@ Method | Description These methods may be combined with additional constraints to create even more finely tuned schedules that only run on certain days of the week. For example, you may schedule a command to run weekly on Monday: + use Illuminate\Support\Facades\Schedule; + // Run once per week on Monday at 1 PM... - $schedule->call(function () { + Schedule::call(function () { // ... })->weekly()->mondays()->at('13:00'); // Run hourly from 8 AM to 5 PM on weekdays... - $schedule->command('foo') + Schedule::command('foo') ->weekdays() ->hourly() ->timezone('America/Chicago') @@ -189,15 +199,18 @@ 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('emails:send') + use Illuminate\Support\Facades\Schedule; + + 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\Support\Facades; use Illuminate\Console\Scheduling\Schedule; - $schedule->command('emails:send') + Facades\Schedule::command('emails:send') ->hourly() ->days([Schedule::SUNDAY, Schedule::WEDNESDAY]); @@ -206,13 +219,13 @@ Alternatively, you may use the constants available on the `Illuminate\Console\Sc The `between` method may be used to limit the execution of a task based on the time of day: - $schedule->command('emails: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('emails:send') + Schedule::command('emails:send') ->hourly() ->unlessBetween('23:00', '4:00'); @@ -221,13 +234,13 @@ Similarly, the `unlessBetween` method can be used to exclude the execution of a 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 () { + Schedule::command('emails:send')->daily()->when(function () { return true; }); The `skip` method may be seen as the inverse of `when`. If the `skip` method returns `true`, the scheduled task will not be executed: - $schedule->command('emails:send')->daily()->skip(function () { + Schedule::command('emails:send')->daily()->skip(function () { return true; }); @@ -238,7 +251,7 @@ When using chained `when` methods, the scheduled command will only execute if al 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') + Schedule::command('emails:send') ->daily() ->environments(['staging', 'production']); @@ -247,21 +260,17 @@ The `environments` method may be used to execute tasks only on the given environ Using the `timezone` method, you may specify that a scheduled task's time should be interpreted within a given timezone: - $schedule->command('report:generate') + use Illuminate\Support\Facades\Schedule; + + Schedule::command('report:generate') ->timezone('America/New_York') ->at('2:00') -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: +If you are repeatedly assigning the same timezone to all of your scheduled tasks, you can specify which timezone should be assigned to all schedules by defining a `schedule_timezone` option within your application's `app` configuration file: - use DateTimeZone; + 'timezone' => env('APP_TIMEZONE', 'UTC'), - /** - * Get the timezone that should be used by default for scheduled events. - */ - protected function scheduleTimezone(): DateTimeZone|string|null - { - return 'America/Chicago'; - } + 'schedule_timezone' => 'America/Chicago', > [!WARNING] > Remember that some timezones utilize daylight savings time. When daylight saving time changes occur, your scheduled task may run twice or even not run at all. For this reason, we recommend avoiding timezone scheduling when possible. @@ -271,13 +280,15 @@ If you are repeatedly assigning the same timezone to all of your scheduled tasks By default, scheduled tasks will be run even if the previous instance of the task is still running. To prevent this, you may use the `withoutOverlapping` method: - $schedule->command('emails:send')->withoutOverlapping(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('emails:send')->withoutOverlapping(); In this example, the `emails:send` [Artisan command](/docs/{{version}}/artisan) will be run every minute if it is not already running. The `withoutOverlapping` method is especially useful if you have tasks that vary drastically in their execution time, preventing you from predicting exactly how long a given task will take. If needed, you may specify how many minutes must pass before the "without overlapping" lock expires. By default, the lock will expire after 24 hours: - $schedule->command('emails:send')->withoutOverlapping(10); + Schedule::command('emails:send')->withoutOverlapping(10); Behind the scenes, the `withoutOverlapping` method utilizes your application's [cache](/docs/{{version}}/cache) to obtain locks. If necessary, you can clear these cache locks using the `schedule:clear-cache` Artisan command. This is typically only necessary if a task becomes stuck due to an unexpected server problem. @@ -291,7 +302,9 @@ If your application's scheduler is running on multiple servers, you may limit a 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: - $schedule->command('report:generate') + use Illuminate\Support\Facades\Schedule; + + Schedule::command('report:generate') ->fridays() ->at('17:00') ->onOneServer(); @@ -302,12 +315,12 @@ To indicate that the task should run on only one server, use the `onOneServer` m Sometimes you may need to schedule the same job to be dispatched with different parameters, while still instructing Laravel to run each permutation of the job on a single server. To accomplish this, you may assign each schedule definition a unique name via the `name` method: ```php -$schedule->job(new CheckUptime('https://laravel.com/')) +Schedule::job(new CheckUptime('https://laravel.com/')) ->name('check_uptime:laravel.com') ->everyFiveMinutes() ->onOneServer(); -$schedule->job(new CheckUptime('https://vapor.laravel.com/')) +Schedule::job(new CheckUptime('https://vapor.laravel.com/')) ->name('check_uptime:vapor.laravel.com') ->everyFiveMinutes() ->onOneServer(); @@ -316,7 +329,7 @@ $schedule->job(new CheckUptime('https://vapor.laravel.com/')) Similarly, scheduled closures must be assigned a name if they are intended to be run on one server: ```php -$schedule->call(fn () => User::resetApiRequestCount()) +Schedule::call(fn () => User::resetApiRequestCount()) ->name('reset-api-request-count') ->daily() ->onOneServer(); @@ -328,7 +341,9 @@ $schedule->call(fn () => User::resetApiRequestCount()) 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') + use Illuminate\Support\Facades\Schedule; + + Schedule::command('analytics:report') ->daily() ->runInBackground(); @@ -340,7 +355,7 @@ By default, multiple tasks scheduled at the same time will execute sequentially 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(); + Schedule::command('emails:send')->evenInMaintenanceMode(); ## Running the Scheduler @@ -358,7 +373,9 @@ So, when using Laravel's scheduler, we only need to add a single cron configurat On most operating systems, cron jobs are limited to running a maximum of once per minute. However, Laravel's scheduler allows you to schedule tasks to run at more frequent intervals, even as often as once per second: - $schedule->call(function () { + use Illuminate\Support\Facades\Schedule; + + Schedule::call(function () { DB::table('recent_users')->delete(); })->everySecond(); @@ -368,9 +385,9 @@ Since sub-minute tasks that take longer than expected to run could delay the exe use App\Jobs\DeleteRecentUsers; - $schedule->job(new DeleteRecentUsers)->everyTenSeconds(); + Schedule::job(new DeleteRecentUsers)->everyTenSeconds(); - $schedule->command('users:delete')->everyTenSeconds()->runInBackground(); + Schedule::command('users:delete')->everyTenSeconds()->runInBackground(); #### Interrupting Sub-Minute Tasks @@ -397,26 +414,28 @@ php artisan schedule:work The Laravel scheduler provides several convenient methods for working with the output generated by scheduled tasks. First, using the `sendOutputTo` method, you may send the output to a file for later inspection: - $schedule->command('emails:send') + use Illuminate\Support\Facades\Schedule; + + Schedule::command('emails:send') ->daily() ->sendOutputTo($filePath); If you would like to append the output to a given file, you may use the `appendOutputTo` method: - $schedule->command('emails:send') + Schedule::command('emails:send') ->daily() ->appendOutputTo($filePath); 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('report:generate') + Schedule::command('report:generate') ->daily() ->sendOutputTo($filePath) ->emailOutputTo('taylor@example.com'); 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('report:generate') + Schedule::command('report:generate') ->daily() ->emailOutputOnFailure('taylor@example.com'); @@ -428,7 +447,9 @@ If you only want to email the output if the scheduled Artisan or system command 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') + use Illuminate\Support\Facades\Schedule; + + Schedule::command('emails:send') ->daily() ->before(function () { // The task is about to execute... @@ -439,7 +460,7 @@ Using the `before` and `after` methods, you may specify code to be executed befo 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') + Schedule::command('emails:send') ->daily() ->onSuccess(function () { // The task succeeded... @@ -452,7 +473,7 @@ If output is available from your command, you may access it in your `after`, `on use Illuminate\Support\Stringable; - $schedule->command('emails:send') + Schedule::command('emails:send') ->daily() ->onSuccess(function (Stringable $output) { // The task succeeded... @@ -466,59 +487,34 @@ If output is available from your command, you may access it in your `after`, `on 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') + Schedule::command('emails:send') ->daily() ->pingBefore($url) ->thenPing($url); The `pingBeforeIf` and `thenPingIf` methods may be used to ping a given URL only if a given condition is `true`: - $schedule->command('emails:send') + 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. A failure indicates that the scheduled Artisan or system command terminated with a non-zero exit code: - $schedule->command('emails:send') + Schedule::command('emails:send') ->daily() ->pingOnSuccess($successUrl) ->pingOnFailure($failureUrl); -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: - -```shell -composer require guzzlehttp/guzzle -``` - ## Events -If needed, you may listen to [events](/docs/{{version}}/events) dispatched by the scheduler. Typically, event listener mappings will be defined within your application's `App\Providers\EventServiceProvider` class: - - /** - * The event listener mappings for the application. - * - * @var array - */ - protected $listen = [ - 'Illuminate\Console\Events\ScheduledTaskStarting' => [ - 'App\Listeners\LogScheduledTaskStarting', - ], - - 'Illuminate\Console\Events\ScheduledTaskFinished' => [ - 'App\Listeners\LogScheduledTaskFinished', - ], - - 'Illuminate\Console\Events\ScheduledBackgroundTaskFinished' => [ - 'App\Listeners\LogScheduledBackgroundTaskFinished', - ], - - 'Illuminate\Console\Events\ScheduledTaskSkipped' => [ - 'App\Listeners\LogScheduledTaskSkipped', - ], - - 'Illuminate\Console\Events\ScheduledTaskFailed' => [ - 'App\Listeners\LogScheduledTaskFailed', - ], - ]; +Laravel dispatches a variety of [events](/docs/{{version}}/events) during the scheduling process. You may [define listeners](/docs/{{version}}/events) for any of the following events: + +Event Name | +------------- | +`Illuminate\Console\Events\ScheduledTaskStarting` | +`Illuminate\Console\Events\ScheduledTaskFinished` | +`Illuminate\Console\Events\ScheduledBackgroundTaskFinished` | +`Illuminate\Console\Events\ScheduledTaskSkipped` | +`Illuminate\Console\Events\ScheduledTaskFailed` | From e93c4d4040037bdc107de3e0caf6e75fcb2057cd Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 26 Feb 2024 19:39:26 +0000 Subject: [PATCH 1435/2609] Adjusts for L11 (#9401) --- rate-limiting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rate-limiting.md b/rate-limiting.md index 5d7e1979dcf..4b120d51231 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -19,7 +19,7 @@ Laravel includes a simple to use rate limiting abstraction which, in conjunction Typically, the rate limiter utilizes your default application cache as defined by the `default` key within your application's `cache` configuration file. However, you may specify which cache driver the rate limiter should use by defining a `limiter` key within your application's `cache` configuration file: - 'default' => 'memcached', + 'default' => env('CACHE_STORE', 'database'), 'limiter' => 'redis', From 49850a762cbc73d08e51c26d736f2f5b7e11625d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 26 Feb 2024 13:56:14 -0600 Subject: [PATCH 1436/2609] wip --- eloquent-resources.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index 05d38e812e0..2623c7412b5 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -334,24 +334,6 @@ By default, your outermost resource is wrapped in a `data` key when the resource } ``` -If you would like to use a custom key instead of `data`, you may define a `$wrap` attribute on the resource class: - - Date: Mon, 26 Feb 2024 13:59:40 -0600 Subject: [PATCH 1437/2609] wip --- strings.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/strings.md b/strings.md index 01855fdca32..fd9a4c25150 100644 --- a/strings.md +++ b/strings.md @@ -559,6 +559,10 @@ The `Str::isUrl` method determines if the given string is a valid URL: // false +The `isUrl` method considers a wide range of protocols as valid. However, you may specify the only protocols that should be considered valid by providing them to the `isUrl` method: + + $isUrl = Str::isUrl('http://example.com/', ['http', 'https']); + #### `Str::isUlid()` {.collection-method} @@ -1758,6 +1762,10 @@ The `isUrl` method determines if a given string is a URL: // false +The `isUrl` method considers a wide range of protocols as valid. However, you may specify the only protocols that should be considered valid by providing them to the `isUrl` method: + + $result = Str::of('http://example.com/')->isUrl(['http', 'https']); + #### `isUuid` {.collection-method} From ce23398eb35d1857f884899882177af4ab11fc5e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 26 Feb 2024 14:00:59 -0600 Subject: [PATCH 1438/2609] wip --- strings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings.md b/strings.md index fd9a4c25150..a940880fecc 100644 --- a/strings.md +++ b/strings.md @@ -559,7 +559,7 @@ The `Str::isUrl` method determines if the given string is a valid URL: // false -The `isUrl` method considers a wide range of protocols as valid. However, you may specify the only protocols that should be considered valid by providing them to the `isUrl` method: +The `isUrl` method considers a wide range of protocols as valid. However, you may specify the protocols that should be considered valid by providing them to the `isUrl` method: $isUrl = Str::isUrl('http://example.com/', ['http', 'https']); @@ -1762,7 +1762,7 @@ The `isUrl` method determines if a given string is a URL: // false -The `isUrl` method considers a wide range of protocols as valid. However, you may specify the only protocols that should be considered valid by providing them to the `isUrl` method: +The `isUrl` method considers a wide range of protocols as valid. However, you may specify the protocols that should be considered valid by providing them to the `isUrl` method: $result = Str::of('http://example.com/')->isUrl(['http', 'https']); From d35a3994d0018810896d46d06f6b3d3ce4f25216 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 26 Feb 2024 14:10:51 -0600 Subject: [PATCH 1439/2609] wip --- upgrade.md | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/upgrade.md b/upgrade.md index 2055c7588ac..ac7937029a0 100644 --- a/upgrade.md +++ b/upgrade.md @@ -10,8 +10,8 @@ - [Updating Dependencies](#updating-dependencies) - [Updating Minimum Stability](#updating-minimum-stability) - [Application Structure](#application-structure) -- [Modifying Columns](#modifying-columns) - [Floating-Point Types](#floating-point-types) +- [Modifying Columns](#modifying-columns) - [SQLite Minimum Version](#sqlite-minimum-version)
    @@ -31,10 +31,12 @@
    -- [The `Enumerable` Contract](#the-enumerable-contract) +- [Doctrine DBAL Removal](#doctrine-dbal-removal) - [Eloquent Model `casts` Method](#eloquent-model-casts-method) - [Spatial Types](#spatial-types) -- [Doctrine DBAL Removal](#doctrine-dbal-removal) +- [The `Enumerable` Contract](#the-enumerable-contract) +- [The `UserProvider` Contract](#the-user-provider-contract) +- [The `Authenticatable` Contract](#the-authenticatable-contract)
    @@ -78,8 +80,11 @@ However, we do **not recommend** that Laravel 10 applications upgrading to Larav ### Authentication + #### The `UserProvider` Contract +**Likelihood Of Impact: Low** + The `Illuminate\Contracts\Auth\UserProvider` contract has received a new `rehashPasswordIfRequired` method. This method is responsible for re-hashing and storing the user's password in storage when the application's hashing algorithm work factor has changed. If your application or package defines a class that implements this interface, you should add the new `rehashPasswordIfRequired` method to your implementation. A reference implementation can be found within the `Illuminate\Auth\EloquentUserProvider` class: @@ -88,6 +93,24 @@ If your application or package defines a class that implements this interface, y public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false); ``` + +#### The `Authenticatable` Contract + +**Likelihood Of Impact: Low** + +The `Illuminate\Contracts\Auth\Authenticatable` contract has received a new `getAuthPasswordName` method. This method is responsible for returning the name of your authenticatable entity's password column. + +If your application or package defines a class that implements this interface, you should add the new `getAuthPasswordName` method to your implementation: + +```php +public function getAuthPasswordName() +{ + return 'password'; +} +``` + +The default `User` model included with Laravel receives this method automatically since the method is included within the `Illuminate\Auth\Authenticatable` trait. + ### Cache From 0a02446a856f883ddf1990a981393e4cb600e883 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 26 Feb 2024 22:23:40 +0000 Subject: [PATCH 1440/2609] [11.x] Rewrites `veritification` for L11 (#9407) * Rewrites `veritification` * formatting --------- Co-authored-by: Taylor Otwell --- verification.md | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/verification.md b/verification.md index 86da18c27fb..8807022c920 100644 --- a/verification.md +++ b/verification.md @@ -39,7 +39,7 @@ Before getting started, verify that your `App\Models\User` model implements the // ... } -Once this interface has been added to your model, newly registered users will automatically be sent an email containing an email verification link. As you can see by examining your application's `App\Providers\EventServiceProvider`, Laravel already contains a `SendEmailVerificationNotification` [listener](/docs/{{version}}/events) that is attached to the `Illuminate\Auth\Events\Registered` event. This event listener will send the email verification link to the user. +Once this interface has been added to your model, newly registered users will automatically be sent an email containing an email verification link. This happens seamlessly because Laravel automatically registers the `Illuminate\Auth\Listeners\SendEmailVerificationNotification` [listener](/docs/{{version}}/events) for the `Illuminate\Auth\Events\Registered` event. If you are manually implementing registration within your application instead of using [a starter kit](/docs/{{version}}/starter-kits), you should ensure that you are dispatching the `Illuminate\Auth\Events\Registered` event after a user's registration is successful: @@ -50,11 +50,7 @@ If you are manually implementing registration within your application instead of ### Database Preparation -Next, your `users` 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: - -```shell -php artisan migrate -``` +Next, your `users` table must contain an `email_verified_at` column to store the date and time that the user's email address was verified. Typically, this is included in Laravel's default `0001_01_01_000000_create_users_table.php` database migration. ## Routing @@ -112,7 +108,7 @@ Sometimes a user may misplace or accidentally delete the email address verificat ### Protecting Routes -[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware alias, which is an alias for 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. Typically, this middleware is paired with the `auth` middleware: +[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware alias, which is an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` class. Since this alias is already automatically registered by Laravel, all you need to do is attach the `verified` middleware to a route definition. Typically, this middleware is paired with the `auth` middleware: Route::get('/profile', function () { // Only verified users may access this route... @@ -128,13 +124,13 @@ If an unverified user attempts to access a route that has been assigned this mid Although the default email verification notification should satisfy the requirements of most applications, Laravel allows you to customize how the email verification mail message is constructed. -To get started, pass a closure to the `toMailUsing` method provided by the `Illuminate\Auth\Notifications\VerifyEmail` notification. The closure will receive the notifiable model instance that is receiving the notification as well as the signed email verification URL that the user must visit to verify their email address. The closure should return an instance of `Illuminate\Notifications\Messages\MailMessage`. Typically, you should call the `toMailUsing` method from the `boot` method of your application's `App\Providers\AuthServiceProvider` class: +To get started, pass a closure to the `toMailUsing` method provided by the `Illuminate\Auth\Notifications\VerifyEmail` notification. The closure will receive the notifiable model instance that is receiving the notification as well as the signed email verification URL that the user must visit to verify their email address. The closure should return an instance of `Illuminate\Notifications\Messages\MailMessage`. Typically, you should call the `toMailUsing` method from the `boot` method of your application's `AppServiceProvider` class: use Illuminate\Auth\Notifications\VerifyEmail; use Illuminate\Notifications\Messages\MailMessage; /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -154,18 +150,4 @@ To get started, pass a closure to the `toMailUsing` method provided by the `Illu ## Events -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`: - - use App\Listeners\LogVerifiedUser; - use Illuminate\Auth\Events\Verified; - - /** - * The event listener mappings for the application. - * - * @var array - */ - protected $listen = [ - Verified::class => [ - LogVerifiedUser::class, - ], - ]; +When using the [Laravel application starter kits](/docs/{{version}}/starter-kits), Laravel dispatches an `Illuminate\Auth\Events\Verified` [event](/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. From cd628b55f343496f8bda85de5521b0ab497762e7 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 26 Feb 2024 22:28:53 +0000 Subject: [PATCH 1441/2609] [11.x] Rewrites `hashing` for L11 (#9408) * Rewrites `hashing` * formatting * formatting * Update hashing.md --------- Co-authored-by: Taylor Otwell --- hashing.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hashing.md b/hashing.md index 2c0320f201a..f2a79e72685 100644 --- a/hashing.md +++ b/hashing.md @@ -17,7 +17,13 @@ Bcrypt is a great choice for hashing passwords because its "work factor" is adju ## Configuration -The default hashing driver for your application is configured in your application's `config/hashing.php` configuration file. There are currently several supported drivers: [Bcrypt](https://en.wikipedia.org/wiki/Bcrypt) and [Argon2](https://en.wikipedia.org/wiki/Argon2) (Argon2i and Argon2id variants). +By default, Laravel uses the `bcrypt` hashing driver when hashing data. However, several other hashing drivers are supported, including [`argon`](https://en.wikipedia.org/wiki/Argon2) and [`argon2id`](https://en.wikipedia.org/wiki/Argon2). + +You may specify your application's hashing driver using the `HASH_DRIVER` environment variable. But, if you want to customize all of Laravel's hashing driver options, you should publish the complete `hashing` configuration file using the `config:publish` Artisan command: + +```bash +php artisan config:publish hashing +``` ## Basic Usage From f38db859448073e894edb4824f0874064e8f7bbe Mon Sep 17 00:00:00 2001 From: Caleb Porzio Date: Mon, 26 Feb 2024 17:29:25 -0500 Subject: [PATCH 1442/2609] Update telescope.md (#9406) --- telescope.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telescope.md b/telescope.md index 2222ba8e167..17d85010c95 100644 --- a/telescope.md +++ b/telescope.md @@ -56,6 +56,8 @@ php artisan telescope:install php artisan migrate ``` +Finally, you may access the Telescope dashboard via the `/telescope` route. + #### Migration Customization From 8a39c813959c91e14e189bc20b78a3f1172aab4d Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Tue, 27 Feb 2024 12:11:18 +0000 Subject: [PATCH 1443/2609] add closing brace (#9411) --- reverb.md | 1 + 1 file changed, 1 insertion(+) diff --git a/reverb.md b/reverb.md index 730864f4f58..1bbdd258232 100644 --- a/reverb.md +++ b/reverb.md @@ -221,6 +221,7 @@ server { } ... +} ``` Typically, web servers are configured to limit the number of allowed connections in order to prevent overloading the server. To increase the number of allowed connections on an Nginx web server to 10,000, the `worker_rlimit_nofile` and `worker_connections` values of the `nginx.conf` file should be updated: From 7c9881b395966c63cfd4982553052fed71d703d4 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 27 Feb 2024 12:26:20 +0000 Subject: [PATCH 1444/2609] Rewrites `queries` for L11 (#9413) --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index ce92e35c60a..a57016c60a9 100644 --- a/queries.md +++ b/queries.md @@ -484,7 +484,7 @@ The `whereNot` and `orWhereNot` methods may be used to negate a given group of q ### 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.39.0 (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator: +Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 8.0+, PostgreSQL 12.0+, SQL Server 2017+, and SQLite 3.39.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') From cfbfddb4fb8ef44c6e1e4293f38201740f5aafbf Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 27 Feb 2024 12:29:15 +0000 Subject: [PATCH 1445/2609] [11.x] Rewrites `database` for L11 (#9410) * Rewrites `database` * Update database.md --------- Co-authored-by: Taylor Otwell --- database.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/database.md b/database.md index 83b5f69650a..3f52ca1634a 100644 --- a/database.md +++ b/database.md @@ -44,12 +44,15 @@ DB_CONNECTION=sqlite DB_DATABASE=/absolute/path/to/database.sqlite ``` -To enable foreign key constraints for SQLite connections, you should set the `DB_FOREIGN_KEYS` environment variable to `true`: +By default, foreign key constraints are enabled for SQLite connections. If you would like to disable them, you should set the `DB_FOREIGN_KEYS` environment variable to `false`: ```ini -DB_FOREIGN_KEYS=true +DB_FOREIGN_KEYS=false ``` +> [!NOTE] +> If you use the [Laravel installer](/docs/{{version}}/installation#creating-a-laravel-project) to create your Laravel application and select SQLite as your database, Laravel will automatically create a `database/database.sqlite` file and run the default [database migrations](/docs/{{version}}/migrations) for you. + #### Microsoft SQL Server Configuration @@ -94,13 +97,20 @@ To see how read / write connections should be configured, let's look at this exa ], ], 'sticky' => true, - 'driver' => 'mysql', - 'database' => 'database', - 'username' => 'root', - 'password' => '', + + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', + 'collation' => 'utf8mb4_0900_ai_ci', 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], ], Note that three keys have been added to the configuration array: `read`, `write` and `sticky`. The `read` and `write` keys have array values containing a single key: `host`. The rest of the database options for the `read` and `write` connections will be merged from the main `mysql` configuration array. From 1659540c3928251f622da4df67bbb994aa02418c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 27 Feb 2024 12:33:04 +0000 Subject: [PATCH 1446/2609] [11.x] Rewrites `passwords` for L11 (#9409) * Rewrites `passwords` * formatting --------- Co-authored-by: Taylor Otwell --- passwords.md | 14 +++++--------- requests.md | 4 ++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/passwords.md b/passwords.md index 26e762606ef..c1e026ed243 100644 --- a/passwords.md +++ b/passwords.md @@ -28,20 +28,16 @@ Next, verify that your `App\Models\User` model implements the `Illuminate\Contra ### 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 application, so you only need to migrate your database to create this table: - -```shell -php artisan migrate -``` +A table must be created to store your application's password reset tokens. Typically, this is included in Laravel's default `0001_01_01_000000_create_users_table.php` database migration. ### Configuring Trusted Hosts By default, Laravel will respond to all requests it receives regardless of the content of the HTTP request's `Host` header. In addition, the `Host` header's value will be used when generating absolute URLs to your application during a web request. -Typically, you should configure your web server, such as Nginx or Apache, to only send requests to your application that match a given host name. However, if you do not have the ability to customize your web server directly and need to instruct Laravel to only respond to certain host names, you may do so by enabling the `App\Http\Middleware\TrustHosts` middleware for your application. This is particularly important when your application offers password reset functionality. +Typically, you should configure your web server, such as Nginx or Apache, to only send requests to your application that match a given hostname. However, if you do not have the ability to customize your web server directly and need to instruct Laravel to only respond to certain hostnames, you may do so by using the `trustHosts` middleware method in your application's `bootstrap/app.php` file. This is particularly important when your application offers password reset functionality. -To learn more about this middleware, please consult the [`TrustHosts` middleware documentation](/docs/{{version}}/requests#configuring-trusted-hosts). +To learn more about this middleware method, please consult the [`TrustHosts` middleware documentation](/docs/{{version}}/requests#configuring-trusted-hosts). ## Routing @@ -172,13 +168,13 @@ If you would like to automate this process, consider adding the command to your #### 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 your `App\Providers\AuthServiceProvider` 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\AppServiceProvider` service provider's `boot` method: use App\Models\User; use Illuminate\Auth\Notifications\ResetPassword; /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { diff --git a/requests.md b/requests.md index 1568c92e451..39f4d9dda3c 100644 --- a/requests.md +++ b/requests.md @@ -636,9 +636,9 @@ If you are using Amazon AWS or another "cloud" load balancer provider, you may n By default, Laravel will respond to all requests it receives regardless of the content of the HTTP request's `Host` header. In addition, the `Host` header's value will be used when generating absolute URLs to your application during a web request. -Typically, you should configure your web server, such as Nginx or Apache, to only send requests to your application that match a given host name. However, if you do not have the ability to customize your web server directly and need to instruct Laravel to only respond to certain host names, you may do so by enabling the `Illuminate\Http\Middleware\TrustHosts` middleware for your application. +Typically, you should configure your web server, such as Nginx or Apache, to only send requests to your application that match a given hostname. However, if you do not have the ability to customize your web server directly and need to instruct Laravel to only respond to certain hostnames, you may do so by enabling the `Illuminate\Http\Middleware\TrustHosts` middleware for your application. -To enable the `TrustHosts` middleware, you should use the `trustHosts` middleware method in your application's `bootstrap/app.php` file. Using the `at` argument of this method, you may specify the host names that your application should respond to. Incoming requests with other `Host` value headers will be rejected: +To enable the `TrustHosts` middleware, you should invoke the `trustHosts` middleware method in your application's `bootstrap/app.php` file. Using the `at` argument of this method, you may specify the hostnames that your application should respond to. Incoming requests with other `Host` headers will be rejected: ->withMiddleware(function (Middleware $middleware) { $middleware->trustHosts(at: ['laravel.test']); From 086a4e75e3f1f322cdfa50877332714b96aeccbe Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 27 Feb 2024 12:37:20 +0000 Subject: [PATCH 1447/2609] [11.x] Rewrites `authorization` for L11 (#9405) * Rewrites `authorization` * formatting --------- Co-authored-by: Taylor Otwell --- authorization.md | 69 ++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/authorization.md b/authorization.md index 32cc990d292..fde4e5d234c 100644 --- a/authorization.md +++ b/authorization.md @@ -41,7 +41,7 @@ You do not need to choose between exclusively using gates or exclusively using p > [!WARNING] > 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. +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\AppServiceProvider` 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 by comparing the user's `id` against the `user_id` of the user that created the post: @@ -50,7 +50,7 @@ In this example, we'll define a gate to determine if a user can update a given ` use Illuminate\Support\Facades\Gate; /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -65,7 +65,7 @@ Like controllers, gates may also be defined using a class callback array: use Illuminate\Support\Facades\Gate; /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -127,7 +127,7 @@ You may authorize multiple actions at a time using the `any` or `none` methods: #### 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` facade's `authorize` method. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler: +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: Gate::authorize('update-post', $post); @@ -281,54 +281,35 @@ php artisan make:policy PostPolicy --model=Post ### Registering Policies -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. + +#### Policy Discovery -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: +By default, Laravel 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. - PostPolicy::class, - ]; - - /** - * Register any application authentication / authorization services. - */ - public function boot(): void - { - // ... - } - } - - -#### Policy Auto-Discovery + Gate::guessPolicyNamesUsing(function (string $modelClass) { + // Return the name of the policy class for the given model... + }); -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. + +#### Manually Registering Policies -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`: +Using the `Gate` facade, you may manually register policies and their corresponding models within the `boot` method of your application's `AppServiceProvider`: + use App\Models\Order; + use App\Policies\OrderPolicy; use Illuminate\Support\Facades\Gate; - Gate::guessPolicyNamesUsing(function (string $modelClass) { - // Return the name of the policy class for the given model... - }); - -> [!WARNING] -> Any policies that are explicitly mapped in your `AuthServiceProvider` will take precedence over any potentially auto-discovered policies. + /** + * Bootstrap any application services. + */ + public function boot(): void + { + Gate::policy(Order::class, OrderPolicy::class); + } ## Writing Policies @@ -625,7 +606,7 @@ As previously discussed, some policy methods like `create` do not require a mode ### 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 application's `App\Http\Kernel` class. Let's explore an example of using the `can` middleware to authorize that a user can update a post: +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 may be attached to a route using the `can` route middleware alias, which is automatically registered by Laravel. Let's explore an example of using the `can` middleware to authorize that a user can update a post: use App\Models\Post; @@ -741,7 +722,7 @@ When attempting to determine if the authenticated user can update a given post, */ public function update(Request $request, Post $post): RedirectResponse { - $this->authorize('update', [$post, $request->category]); + Gate::authorize('update', [$post, $request->category]); // The current user can update the blog post... From 5fd264cd8abb436d2592950856d2108e124a4aad Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Feb 2024 10:21:46 -0600 Subject: [PATCH 1448/2609] wip --- validation.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/validation.md b/validation.md index c4b64ed455f..91c39e3b218 100644 --- a/validation.md +++ b/validation.md @@ -1245,6 +1245,14 @@ The `Enum` rule is a class based rule that validates whether the field under val 'status' => [Rule::enum(ServerStatus::class)], ]); +The `Enum` rule's `only` and `except` methods may be used to limit which enum cases should be considered valid: + + Rule::enum(ServerStatus::class) + ->only([ServerStatus::Pending, ServerStatus::Active]); + + Rule::enum(ServerStatus::class) + ->except([ServerStatus::Pending, ServerStatus::Active]); + #### exclude From 1b9e3ad8f3b48165037da201a5c7f15e3c349a4b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Feb 2024 10:24:50 -0600 Subject: [PATCH 1449/2609] document custom rate limiter increment --- rate-limiting.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rate-limiting.md b/rate-limiting.md index 5d7e1979dcf..95356ed309d 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -66,20 +66,24 @@ If you would like to manually interact with the rate limiter, a variety of other return 'Too many attempts!'; } - RateLimiter::hit('send-message:'.$user->id); + RateLimiter::increment('send-message:'.$user->id); // Send message... -Alternatively, you may use the `remaining` method to retrieve the number of attempts remaining for a given key. If a given key has retries remaining, you may invoke the `hit` method to increment the number of total attempts: +Alternatively, you may use the `remaining` method to retrieve the number of attempts remaining for a given key. If a given key has retries remaining, you may invoke the `increment` method to increment the number of total attempts: use Illuminate\Support\Facades\RateLimiter; if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) { - RateLimiter::hit('send-message:'.$user->id); + RateLimiter::increment('send-message:'.$user->id); // Send message... } +If you would like to increment the value for a given rate limiter key by more than one, you may provide the desired amount to the `increment` method: + + RateLimiter::increment('send-message:'.$user->id, amount: 5); + #### Determining Limiter Availability @@ -93,7 +97,7 @@ When a key has no more attempts left, the `availableIn` method returns the numbe return 'You may try again in '.$seconds.' seconds.'; } - RateLimiter::hit('send-message:'.$user->id); + RateLimiter::increment('send-message:'.$user->id); // Send message... From d1addfebfb3d827260fd16e8c8c5dc3befe616b8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Feb 2024 10:26:51 -0600 Subject: [PATCH 1450/2609] wip --- configuration.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/configuration.md b/configuration.md index b2760786433..7245036249c 100644 --- a/configuration.md +++ b/configuration.md @@ -185,14 +185,20 @@ php artisan env:decrypt --force ## Accessing Configuration Values -You may easily access your configuration values using the global `config` function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: +You may easily access your configuration values using the `Config` facade or global `config` function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: + + use Illuminate\Support\Facades\Config; + + $value = Config::get('app.timezone'); $value = config('app.timezone'); // Retrieve a default value if the configuration value does not exist... $value = config('app.timezone', 'Asia/Seoul'); -To set configuration values at runtime, pass an array to the `config` function: +To set configuration values at runtime, you may invoke the `Config` facade's `set` method or pass an array to the `config` function: + + Config::set('app.timezone', 'America/Chicago'); config(['app.timezone' => 'America/Chicago']); From db600493ef8063ade845515d9c88a830905e9861 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Feb 2024 10:28:38 -0600 Subject: [PATCH 1451/2609] wip --- configuration.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configuration.md b/configuration.md index 7245036249c..1d942531d9f 100644 --- a/configuration.md +++ b/configuration.md @@ -202,6 +202,14 @@ To set configuration values at runtime, you may invoke the `Config` facade's `se config(['app.timezone' => 'America/Chicago']); +To assist with static analysis, the `Config` facade also provides typed configuration retrieval methods. If the retrieved configuration value does not match the expected type, an exception will be thrown: + + Config::string('config-key'); + Config::integer('config-key'); + Config::float('config-key'); + Config::boolean('config-key'); + Config::array('config-key'); + ## Configuration Caching From d55b27a9a653a15abc97a431814e0ec6e486dc99 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Feb 2024 10:34:45 -0600 Subject: [PATCH 1452/2609] wip --- prompts.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/prompts.md b/prompts.md index d17be47da33..058dfa5b1b0 100644 --- a/prompts.md +++ b/prompts.md @@ -11,6 +11,7 @@ - [Suggest](#suggest) - [Search](#search) - [Multi-search](#multisearch) + - [Pause](#pause) - [Informational Messages](#informational-messages) - [Tables](#tables) - [Spin](#spin) @@ -610,8 +611,19 @@ $ids = multisearch( If the `options` closure returns an associative array, then the closure will receive the selected keys; otherwise, it will receive the selected values. The closure may return an error message, or `null` if the validation passes. + +### Pause + +The `pause` function may be used to display informational text to the user and wait for them to confirm their desire to proceed by pressing the Enter / Return key: + +```php +use function Laravel\Prompts\pause; + +pause('Press ENTER to continue.'); +``` + -### Informational Messages +## Informational Messages The `note`, `info`, `warning`, `error`, and `alert` functions may be used to display informational messages: @@ -622,7 +634,7 @@ info('Package installed successfully.'); ``` -### Tables +## Tables The `table` function makes it easy to display multiple rows and columns of data. All you need to do is provide the column names and the data for the table: @@ -636,7 +648,7 @@ table( ``` -### Spin +## Spin The `spin` function displays a spinner along with an optional message while executing a specified callback. It serves to indicate ongoing processes and returns the callback's results upon completion: @@ -705,7 +717,7 @@ $progress->finish(); ``` -### Terminal Considerations +## Terminal Considerations #### Terminal Width @@ -718,7 +730,7 @@ If the length of any label, option, or validation message exceeds the number of For any prompts that accept the `scroll` argument, the configured value will automatically be reduced to fit the height of the user's terminal, including space for a validation message. -### Unsupported Environments and Fallbacks +## Unsupported Environments and Fallbacks Laravel Prompts supports macOS, Linux, and Windows with WSL. Due to limitations in the Windows version of PHP, it is not currently possible to use Laravel Prompts on Windows outside of WSL. From 0c14ee35b5c7cd3b0935e385fc7f062ec30ed9a8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Feb 2024 10:49:15 -0600 Subject: [PATCH 1453/2609] wip --- upgrade.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/upgrade.md b/upgrade.md index ac7937029a0..31e30b7b2fa 100644 --- a/upgrade.md +++ b/upgrade.md @@ -12,6 +12,7 @@ - [Application Structure](#application-structure) - [Floating-Point Types](#floating-point-types) - [Modifying Columns](#modifying-columns) +- [Sanctum Middleware Configuration](#sanctum-middleware-configuration) - [SQLite Minimum Version](#sqlite-minimum-version)
    @@ -65,6 +66,7 @@ You should update the following dependencies in your application's `composer.jso
    - `laravel/framework` to `^11.0` +- `laravel/sanctum` to `^4.0`
    @@ -383,3 +385,20 @@ The `Illuminate\Queue\Middleware\ThrottlesExceptions` and `Illuminate\Queue\Midd new ThrottlesExceptions($attempts, 2 * 60); new ThrottlesExceptionsWithRedis($attempts, 2 * 60); ``` + + +### Sanctum + + +#### Sanctum Middleware Configuration + +**Likelihood Of Impact: High** + +In your application's `config/sanctum.php` configuration file, you should update the references to the `authenticate_session`, `encrypt_cookies`, and `validate_csrf_token` middleware to the following: + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + From 7bd44ce25f444fcc7ce26e07f05a0048e5002237 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Feb 2024 11:00:40 -0600 Subject: [PATCH 1454/2609] document provider publish --- upgrade.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/upgrade.md b/upgrade.md index 31e30b7b2fa..162d60d9ccb 100644 --- a/upgrade.md +++ b/upgrade.md @@ -341,6 +341,24 @@ The `Illuminate\Contracts\Mail\Mailer` contract has received a new `sendNow` met public function sendNow($mailable, array $data = [], $callback = null); ``` + +### Packages + + +#### Publishing Service Providers to the Application + +**Likelihood Of Impact: Very Low** + +If you have written a Laravel package that manually publishes a service provider to the application's `app/Providers` directory and manually modifies the application's `config/app.php` configuration file to register the service provider, you should update your package to utilize the new `ServiceProvider::addProviderToBootstrapFile` method. + +The `addProviderToBootstrapFile` method will automatically add the service provider you have published to the application's `bootstrap/providers.php` file, since the `providers` array does not exist within the `config/app.php` configuration file in new Laravel 11 applications. + +```php +use Illuminate\Support\ServiceProvider; + +ServiceProvider::addProviderToBootstrapFile(Provider::class); +``` + ### Queues From 69ff2b4f453fb01fc422513a9d6bc60a73fc816b Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 02:01:16 +0000 Subject: [PATCH 1455/2609] Small changes (#9420) --- testing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing.md b/testing.md index 287eca971a6..15a918a735d 100644 --- a/testing.md +++ b/testing.md @@ -94,13 +94,13 @@ As mentioned previously, once you've written tests, you may run them using `pest ./vendor/bin/phpunit ``` -In addition to the `phpunit` command, you may use the `test` Artisan command to run your tests. The Artisan test runner provides verbose test reports in order to ease development and debugging: +In addition to the `pest` or `phpunit` commands, you may use the `test` Artisan command to run your tests. The Artisan test runner provides verbose test reports in order to ease development and debugging: ```shell php artisan test ``` -Any arguments that can be passed to the `phpunit` command may also be passed to the Artisan `test` command: +Any arguments that can be passed to the `pest` or `phpunit` commands may also be passed to the Artisan `test` command: ```shell php artisan test --testsuite=Feature --stop-on-failure From 5e6323db5d86890dbea7b1d46fbf1cc5fba10445 Mon Sep 17 00:00:00 2001 From: laser-hybiz <100562257+laser-hybiz@users.noreply.github.com> Date: Tue, 27 Feb 2024 21:04:26 -0500 Subject: [PATCH 1456/2609] Update authentication.md (#9415) --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index 3b668bafa81..546836b3212 100644 --- a/authentication.md +++ b/authentication.md @@ -734,4 +734,4 @@ Event Name | `Illuminate\Auth\Events\CurrentDeviceLogout` | `Illuminate\Auth\Events\OtherDeviceLogout` | `Illuminate\Auth\Events\Lockout` | -`Illuminate\Auth\Events\PasswordRese` | +`Illuminate\Auth\Events\PasswordReset` | From 2050e03e0767545636a2581f610c882fc860e949 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 15:58:04 +0000 Subject: [PATCH 1457/2609] [11.x] Rewrites `cashier-paddle` for L11 (#9424) * Rewrites `cashier-paddle` * Update cashier-paddle.md --------- Co-authored-by: Taylor Otwell --- cashier-paddle.md | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index bfc72536dd8..4298e39f0f2 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -257,7 +257,7 @@ As you can see in the example above, when a user begins the checkout process, we Of course, you will likely want to mark the order as "complete" once the customer has finished the checkout process. To accomplish this, you may listen to the webhooks dispatched by Paddle and raised via events by Cashier to store order information in your database. -To get started, listen for the `TransactionCompleted` event dispatched by Cashier. Typically, you should register the event listener in the `boot` method of one of your application's service providers: +To get started, listen for the `TransactionCompleted` event dispatched by Cashier. Typically, you should register the event listener in the `boot` method of your application's `AppServiceProvider`: use App\Listeners\CompleteOrder; use Illuminate\Support\Facades\Event; @@ -1199,11 +1199,13 @@ To ensure your application can handle Paddle webhooks, be sure to [configure the #### Webhooks and 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 `App\Http\Middleware\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), you should ensure that Laravel does not attempt to verify the CSRF token for incoming Paddle webhooks. To accomplish this, you should exclude `paddle/*` from CSRF protection in your application's `bootstrap/app.php` file: - protected $except = [ - 'paddle/*', - ]; + ->withMiddleware(function (Middleware $middleware) { + $middleware->validateCsrfTokens(except: [ + 'paddle/*', + ]); + }) #### Webhooks and Local Development @@ -1239,25 +1241,6 @@ Both events contain the full payload of the Paddle webhook. For example, if you } } -Once your listener has been defined, you may register it within your application's `EventServiceProvider`: - - [ - PaddleEventListener::class, - ], - ]; - } - Cashier also emit events dedicated to the type of the received webhook. In addition to the full payload from Paddle, they also contain the relevant models that were used to process the webhook such as the billable model, the subscription, or the receipt:
    From 4172ec7576541246996b954b24cb0824838538e0 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:03:38 +0000 Subject: [PATCH 1458/2609] [11.x] Rewrites `billing` (#9422) * Rewrites `billing` * Update billing.md --------- Co-authored-by: Taylor Otwell --- billing.md | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/billing.md b/billing.md index 597a16391c0..efb3f6c8fc7 100644 --- a/billing.md +++ b/billing.md @@ -1688,11 +1688,13 @@ php artisan cashier:webhook --disabled #### Webhooks and 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 application's `App\Http\Middleware\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), you should ensure that Laravel does not attempt to validate the CSRF token for incoming Stripe webhooks. To accomplish this, you should exclude `stripe/*` from CSRF protection in your application's `bootstrap/app.php` file: - protected $except = [ - 'stripe/*', - ]; + ->withMiddleware(function (Middleware $middleware) { + $middleware->validateCsrfTokens(except: [ + 'stripe/*', + ]); + }) ### Defining Webhook Event Handlers @@ -1723,25 +1725,6 @@ Both events contain the full payload of the Stripe webhook. For example, if you } } -Once your listener has been defined, you may register it within your application's `EventServiceProvider`: - - [ - StripeEventListener::class, - ], - ]; - } - ### Verifying Webhook Signatures From bf02092cfb84070be58f97ffa9a5de61fac531b9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:04:56 +0000 Subject: [PATCH 1459/2609] [11.x] Rewrites `factories` by Laravel 11 (#9419) * Rewrites `factories` * Update eloquent-factories.md --------- Co-authored-by: Taylor Otwell --- eloquent-factories.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/eloquent-factories.md b/eloquent-factories.md index 4b5885cdb87..2c852a7f7ba 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -26,11 +26,20 @@ To see an example of how to write a factory, take a look at the `database/factor namespace Database\Factories; - use Illuminate\Support\Str; use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Support\Facades\Hash; + use Illuminate\Support\Str; + /** + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User> + */ class UserFactory extends Factory { + /** + * The current password being used by the factory. + */ + protected static ?string $password; + /** * Define the model's default state. * @@ -42,10 +51,20 @@ To see an example of how to write a factory, take a look at the `database/factor 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), - 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'password' => static::$password ??= Hash::make('password'), 'remember_token' => Str::random(10), ]; } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } } As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. @@ -53,7 +72,7 @@ As you can see, in their most basic form, factories are classes that extend Lara Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing and seeding. > [!NOTE] -> You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. +> You can change your application's Faker locale by updating the `faker_locale` option in your `config/app.php` configuration file. ## Defining Model Factories From d90d11d8f1c4e69fd09ec41d223fbc0b78d35a5d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:06:50 +0000 Subject: [PATCH 1460/2609] [11.x] Rewrites `eloquent` (#9418) * Rewrites `eloquent` * formatting --------- Co-authored-by: Taylor Otwell --- eloquent.md | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/eloquent.md b/eloquent.md index e9d2e372167..4da807f7b88 100644 --- a/eloquent.md +++ b/eloquent.md @@ -873,7 +873,7 @@ If you would like to make all of your attributes mass assignable, you may define By default, attributes that are not included in the `$fillable` array are silently discarded when performing mass-assignment operations. In production, this is expected behavior; however, during local development it can lead to confusion as to why model changes are not taking effect. -If you wish, you may instruct Laravel to throw an exception when attempting to fill an unfillable attribute by invoking the `preventSilentlyDiscardingAttributes` method. Typically, this method should be invoked within the `boot` method of one of your application's service providers: +If you wish, you may instruct Laravel to throw an exception when attempting to fill an unfillable attribute by invoking the `preventSilentlyDiscardingAttributes` method. Typically, this method should be invoked in the `boot` method of your application's `AppServiceProvider` class: use Illuminate\Database\Eloquent\Model; @@ -1071,25 +1071,21 @@ When marking models as `Prunable`, you may also define a `pruning` method on the // ... } -After configuring your prunable model, you should schedule the `model:prune` Artisan command in your application's `App\Console\Kernel` class. You are free to choose the appropriate interval at which this command should be run: +After configuring your prunable model, you should schedule the `model:prune` Artisan command in your application's `routes/console.php` file. You are free to choose the appropriate interval at which this command should be run: - /** - * Define the application's command schedule. - */ - protected function schedule(Schedule $schedule): void - { - $schedule->command('model:prune')->daily(); - } + use Illuminate\Support\Facades\Schedule; + + Schedule::command('model:prune')->daily(); Behind the scenes, the `model:prune` command will automatically detect "Prunable" models within your application's `app/Models` directory. If your models are in a different location, you may use the `--model` option to specify the model class names: - $schedule->command('model:prune', [ + Schedule::command('model:prune', [ '--model' => [Address::class, Flight::class], ])->daily(); If you wish to exclude certain models from being pruned while pruning all other detected models, you may use the `--except` option: - $schedule->command('model:prune', [ + Schedule::command('model:prune', [ '--except' => [Address::class, Flight::class], ])->daily(); @@ -1423,7 +1419,7 @@ To start listening to model events, define a `$dispatchesEvents` property on you /** * The event map for the model. * - * @var array + * @var array */ protected $dispatchesEvents = [ 'saved' => UserSaved::class, @@ -1542,13 +1538,13 @@ To register an observer, you may place the `ObservedBy` attribute on the corresp // } -Or, you may manually register an observer by calling the `observe` method on the model you wish to observe. You may register observers in the `boot` method of your application's `App\Providers\EventServiceProvider` service provider: +Or, you may manually register an observer by invoking the `observe` method on the model you wish to observe. You may register observers in the `boot` method of your application's `AppServiceProvider` class: use App\Models\User; use App\Observers\UserObserver; /** - * Register any events for your application. + * Bootstrap any application services. */ public function boot(): void { From 478c8a3d66ffa31118831bfd3365db9fa0e2baa4 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:11:42 +0000 Subject: [PATCH 1461/2609] [11.x] Rewrites `redis` for L11 (#9417) * Rewrites `redis` * wip * formatting --------- Co-authored-by: Taylor Otwell --- redis.md | 104 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/redis.md b/redis.md index 4807ce752f5..0cb2a26deac 100644 --- a/redis.md +++ b/redis.md @@ -20,7 +20,7 @@ Before using Redis with Laravel, we encourage you to install and use the [PhpRed 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: ```shell -composer require predis/predis +composer require predis/predis:^2.0 ``` @@ -32,18 +32,27 @@ You may configure your application's Redis settings via the `config/database.php 'client' => env('REDIS_CLIENT', 'phpredis'), + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + 'default' => [ + 'url' => env('REDIS_URL'), 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', 6379), - 'database' => env('REDIS_DB', 0), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), ], 'cache' => [ + 'url' => env('REDIS_URL'), 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', 6379), - 'database' => env('REDIS_CACHE_DB', 1), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), ], ], @@ -54,6 +63,11 @@ Each Redis server defined in your configuration file is required to have a name, 'client' => env('REDIS_CLIENT', 'phpredis'), + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + 'default' => [ 'url' => 'tcp://127.0.0.1:6379?database=0', ], @@ -69,18 +83,14 @@ Each Redis server defined in your configuration file is required to have a name, 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' => [ - - 'client' => env('REDIS_CLIENT', 'phpredis'), - - 'default' => [ - 'scheme' => 'tls', - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', 6379), - 'database' => env('REDIS_DB', 0), - ], - + 'default' => [ + 'scheme' => 'tls', + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), ], @@ -92,35 +102,42 @@ If your application is utilizing a cluster of Redis servers, you should define t 'client' => env('REDIS_CLIENT', 'phpredis'), + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + 'clusters' => [ 'default' => [ [ - 'host' => env('REDIS_HOST', 'localhost'), + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', 6379), - 'database' => 0, + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), ], ], ], + // ... ], -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. +By default, Laravel will use native Redis clustering since the `options.cluster` configuration value is set to `redis`. Redis clustering is a great default option, as it gracefully handles failover. + +Laravel also supports client-side sharding. 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: +If you would like to use client-side sharding instead of native Redis clustering, you may remove the `options.cluster` configuration value within your application's `config/database.php` configuration file: 'redis' => [ 'client' => env('REDIS_CLIENT', 'phpredis'), - 'options' => [ - 'cluster' => env('REDIS_CLUSTER', 'redis'), - ], - 'clusters' => [ // ... ], + // ... ], @@ -135,25 +152,18 @@ If you would like your application to interact with Redis via the Predis package // ... ], -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: +In addition to the default 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'), + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', 6379), - 'database' => 0, + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), '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. By default, no `Redis` alias is included because it would conflict with the `Redis` class name provided by the PhpRedis extension. If you are using the Predis client and would like to add a `Redis` alias, you may add it to the `aliases` array in your application's `config/app.php` configuration file: - - 'aliases' => Facade::defaultAliases()->merge([ - 'Redis' => Illuminate\Support\Facades\Redis::class, - ])->toArray(), - ### PhpRedis @@ -163,16 +173,18 @@ By default, Laravel will use the PhpRedis extension to communicate with Redis. T 'client' => env('REDIS_CLIENT', 'phpredis'), - // Rest of Redis configuration... + // ... ], -In addition to the default `scheme`, `host`, `port`, `database`, and `password` server configuration options, PhpRedis supports the following additional connection parameters: `name`, `persistent`, `persistent_id`, `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 configuration options, PhpRedis supports the following additional connection parameters: `name`, `persistent`, `persistent_id`, `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'), + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', 6379), - 'database' => 0, + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), 'read_timeout' => 60, 'context' => [ // 'auth' => ['username', 'secret'], @@ -190,11 +202,13 @@ The PhpRedis extension may also be configured to use a variety of serializers an 'client' => env('REDIS_CLIENT', 'phpredis'), 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), 'serializer' => Redis::SERIALIZER_MSGPACK, 'compression' => Redis::COMPRESSION_LZ4, ], - // Rest of Redis configuration... + // ... ], Currently supported serializers include: `Redis::SERIALIZER_NONE` (default), `Redis::SERIALIZER_PHP`, `Redis::SERIALIZER_JSON`, `Redis::SERIALIZER_IGBINARY`, and `Redis::SERIALIZER_MSGPACK`. From aff7427418283084fa8c765208a7b1a952c07161 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:13:01 +0000 Subject: [PATCH 1462/2609] [11.x] Rewrites `migrations` for L11 (#9414) * Rewrites `migrations` * Update migrations.md --------- Co-authored-by: Taylor Otwell --- migrations.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/migrations.md b/migrations.md index 87f2a7647e3..86de2036511 100644 --- a/migrations.md +++ b/migrations.md @@ -1079,23 +1079,6 @@ Command | Description `$table->fullText('body')->language('english');` | Adds a full text index of the specified language (PostgreSQL). `$table->spatialIndex('location');` | Adds a spatial index (except SQLite). - -#### Index Lengths and MySQL / MariaDB - -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; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Schema::defaultStringLength(191); - } - -Alternatively, you may enable the `innodb_large_prefix` option for your database. Refer to your database's documentation for instructions on how to properly enable this option. - ### Renaming Indexes From 41e230181aa8ab1c36a2e2da187fec44731f53f3 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:14:21 +0000 Subject: [PATCH 1463/2609] [11.x] Rewrites `fortify` for L11 (#9425) * Rewrites `fortify` * Update fortify.md --------- Co-authored-by: Taylor Otwell --- fortify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortify.md b/fortify.md index 6b61a4a180c..49feb41c72f 100644 --- a/fortify.md +++ b/fortify.md @@ -92,7 +92,7 @@ php artisan migrate ### The Fortify Service Provider -The `vendor:publish` command discussed above will also publish the `App\Providers\FortifyServiceProvider` class. You should ensure this class is registered within the `providers` array of your application's `config/app.php` configuration file. +The `vendor:publish` command discussed above will also publish the `App\Providers\FortifyServiceProvider` class. You should ensure this class is registered within the array of service providers in your application's `bootstrap/providers.php` file. The Fortify service provider registers the actions that Fortify published and instructs Fortify to use them when their respective tasks are executed by Fortify. From 5c63b6bf35200ddae5aa42c741f0a05778e89f01 Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Wed, 28 Feb 2024 17:22:39 +0100 Subject: [PATCH 1464/2609] [11.x] Document upgrade to MariaDB driver (#9423) * Document upgrade to MariaDB driver * formatting * update comment --------- Co-authored-by: Taylor Otwell --- upgrade.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/upgrade.md b/upgrade.md index 162d60d9ccb..0fcaebf6fec 100644 --- a/upgrade.md +++ b/upgrade.md @@ -235,6 +235,33 @@ $table->double('amount')->unsigned(); $table->float('amount', precision: 53)->unsigned(); ``` + +#### Dedicated MariaDB Driver + +**Likelihood Of Impact: Very Low** + +Instead of always utilizing the MySQL driver when connecting to MariaDB databases, Laravel 11 adds a dedicated database driver for MariaDB. + +If your application connects to a MariaDB database, you may update the connection configuration to the new `mariadb` driver to benefit from MariaDB specific features in the future: + + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + // ... + +Currently, the new MariaDB driver behaves like the current MySQL driver with one exception: the `uuid` schema builder method creates native UUID columns instead of `char(36)` columns. + +If your existing migrations utilize the `uuid` schema builder method and you choose to use the new `mariadb` database driver, you should update your migration's invocations of the `uuid` method to `char` to avoid breaking changes or unexpected behavior: + +```php +Schema::table('users', function (Blueprint $table) { + $table->char('uuid', 36); + + // ... +}); +``` + #### Spatial Types From d58cfaa372537ae29599ed61117d5e913406e957 Mon Sep 17 00:00:00 2001 From: Magnus Hauge Bakke Date: Wed, 28 Feb 2024 17:27:08 +0100 Subject: [PATCH 1465/2609] [10.x] Adds documentation for `joinLateral` and `leftJoinLateral` (#9421) * [10.x] Adds documentation for `joinLateral` and `leftJoinLateral` * formatting --------- Co-authored-by: Taylor Otwell --- queries.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/queries.md b/queries.md index ce92e35c60a..bab2ed2269d 100644 --- a/queries.md +++ b/queries.md @@ -380,6 +380,26 @@ You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a q $join->on('users.id', '=', 'latest_posts.user_id'); })->get(); + +#### Lateral Joins + +> [!WARNING] +> Lateral joins are currently supported by PostgreSQL, MySQL >= 8.0.14, and SQL Server. + +You may use the `joinLateral` and `leftJoinLateral` methods to perform a "lateral join" with a subquery. Each of these methods receives two arguments: the subquery and its table alias. The join condition(s) should be specified within the `where` clause of the given subquery. Lateral joins are evaluated for each row and can reference columns outside the subquery. + +In this example, we will retrieve a collection of users as well as the user's three most recent blog posts. Each user can produce up to three rows in the result set: one for each of their most recent blog posts. The join condition is specified with a `whereColumn` clause within the subquery, referencing the current user row: + + $latestPosts = DB::table('posts') + ->select('id as post_id', 'title as post_title', 'created_at as post_created_at') + ->whereColumn('user_id', 'users.id') + ->orderBy('created_at', 'desc') + ->limit(3); + + $users = DB::table('users') + ->joinLateral($latestPosts, 'latest_posts') + ->get(); + ## Unions From 8f995824d69bfeded20ea9c50288935f9eee8b0b Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:03:00 +0330 Subject: [PATCH 1466/2609] [10.x] Add condition Enum rule in Validation (#9412) * Update validation.md * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/validation.md b/validation.md index 91c39e3b218..bad1763ed02 100644 --- a/validation.md +++ b/validation.md @@ -1253,6 +1253,20 @@ The `Enum` rule's `only` and `except` methods may be used to limit which enum ca Rule::enum(ServerStatus::class) ->except([ServerStatus::Pending, ServerStatus::Active]); +The `when` method may be used to conditionally modify the `Enum` rule: + +```php +use Illuminate\Support\Facades\Auth; +use Illuminate\Validation\Rule; + +Rule::enum(ServerStatus::class) + ->when( + Auth::user()->isAdmin(), + fn ($rule) => $rule->only(...), + fn ($rule) => $rule->only(...), + ); +``` + #### exclude From cced9154b6015f3c6c61e6c584828341b548622d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:33:18 +0000 Subject: [PATCH 1467/2609] Rewrites `homestead` (#9426) --- homestead.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homestead.md b/homestead.md index b61bfb3d7ef..843f89003b8 100644 --- a/homestead.md +++ b/homestead.md @@ -623,7 +623,7 @@ Once configured, Homestead will export your databases to `.backup/mysql_backup` ### Configuring Cron Schedules -Laravel provides a convenient way to [schedule cron jobs](/docs/{{version}}/scheduling) by scheduling a single `schedule:run` Artisan command to run every minute. The `schedule:run` command will examine the job schedule defined in your `App\Console\Kernel` class to determine which scheduled tasks to run. +Laravel provides a convenient way to [schedule cron jobs](/docs/{{version}}/scheduling) by scheduling a single `schedule:run` Artisan command to run every minute. The `schedule:run` command will examine the job schedule defined in your `routes/console.php` file to determine which scheduled tasks to run. If you would like the `schedule:run` command to be run for a Homestead site, you may set the `schedule` option to `true` when defining the site: From af75fcd6084f2a36d071ab6f6176a4a5eb4b30f9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:44:45 +0000 Subject: [PATCH 1468/2609] Rewrites `homestead` (#9427) --- homestead.md | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/homestead.md b/homestead.md index 843f89003b8..05d9448955e 100644 --- a/homestead.md +++ b/homestead.md @@ -661,25 +661,14 @@ Once Mailpit has been configured, you may access the Mailpit dashboard at `http: By default, Minio is available on port 9600. You may access the Minio control panel by visiting `http://localhost:9600`. The default access key is `homestead`, while the default secret key is `secretkey`. When accessing Minio, you should always use region `us-east-1`. -In order to use Minio, you will need to adjust the S3 disk configuration in your application's `config/filesystems.php` configuration file. You will need to add the `use_path_style_endpoint` option to the disk configuration as well as change the `url` key to `endpoint`: - - 's3' => [ - 'driver' => 's3', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION'), - 'bucket' => env('AWS_BUCKET'), - 'endpoint' => env('AWS_URL'), - 'use_path_style_endpoint' => true, - ] - -Finally, ensure your `.env` file has the following options: +In order to use Minio, ensure your `.env` file has the following options: ```ini +AWS_USE_PATH_STYLE_ENDPOINT=true +AWS_ENDPOINT=http://localhost:9600 AWS_ACCESS_KEY_ID=homestead AWS_SECRET_ACCESS_KEY=secretkey AWS_DEFAULT_REGION=us-east-1 -AWS_URL=http://localhost:9600 ``` To provision Minio powered "S3" buckets, add a `buckets` directive to your `Homestead.yaml` file. After defining your buckets, you should execute the `vagrant reload --provision` command in your terminal: From d5d1f85fff1a3e500eef3cad5abae497d260ff21 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 16:58:06 +0000 Subject: [PATCH 1469/2609] [11.x] Rewrites `horizon` for L11 (#9428) * Rewrites `horizon` * Update horizon.md --------- Co-authored-by: Taylor Otwell --- horizon.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/horizon.md b/horizon.md index 0f6d7669a84..0d55a87780c 100644 --- a/horizon.md +++ b/horizon.md @@ -417,15 +417,11 @@ You may configure how many seconds are considered a "long wait" within your appl ## Metrics -Horizon includes a metrics dashboard which provides information regarding your job and queue wait times and throughput. In order to populate this dashboard, you should configure Horizon's `snapshot` Artisan command to run every five minutes via your application's [scheduler](/docs/{{version}}/scheduling): +Horizon includes a metrics dashboard which provides information regarding your job and queue wait times and throughput. In order to populate this dashboard, you should configure Horizon's `snapshot` Artisan command to run every five minutes in your application's `routes/console.php` file: - /** - * Define the application's command schedule. - */ - protected function schedule(Schedule $schedule): void - { - $schedule->command('horizon:snapshot')->everyFiveMinutes(); - } + use Illuminate\Support\Facades\Schedule; + + Schedule::command('horizon:snapshot')->everyFiveMinutes(); ## Deleting Failed Jobs From c39a5c3a0f3222270c489adaae540cab7a3998dd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:13:40 -0800 Subject: [PATCH 1470/2609] wip --- upgrade.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/upgrade.md b/upgrade.md index 0fcaebf6fec..2a6c9c14c9c 100644 --- a/upgrade.md +++ b/upgrade.md @@ -12,8 +12,8 @@ - [Application Structure](#application-structure) - [Floating-Point Types](#floating-point-types) - [Modifying Columns](#modifying-columns) -- [Sanctum Middleware Configuration](#sanctum-middleware-configuration) - [SQLite Minimum Version](#sqlite-minimum-version) +- [Updating Sanctum](#updating-sanctum)
    @@ -434,12 +434,20 @@ new ThrottlesExceptionsWithRedis($attempts, 2 * 60); ### Sanctum - -#### Sanctum Middleware Configuration + +#### Updating Sanctum **Likelihood Of Impact: High** -In your application's `config/sanctum.php` configuration file, you should update the references to the `authenticate_session`, `encrypt_cookies`, and `validate_csrf_token` middleware to the following: +Laravel 11 no longer supports Laravel Sanctum 3.x. Therefore, you should update your application's Laravel Sanctum dependency to `^4.0` in your `composer.json` file. + +Sanctum 4.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Sanctum's migrations to your application: + +```shell +php artisan vendor:publish --tag=sanctum-migrations +``` + +Then, in your application's `config/sanctum.php` configuration file, you should update the references to the `authenticate_session`, `encrypt_cookies`, and `validate_csrf_token` middleware to the following: 'middleware' => [ 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, From 69d3aa0bd49f383d35063b85f76adbb6cdd20b7a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:13:45 -0800 Subject: [PATCH 1471/2609] wip --- upgrade.md | 1 - 1 file changed, 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 2a6c9c14c9c..320600a2d2a 100644 --- a/upgrade.md +++ b/upgrade.md @@ -454,4 +454,3 @@ Then, in your application's `config/sanctum.php` configuration file, you should 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, ], - From 3a0a2900997d3d69a86f5e00fa87b2a662dc89a6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:18:00 -0800 Subject: [PATCH 1472/2609] wip --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index c3072f003b2..bdf3ad6e40c 100644 --- a/cache.md +++ b/cache.md @@ -412,7 +412,7 @@ To register the custom cache driver with Laravel, we will use the `extend` metho 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 the `CACHE_DRIVER` environment variable or `driver` option within your application's `config/cache.php` configuration file to the name of your extension. +Once your extension is registered, update the `CACHE_STORE` environment variable or `driver` option within your application's `config/cache.php` configuration file to the name of your extension. ## Events From 375ce3778d385489527773f23665ee5b8169a4f3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:18:19 -0800 Subject: [PATCH 1473/2609] wip --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index bdf3ad6e40c..f5e1940e335 100644 --- a/cache.md +++ b/cache.md @@ -412,7 +412,7 @@ To register the custom cache driver with Laravel, we will use the `extend` metho 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 the `CACHE_STORE` environment variable or `driver` option within your application's `config/cache.php` configuration file to the name of your extension. +Once your extension is registered, update the `CACHE_STORE` environment variable or `default` option within your application's `config/cache.php` configuration file to the name of your extension. ## Events From 892c51d9284851226f0c8dd0ff4a859b9da21f46 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:29:58 -0800 Subject: [PATCH 1474/2609] wip --- helpers.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/helpers.md b/helpers.md index 626ff124c1a..8009bb168f0 100644 --- a/helpers.md +++ b/helpers.md @@ -1034,6 +1034,19 @@ The function also accepts wildcards using asterisks, which may target any key of // ['Desk 1', 'Desk 2']; +The `{first}` and `{last}` placeholders may be used to retrieve the first or last items in an array: + + $flight = [ + 'segments' => [ + ['from' => 'LHR', 'departure' => '9:00', 'to' => 'IST', 'arrival' => '15:00'], + ['from' => 'IST', 'departure' => '16:00', 'to' => 'PKX', 'arrival' => '20:00'], + ], + ]; + + data_get($flight, 'segments.{first}.arrival'); + + // 15:00 + #### `data_set()` {.collection-method} From 0f110fc8d05f2739cc4ce7b7c83080ddccdc7e37 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:33:36 -0800 Subject: [PATCH 1475/2609] wip --- upgrade.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/upgrade.md b/upgrade.md index 320600a2d2a..21ecf6530c1 100644 --- a/upgrade.md +++ b/upgrade.md @@ -23,6 +23,7 @@
    - [Carbon 3](#carbon-3) +- [Password Rehashing](#password-rehashing) - [Per-Second Rate Limiting](#per-second-rate-limiting)
    @@ -82,6 +83,15 @@ However, we do **not recommend** that Laravel 10 applications upgrading to Larav ### Authentication + +#### Password Rehashing + +Laravel 11 will automatically rehash your user's passwords during authentication if your hashing algorithm's "work factor" has been updated since the password was last hashed. + +Typically, this should not disrupt your application; however, you may disable this behavior by adding the `rehash_on_login` option to your application's `config/hashing.php` configuration file: + + 'rehash_on_login' => false, + #### The `UserProvider` Contract From 277beea3eaa814934f1fcf2c426a2f47718feb07 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:39:49 -0800 Subject: [PATCH 1476/2609] wip --- queues.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/queues.md b/queues.md index 7f27405dfe9..309004a95d0 100644 --- a/queues.md +++ b/queues.md @@ -51,6 +51,7 @@ - [Faking a Subset of Jobs](#faking-a-subset-of-jobs) - [Testing Job Chains](#testing-job-chains) - [Testing Job Batches](#testing-job-batches) + - [Testing Job / Queue Interactions](#testing-job-queue-interactions) - [Job Events](#job-events) @@ -2319,6 +2320,25 @@ In addition, you may occasionally need to test an individual job's interaction w $this->assertTrue($batch->cancelled()); $this->assertEmpty($batch->added); + +### Testing Job / Queue Interactions + +Sometimes, you may need to test that a queued job [releases itself back onto the queue](#manually-releasing-a-job). Or, you may need to test that the job deleted itself. You may test these queue interactions by instantiating the job and invoking the `withFakeQueueInteractions` method. + +Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assetReleased`, `assertDeleted`, and `assertFailed` methods may be used to make assertions against the job's queue interactions: + +```php +use App\Jobs\ProcessPodcast; + +$job = (new ProcessPodcast)->withFakeQueueInteractions(); + +$job->handle(); + +$job->assertReleased(delay: 30); +$job->assertDeleted(); +$job->assertFailed(); +``` + ## Job Events From a763585c5f0755dadc545125affe9c49c066897b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 09:48:40 -0800 Subject: [PATCH 1477/2609] wip --- encryption.md | 17 +++++++++++++++++ releases.md | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/encryption.md b/encryption.md index ee4ca30b544..02a07927207 100644 --- a/encryption.md +++ b/encryption.md @@ -2,6 +2,7 @@ - [Introduction](#introduction) - [Configuration](#configuration) + - [Gracefully Rotating Encryption Keys](#gracefully-rotating-encryption-keys) - [Using the Encrypter](#using-the-encrypter) @@ -14,6 +15,22 @@ Laravel's encryption services provide a simple, convenient interface for encrypt Before using Laravel's encrypter, you must set the `key` configuration option in your `config/app.php` configuration file. This configuration value is driven by the `APP_KEY` environment variable. You should use the `php artisan key:generate` command to generate this variable's value since the `key:generate` command will use PHP's secure random bytes generator to build a cryptographically secure key for your application. Typically, the value of the `APP_KEY` environment variable will be generated for you during [Laravel's installation](/docs/{{version}}/installation). + +### Gracefully Rotating Encryption Keys + +If you change your application's encryption key, all authenticated user sessions will be logged out of your application. This is because every cookie, including session cookies, are encrypted by Laravel. In addition, it will no longer be possible to decrypt any data that was encrypted with your previous encryption key. + +To mitigate this issue, Laravel allows you to list your previous encryption keys in your application's `APP_PREVIOUS_KEYS` environment variable. This variable may contain a comma-delimited list of all of your previous encryption keys: + +```ini +APP_KEY="base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY=" +APP_PREVIOUS_KEYS="base64:2nLsGFGzyoae2ax3EF2Lyq/hH6QghBGLIq5uL+Gp8/w=" +``` + +When you set this environment variable, Laravel will always use the "current" encryption key when encrypting values. However, when decrypting values, Laravel will first try the current key, and if decryption fails using the current key, Laravel will try all previous keys until one of the keys is able to decrypt the value. + +This approach to graceful decryption allows users to keep using your application uninterrupted even if your encryption key is rotated. + ## Using the Encrypter diff --git a/releases.md b/releases.md index b84c393de32..4a39aa37a4d 100644 --- a/releases.md +++ b/releases.md @@ -226,7 +226,7 @@ Laravel 11 allows you to define your application's previous encryption keys as a When encrypting values, Laravel will always use the "current" encryption key, which is within the `APP_KEY` environment variable. When decrypting values, Laravel will first try the current key. If decryption fails using the current key, Laravel will try all previous keys until one of the keys is able to decrypt the value. -This approach to graceful decryption allows users to keep using your application uninterrupted even if your encryption key rotated. +This approach to graceful decryption allows users to keep using your application uninterrupted even if your encryption key is rotated. For more information on encryption in Laravel, check out the [encryption documentation](/docs/{{version}}/encryption). From d80160248044822bb6168e3034edcc7c781a8fad Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 10:08:54 -0800 Subject: [PATCH 1478/2609] wip --- upgrade.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/upgrade.md b/upgrade.md index 21ecf6530c1..ff9f52fc395 100644 --- a/upgrade.md +++ b/upgrade.md @@ -68,9 +68,16 @@ You should update the following dependencies in your application's `composer.jso - `laravel/framework` to `^11.0` - `laravel/sanctum` to `^4.0` +- `laravel/telescope` to `^5.0` (If installed)
    +If your application is using Laravel Telescope, you should run the following command to publish Telescope's migrations to your application. Telescope no longer automatically loads migrations from its own migrations directory: + +```bash +php artisan vendor:publish --tag=telescope-migrations +``` + In addition, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. From 599ecd5e6032ba7bc360daf561e458db100af2ca Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 21:20:07 +0000 Subject: [PATCH 1479/2609] Adds missing Pest snippet (#9430) --- pennant.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pennant.md b/pennant.md index 208544e848a..cad9ee69b49 100644 --- a/pennant.md +++ b/pennant.md @@ -828,7 +828,7 @@ use Laravel\Pennant\Feature; test('it can control feature values', function () { Feature::define('purchase-button', 'seafoam-green'); - $this->assertSame('seafoam-green', Feature::value('purchase-button')); + expect(Feature::value('purchase-button'))->toBe('seafoam-green'); }); ``` @@ -845,7 +845,17 @@ public function test_it_can_control_feature_values() The same approach may be used for class based features: -```php +```php tab=Pest +use Laravel\Pennant\Feature; + +test('it can control feature values', function () { + Feature::define(NewApi::class, true); + + expect(Feature::value(NewApi::class))->toBeTrue(); +}); +``` + +```php tab=PHPUnit use App\Features\NewApi; use Laravel\Pennant\Feature; From 83a06597e819dcd360cfd3c6d968400978e5af15 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 28 Feb 2024 21:21:34 +0000 Subject: [PATCH 1480/2609] [11.x] Adds missing pest snippet (#9431) * Adjusts pest script * Update precognition.md --------- Co-authored-by: Taylor Otwell --- precognition.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/precognition.md b/precognition.md index 35a340e09ff..3877cd0993e 100644 --- a/precognition.md +++ b/precognition.md @@ -683,7 +683,20 @@ If you would like to make precognitive requests in your tests, Laravel's `TestCa Additionally, if you would like to assert that a precognitive request was successful, e.g., did not return any validation errors, you may use the `assertSuccessfulPrecognition` method on the response: -```php +```php tab=Pest +it('validates registration form with precognition', function () { + $response = $this->withPrecognition() + ->post('/register', [ + 'name' => 'Taylor Otwell', + ]); + + $response->assertSuccessfulPrecognition(); + + expect(User::count())->toBe(0); +}); +``` + +```php tab=PHPUnit public function test_it_validates_registration_form_with_precognition() { $response = $this->withPrecognition() From 29cb9d388545bbd6d74b29169c60947d9a15b22c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 29 Feb 2024 08:27:36 +1100 Subject: [PATCH 1481/2609] [10.x] Global Http client options (#9270) * global default options * Update http-client.md --------- Co-authored-by: Taylor Otwell --- http-client.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index 78dfc8255fe..c49aaa57d8f 100644 --- a/http-client.md +++ b/http-client.md @@ -385,12 +385,31 @@ Http::globalResponseMiddleware(fn ($response) => $response->withHeader( ### Guzzle Options -You may specify additional [Guzzle request options](http://docs.guzzlephp.org/en/stable/request-options.html) using the `withOptions` method. The `withOptions` method accepts an array of key / value pairs: +You may specify additional [Guzzle request options](http://docs.guzzlephp.org/en/stable/request-options.html) for an outgoing request using the `withOptions` method. The `withOptions` method accepts an array of key / value pairs: $response = Http::withOptions([ 'debug' => true, ])->get('http://example.com/users'); + +#### Global Options + +To configure default options for every outgoing request, you may utilize the `globalOptions` method. Typically, this method should be invoked from the `boot` method of your application's `AppServiceProvider`: + +```php +use Illuminate\Support\Facades\Http; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Http::globalOptions([ + 'allow_redirects' => false, + ]); +} +``` + ## Concurrent Requests From 91ec47d084997bfe0f4b8df4eb04b7a08337c9e3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 28 Feb 2024 13:46:54 -0800 Subject: [PATCH 1482/2609] wip --- prompts.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/prompts.md b/prompts.md index b249e57c056..b1cf19df434 100644 --- a/prompts.md +++ b/prompts.md @@ -105,6 +105,15 @@ $name = text( The closure will receive the value that has been entered and may return an error message, or `null` if the validation passes. +Alternatively, you may leverage the power of Laravel's [validator](/docs/{{version}}/validation). To do so, provide an array containing the name of the attribute and the desired validation rules to the `validate` argument: + +```php +$name = text( + label: 'What is your name?', + validate: ['name' => 'required|max:255|unique:users,name'] +); +``` + ### Password @@ -164,6 +173,15 @@ $password = password( The closure will receive the value that has been entered and may return an error message, or `null` if the validation passes. +Alternatively, you may leverage the power of Laravel's [validator](/docs/{{version}}/validation). To do so, provide an array containing the name of the attribute and the desired validation rules to the `validate` argument: + +```php +$password = password( + label: 'What is your password?', + validate: ['password' => 'min:8'] +); +``` + ### Confirm @@ -451,6 +469,16 @@ $name = suggest( The closure will receive the value that has been entered and may return an error message, or `null` if the validation passes. +Alternatively, you may leverage the power of Laravel's [validator](/docs/{{version}}/validation). To do so, provide an array containing the name of the attribute and the desired validation rules to the `validate` argument: + +```php +$name = suggest( + label: 'What is your name?', + options: ['Taylor', 'Dayle'], + validate: ['name' => 'required|min:3|max:255'] +); +``` + ### Search From ed3eb168503bfcab88bfc2c72558ef834bb48a70 Mon Sep 17 00:00:00 2001 From: GiYmon <44269882+GiYmon@users.noreply.github.com> Date: Thu, 29 Feb 2024 18:11:26 +0100 Subject: [PATCH 1483/2609] Update requests.md (#9432) --- requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests.md b/requests.md index 39f4d9dda3c..e2dbc005f27 100644 --- a/requests.md +++ b/requests.md @@ -187,7 +187,7 @@ The `ip` method may be used to retrieve the IP address of the client that made t $ipAddress = $request->ip(); -If you would like to retrieve an array of IP addresses, including all of the client IP addesses that were forwarded by proxies, you may use the `ips` method. The "original" client IP address will be at the end of the array: +If you would like to retrieve an array of IP addresses, including all of the client IP addresses that were forwarded by proxies, you may use the `ips` method. The "original" client IP address will be at the end of the array: $ipAddresses = $request->ips(); From e3a4ee96a60020235cefbfe94bba1bc69f5b28e3 Mon Sep 17 00:00:00 2001 From: MrShnaider Date: Fri, 1 Mar 2024 23:38:52 +0300 Subject: [PATCH 1484/2609] [11.x] Adds manual middleware configuration for L11 --- middleware.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/middleware.md b/middleware.md index 16da7b74f44..34b2737bde6 100644 --- a/middleware.md +++ b/middleware.md @@ -122,6 +122,21 @@ If you want a middleware to run during every HTTP request to your application, y The `$middleware` object provided to the `withMiddleware` closure is an instance of `Illuminate\Foundation\Configuration\Middleware` and is responsible for managing the middleware assigned to your application's routes. The `append` method adds the middleware to the end of the list of global middleware. If you would like to add a middleware to the beginning of the list, you should use the `prepend` method. +If you would like to manage global middleware manually, you can fully rewrite predefined ones with `use` method. The code snippet below contain all predefined middleware, so you can change them, as you would like to: + + ->withMiddleware(function (Middleware $middleware) { + $middleware->use([ + // \Illuminate\Http\Middleware\TrustHosts::class, + \Illuminate\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Http\Middleware\ValidatePostSize::class, + \Illuminate\Foundation\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]); + }) + + ### Assigning Middleware to Routes @@ -233,6 +248,25 @@ If you would like to append or prepend middleware to these groups, you may use t ]); }) +If you would like to manage middleware on `web` and `api` groups manually, you can fully rewrite predefined ones. The code snippet below contain all predefined middleware, so you can change them, as you would like to: + + ->withMiddleware(function (Middleware $middleware) { + $middleware->group('web', [ + \Illuminate\Cookie\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + // \Illuminate\Session\Middleware\AuthenticateSession::class, + ]); + $middleware->group('api', [ + // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + // 'throttle:api', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ]); + }) + Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware: Route::get('/', function () { From ac96dfa7b0fff0074f6f721daf130aa4319cd65b Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sat, 2 Mar 2024 00:34:02 +0000 Subject: [PATCH 1485/2609] Rewrites `valet` (#9439) --- valet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/valet.md b/valet.md index 5b42d8ffabc..9750fed374f 100644 --- a/valet.md +++ b/valet.md @@ -109,7 +109,7 @@ Valet will automatically start its required services each time your machine boot 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: ```shell -valet use php@8.1 +valet use php@8.2 valet use php ``` @@ -117,7 +117,7 @@ valet use php You may also create a `.valetrc` file in the root of your project. The `.valetrc` file should contain the PHP version the site should use: ```shell -php=php@8.1 +php=php@8.2 ``` Once this file has been created, you may simply execute the `valet use` command and the command will determine the site's preferred PHP version by reading the file. From 871dcf58037ba89a490b4f5e11bb736163ee1ab0 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sat, 2 Mar 2024 00:34:42 +0000 Subject: [PATCH 1486/2609] Rewrites `telescope` (#9438) --- telescope.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/telescope.md b/telescope.md index 2222ba8e167..991623e7335 100644 --- a/telescope.md +++ b/telescope.md @@ -48,7 +48,7 @@ You may use the Composer package manager to install Telescope into your Laravel 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 in order to create the tables needed to store Telescope's data: +After installing Telescope, publish its assets and migrations 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: ```shell php artisan telescope:install @@ -56,11 +56,6 @@ php artisan telescope:install php artisan migrate ``` - -#### 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 @@ -74,7 +69,7 @@ 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: +After running `telescope:install`, you should remove the `TelescopeServiceProvider` service provider registration from your application's `bootstrap/providers.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. @@ -113,11 +108,15 @@ If desired, you may disable Telescope's data collection entirely using the `enab 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(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('telescope:prune')->daily(); By default, all entries older than 24 hours will be pruned. You may use the `hours` option when calling the command to determine how long to retain Telescope data. For example, the following command will delete all records created over 48 hours ago: - $schedule->command('telescope:prune --hours=48')->daily(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('telescope:prune --hours=48')->daily(); ### Dashboard Authorization From be334c7476b92174b73363f25c6e4d3cf4b88318 Mon Sep 17 00:00:00 2001 From: Michael <12945959+9mdv@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:59:21 +0800 Subject: [PATCH 1487/2609] Fix typo (#9447) * Update testing.md * Update testing.md * Update broadcasting.md --------- Co-authored-by: Taylor Otwell --- broadcasting.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 68de72b5f6a..9c0390bfd1f 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -263,7 +263,7 @@ Events are broadcast over "channels", which may be specified as public or privat 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 an `OrderShipmentStatusUpdated` event is fired when a shipping status update is processed by the application: use App\Events\OrderShipmentStatusUpdated; @@ -702,7 +702,7 @@ However, remember that we also broadcast the task's creation. If your JavaScript #### Configuration -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. +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 an `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 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: @@ -857,7 +857,7 @@ Echo.join(`chat.${roomId}`) }); ``` -The `here` callback will be executed immediately once the channel is joined successfully, and will receive an array containing the user information for all of the other users currently subscribed to the channel. The `joining` method will be executed when a new user joins a channel, while the `leaving` method will be executed when a user leaves the channel. The `error` method will be executed when the authentication endpoint returns a HTTP status code other than 200 or if there is a problem parsing the returned JSON. +The `here` callback will be executed immediately once the channel is joined successfully, and will receive an array containing the user information for all of the other users currently subscribed to the channel. The `joining` method will be executed when a new user joins a channel, while the `leaving` method will be executed when a user leaves the channel. The `error` method will be executed when the authentication endpoint returns an HTTP status code other than 200 or if there is a problem parsing the returned JSON. ### Broadcasting to Presence Channels @@ -988,7 +988,7 @@ protected function newBroadcastableEvent(string $event): BroadcastableModelEvent As you may have noticed, the `broadcastOn` method in the model example above did not return `Channel` instances. Instead, Eloquent models were returned directly. If an Eloquent model instance is returned by your model's `broadcastOn` method (or is contained in an array returned by the method), Laravel will automatically instantiate a private channel instance for the model using the model's class name and primary key identifier as the channel name. -So, an `App\Models\User` model with an `id` of `1` would be converted into a `Illuminate\Broadcasting\PrivateChannel` instance with a name of `App.Models.User.1`. Of course, in addition to returning Eloquent model instances from your model's `broadcastOn` method, you may return complete `Channel` instances in order to have full control over the model's channel names: +So, an `App\Models\User` model with an `id` of `1` would be converted into an `Illuminate\Broadcasting\PrivateChannel` instance with a name of `App.Models.User.1`. Of course, in addition to returning Eloquent model instances from your model's `broadcastOn` method, you may return complete `Channel` instances in order to have full control over the model's channel names: ```php use Illuminate\Broadcasting\PrivateChannel; From 215d89e5b96604f5c1ae6f22d46df693d76c0b55 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 5 Mar 2024 01:16:07 +1100 Subject: [PATCH 1488/2609] Nullable `failed` method (#9445) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 26bd1166556..5044e4c55bf 100644 --- a/queues.md +++ b/queues.md @@ -1897,7 +1897,7 @@ When a particular job fails, you may want to send an alert to your users or reve /** * Handle a job failure. */ - public function failed(Throwable $exception): void + public function failed(?Throwable $exception): void { // Send user notification of failure, etc... } From e9aa383481916e15ce40277e841f3e0764c07178 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 4 Mar 2024 18:25:59 +0330 Subject: [PATCH 1489/2609] [11.x] Add binary type and other enhancements on migartions (#9449) * Update migrations.md * formatting --------- Co-authored-by: Taylor Otwell --- migrations.md | 72 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/migrations.md b/migrations.md index 86de2036511..9df9c3a4200 100644 --- a/migrations.md +++ b/migrations.md @@ -479,6 +479,12 @@ The `binary` method creates a `BLOB` equivalent column: $table->binary('photo'); +When utilizing MySQL, MariaDB, or SQL Server, you may pass `length` and `fixed` arguments to create `VARBINARY` or `BINARY` equivalent column: + + $table->binary('data', length: 16); // VARBINARY(16) + + $table->binary('data', length: 16, fixed: true); // BINARY(16) + #### `boolean()` {.collection-method} @@ -491,21 +497,21 @@ The `boolean` method creates a `BOOLEAN` equivalent column: The `char` method creates a `CHAR` equivalent column with of a given length: - $table->char('name', 100); + $table->char('name', length: 100); #### `dateTimeTz()` {.collection-method} -The `dateTimeTz` method creates a `DATETIME` (with timezone) equivalent column with an optional precision (total digits): +The `dateTimeTz` method creates a `DATETIME` (with timezone) equivalent column with an optional fractional seconds precision: - $table->dateTimeTz('created_at', $precision = 0); + $table->dateTimeTz('created_at', precision: 0); #### `dateTime()` {.collection-method} -The `dateTime` method creates a `DATETIME` equivalent column with an optional precision (total digits): +The `dateTime` method creates a `DATETIME` equivalent column with an optional fractional seconds precision: - $table->dateTime('created_at', $precision = 0); + $table->dateTime('created_at', precision: 0); #### `date()` {.collection-method} @@ -519,7 +525,7 @@ The `date` method creates a `DATE` equivalent column: 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); + $table->decimal('amount', total: 8, places: 2); #### `double()` {.collection-method} @@ -540,7 +546,7 @@ The `enum` method creates a `ENUM` equivalent column with the given valid values The `float` method creates a `FLOAT` equivalent column with the given precision: - $table->float('amount', $precision = 53); + $table->float('amount', precision: 53); #### `foreignId()` {.collection-method} @@ -641,6 +647,10 @@ The `longText` method creates a `LONGTEXT` equivalent column: $table->longText('description'); +When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `LONGBLOB` equivalent column: + + $table->longText('data')->charset('binary'); // LONGBLOB + #### `macAddress()` {.collection-method} @@ -669,6 +679,10 @@ The `mediumText` method creates a `MEDIUMTEXT` equivalent column: $table->mediumText('description'); +When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `MEDIUMBLOB` equivalent column: + + $table->mediumText('data')->charset('binary'); // MEDIUMBLOB + #### `morphs()` {.collection-method} @@ -683,7 +697,7 @@ This method is intended to be used when defining the columns necessary for a pol The `nullableTimestamps` method is an alias of the [timestamps](#column-method-timestamps) method: - $table->nullableTimestamps(0); + $table->nullableTimestamps(precision: 0); #### `nullableMorphs()` {.collection-method} @@ -737,23 +751,23 @@ The `smallInteger` method creates a `SMALLINT` equivalent column: #### `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: +The `softDeletesTz` method adds a nullable `deleted_at` `TIMESTAMP` (with timezone) equivalent column with an optional fractional seconds precision. This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality: - $table->softDeletesTz($column = 'deleted_at', $precision = 0); + $table->softDeletesTz('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: +The `softDeletes` method adds a nullable `deleted_at` `TIMESTAMP` equivalent column with an optional fractional seconds precision. This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality: - $table->softDeletes($column = 'deleted_at', $precision = 0); + $table->softDeletes('deleted_at', precision: 0); #### `string()` {.collection-method} The `string` method creates a `VARCHAR` equivalent column of the given length: - $table->string('name', 100); + $table->string('name', length: 100); #### `text()` {.collection-method} @@ -762,47 +776,51 @@ The `text` method creates a `TEXT` equivalent column: $table->text('description'); +When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `BLOB` equivalent column: + + $table->text('data')->charset('binary'); // BLOB + #### `timeTz()` {.collection-method} -The `timeTz` method creates a `TIME` (with timezone) equivalent column with an optional precision (total digits): +The `timeTz` method creates a `TIME` (with timezone) equivalent column with an optional fractional seconds precision: - $table->timeTz('sunrise', $precision = 0); + $table->timeTz('sunrise', precision: 0); #### `time()` {.collection-method} -The `time` method creates a `TIME` equivalent column with an optional precision (total digits): +The `time` method creates a `TIME` equivalent column with an optional fractional seconds precision: - $table->time('sunrise', $precision = 0); + $table->time('sunrise', precision: 0); #### `timestampTz()` {.collection-method} -The `timestampTz` method creates a `TIMESTAMP` (with timezone) equivalent column with an optional precision (total digits): +The `timestampTz` method creates a `TIMESTAMP` (with timezone) equivalent column with an optional fractional seconds precision: - $table->timestampTz('added_at', $precision = 0); + $table->timestampTz('added_at', precision: 0); #### `timestamp()` {.collection-method} -The `timestamp` method creates a `TIMESTAMP` equivalent column with an optional precision (total digits): +The `timestamp` method creates a `TIMESTAMP` equivalent column with an optional fractional seconds precision: - $table->timestamp('added_at', $precision = 0); + $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): +The `timestampsTz` method creates `created_at` and `updated_at` `TIMESTAMP` (with timezone) equivalent columns with an optional fractional seconds precision: - $table->timestampsTz($precision = 0); + $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): +The `timestamps` method creates `created_at` and `updated_at` `TIMESTAMP` equivalent columns with an optional fractional seconds precision: - $table->timestamps($precision = 0); + $table->timestamps(precision: 0); #### `tinyIncrements()` {.collection-method} @@ -825,6 +843,10 @@ The `tinyText` method creates a `TINYTEXT` equivalent column: $table->tinyText('notes'); +When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `TINYBLOB` equivalent column: + + $table->tinyText('data')->charset('binary'); // TINYBLOB + #### `unsignedBigInteger()` {.collection-method} From 85cbf31a82b51ba2b587eada32d752a93aa8c4c0 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 4 Mar 2024 21:01:53 +0000 Subject: [PATCH 1490/2609] [11.x] Documents `Synchronous Queue Jobs Changes In Database Transactions` (#9450) * Documents `Synchronous Queue Jobs Changes In Database Transactions` * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- upgrade.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/upgrade.md b/upgrade.md index ff9f52fc395..a2cf2143343 100644 --- a/upgrade.md +++ b/upgrade.md @@ -417,6 +417,15 @@ The `Illuminate\Bus\BatchRepository` interface has received a new `rollBack` met public function rollBack(); ``` + +#### Synchronous Jobs in Database Transactions + +**Likelihood Of Impact: Very Low** + +Previously, synchronous jobs (jobs using the `sync` queue driver) would execute immediately, regardless of whether the `after_commit` configuration option of the queue connection was set to `true` or the `afterCommit` method was invoked on the job. + +In Laravel 11, synchronous queue jobs will now respect the "after commit" configuration of the queue connection or job. + ### Rate Limiting From 7c128d3f7acb7a5a5d15e4e6de4a218c8a562b50 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 13:07:42 +0000 Subject: [PATCH 1491/2609] [11.x] Sets release date (#9452) * Adjusts release date * wip * Apply suggestions from code review Co-authored-by: Dries Vints --------- Co-authored-by: Dries Vints --- releases.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releases.md b/releases.md index 4a39aa37a4d..cb5e5769f9c 100644 --- a/releases.md +++ b/releases.md @@ -28,8 +28,8 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | --- | --- | --- | --- | --- | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | -| 11 | 8.2 - 8.3 | Q1 2024 | August 5th, 2025 | February 3rd, 2026 | -| 12 | 8.2 - 8.3 | Q1 2025 | August, 2026 | February, 2027 | +| 11 | 8.2 - 8.3 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | +| 12 | 8.2 - 8.3 | Q1 2025 | Q3, 2026 | Q1, 2027 |
    From ae20820655bb6dfcb337edf44a292af6e68058eb Mon Sep 17 00:00:00 2001 From: Hussain Aminu Date: Tue, 5 Mar 2024 16:07:54 +0300 Subject: [PATCH 1492/2609] Fix: Typo in the reverb section of the documentation (#9451) --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index 1bbdd258232..5ee5dafc002 100644 --- a/reverb.md +++ b/reverb.md @@ -48,7 +48,7 @@ The `reverb:install` command will automatically start Reverb using a sensible se ### Application Credentials -In order establish a connection to Reverb, a set of Reverb "application" credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You may define these credentials using the following environment variables: +In order to establish a connection to Reverb, a set of Reverb "application" credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You may define these credentials using the following environment variables: ```ini REVERB_APP_ID=my-app-id From 0f129aef8b421eeef29dd51ab2038a2eb7cdb620 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 5 Mar 2024 14:50:32 +0000 Subject: [PATCH 1493/2609] Revert "[11.x] Bump supported database versions" (#9113) * Revert "Update database.md (#9017)" This reverts commit c58a6cbb1c9f407594695adf0af1061c6abd4d72. * Update database.md Co-authored-by: Julius Kiekbusch --------- Co-authored-by: Taylor Otwell Co-authored-by: Julius Kiekbusch --- database.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database.md b/database.md index 3f52ca1634a..86d7dba524c 100644 --- a/database.md +++ b/database.md @@ -19,9 +19,9 @@ Almost every modern web application interacts with a database. Laravel makes int
    -- MariaDB 10.11+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) -- MySQL 8.0+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) -- PostgreSQL 12.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) +- MariaDB 10.3+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) +- MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) +- PostgreSQL 10.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) - SQLite 3.35.0+ - SQL Server 2017+ ([Version Policy](https://docs.microsoft.com/en-us/lifecycle/products/?products=sql-server)) From 4b03c1189df91494c12118c944af2d098d84cf42 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 17:02:43 +0000 Subject: [PATCH 1494/2609] [11.x] Rewrites `passport` (#9429) * Rewrites `passport` * formatting --------- Co-authored-by: Taylor Otwell --- passport.md | 116 ++++++++++++++++++++++------------------------------ 1 file changed, 50 insertions(+), 66 deletions(-) diff --git a/passport.md b/passport.md index 0b3f72df107..ff5a9270e8d 100644 --- a/passport.md +++ b/passport.md @@ -4,7 +4,6 @@ - [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) @@ -68,18 +67,14 @@ To get started, install Passport via the Composer package manager: composer require laravel/passport ``` -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: - -```shell -php artisan migrate -``` - -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: +Next, you should execute the `passport:install` 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: ```shell php artisan passport:install ``` +Additionally, the `passport:install` command will create the encryption keys required to generate secure access tokens, as well as the "personal access" and "password grant" clients, which will be used to generate access tokens. + > [!NOTE] > If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). @@ -131,10 +126,10 @@ When deploying Passport to your application's servers for the first time, you wi 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. Typically, this method should be called from the `boot` method of your application's `App\Providers\AuthServiceProvider` class: +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\AppServiceProvider` class: /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -162,15 +157,6 @@ 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 `App\Providers\AppServiceProvider` class. You may export the default migrations using the `vendor:publish` Artisan command: - -```shell -php artisan vendor:publish --tag=passport-migrations -``` - ### Upgrading Passport @@ -182,7 +168,7 @@ When upgrading to a new major version of Passport, it's important that you caref ### 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 `App\Providers\AuthServiceProvider` class: +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\AppServiceProvider` class: use Laravel\Passport\Passport; @@ -193,10 +179,10 @@ Once enabled, all of your client secrets will only be displayable to the user im ### 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 application's `App\Providers\AuthServiceProvider` class: +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\AppServiceProvider` class: /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -220,7 +206,7 @@ You are free to extend the models used internally by Passport by defining your o // ... } -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: +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\AppServiceProvider` class: use App\Models\Passport\AuthCode; use App\Models\Passport\Client; @@ -229,7 +215,7 @@ After defining your model, you may instruct Passport to use your custom model vi use App\Models\Passport\Token; /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -546,15 +532,11 @@ php artisan passport:purge --revoked php artisan passport:purge --expired ``` -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: +You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your application's `routes/console.php` file to automatically prune your tokens on a schedule: - /** - * Define the application's command schedule. - */ - protected function schedule(Schedule $schedule): void - { - $schedule->command('passport:purge')->hourly(); - } + use Laravel\Support\Facades\Schedule; + + Schedule::command('passport:purge')->hourly(); ## Authorization Code Grant With PKCE @@ -778,10 +760,10 @@ When authenticating using the password grant, Passport will use the `password` a > [!WARNING] > We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). -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 application's `App\Providers\AuthServiceProvider` class: +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 application's `App\Providers\AppServiceProvider` class: /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -821,13 +803,15 @@ Before your application can issue tokens via the client credentials grant, you w php artisan passport:client --client ``` -Next, to use this grant type, you may add the `CheckClientCredentials` middleware to the `$middlewareAliases` property of your application's `app/Http/Kernel.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: use Laravel\Passport\Http\Middleware\CheckClientCredentials; - protected $middlewareAliases = [ - 'client' => CheckClientCredentials::class, - ]; + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'client' => CheckClientCredentials::class + ]); + }) Then, attach the middleware to a route: @@ -1017,10 +1001,10 @@ 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 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: +You may define your API's scopes using the `Passport::tokensCan` method in the `boot` method of your application's `App\Providers\AppServiceProvider` 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: /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -1033,7 +1017,7 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` ### Default Scope -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: +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\AppServiceProvider` class: use Laravel\Passport\Passport; @@ -1079,10 +1063,17 @@ If you are issuing personal access tokens using the `App\Models\User` model's `c ### Checking Scopes -Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, add the following middleware to the `$middlewareAliases` property of your `app/Http/Kernel.php` file: +Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, define the following middleware aliases in your application's `bootstrap/app.php` file: - 'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class, - 'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class, + use Laravel\Passport\Http\Middleware\CheckForAnyScope; + use Laravel\Passport\Http\Middleware\CheckScopes; + + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'scopes' => CheckScopes::class, + 'scope' => CheckForAnyScope::class, + ]); + }) #### Check For All Scopes @@ -1141,12 +1132,15 @@ You may determine if a given scope has been defined using the `hasScope` method: When building an API, it can be extremely useful to be able to consume your own API from your JavaScript application. This approach to API development allows your own application to consume the same API that you are sharing with the world. The same API may be consumed by your web application, mobile applications, third-party applications, and any SDKs that you may publish on various package managers. -Typically, if you want to consume your API from your JavaScript application, you would need to manually send an access token to the application and pass it with each request to your application. However, Passport includes a middleware that can handle this for you. All you need to do is add the `CreateFreshApiToken` middleware to your `web` middleware group in your `app/Http/Kernel.php` file: +Typically, if you want to consume your API from your JavaScript application, you would need to manually send an access token to the application and pass it with each request to your application. However, Passport includes a middleware that can handle this for you. All you need to do is append the `CreateFreshApiToken` middleware to the `web` middleware group in your application's `bootstrap/app.php` file: - 'web' => [ - // Other middleware... - \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, - ], + use Laravel\Passport\Http\Middleware\CreateFreshApiToken; + + ->withMiddleware(function (Middleware $middleware) { + $middleware->web(append: [ + CreateFreshApiToken::class, + ]); + }) > [!WARNING] > You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. @@ -1161,10 +1155,10 @@ This middleware will attach a `laravel_token` cookie to your outgoing responses. #### 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 application's `App\Providers\AuthServiceProvider` class: +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\AppServiceProvider` class: /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -1182,22 +1176,12 @@ 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. 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. - * - * @var array - */ - protected $listen = [ - 'Laravel\Passport\Events\AccessTokenCreated' => [ - 'App\Listeners\RevokeOldTokens', - ], +Passport raises events when issuing access tokens and refresh tokens. You may [listen for these events](/docs/{{version}}/events) to prune or revoke other access tokens in your database: - 'Laravel\Passport\Events\RefreshTokenCreated' => [ - 'App\Listeners\PruneOldTokens', - ], - ]; +Event Name | +------------- | +`Laravel\Passport\Events\AccessTokenCreated` | +`Laravel\Passport\Events\RefreshTokenCreated` | ## Testing From a4f5769cba84e5c8650a33eeb752fafc8bf03dfa Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Mar 2024 11:11:25 -0600 Subject: [PATCH 1495/2609] formatting --- middleware.md | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/middleware.md b/middleware.md index 34b2737bde6..eadd0ecc74f 100644 --- a/middleware.md +++ b/middleware.md @@ -122,7 +122,10 @@ If you want a middleware to run during every HTTP request to your application, y The `$middleware` object provided to the `withMiddleware` closure is an instance of `Illuminate\Foundation\Configuration\Middleware` and is responsible for managing the middleware assigned to your application's routes. The `append` method adds the middleware to the end of the list of global middleware. If you would like to add a middleware to the beginning of the list, you should use the `prepend` method. -If you would like to manage global middleware manually, you can fully rewrite predefined ones with `use` method. The code snippet below contain all predefined middleware, so you can change them, as you would like to: + +#### Manually Managing Laravel's Default Global Middleware + +If you would like to manage Laravel's global middleware stack manually, you may provide Laravel's default stack of global middleware to the `use` method. Then, you may adjust the default middleware stack as necessary: ->withMiddleware(function (Middleware $middleware) { $middleware->use([ @@ -212,9 +215,25 @@ Sometimes you may want to group several middleware under a single key to make th First::class, Second::class, ]); + + $middleware->prependToGroup('group-name', [ + First::class, + Second::class, + ]); }) -You may prepend middleware to a group using the `prependToGroup` method. +Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware: + + Route::get('/', function () { + // ... + })->middleware('group-name'); + + Route::middleware(['group-name'])->group(function () { + // ... + }); + + +#### Laravel's Default Middleware Groups Laravel includes predefined `web` and `api` middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, Laravel automatically applies these middleware groups to the corresponding `routes/web.php` and `routes/api.php` files: @@ -248,7 +267,10 @@ If you would like to append or prepend middleware to these groups, you may use t ]); }) -If you would like to manage middleware on `web` and `api` groups manually, you can fully rewrite predefined ones. The code snippet below contain all predefined middleware, so you can change them, as you would like to: + +#### Manually Managing Laravel's Default Middleware Groups + +If you would like to manually manage all of the middleware within Laravel's default `web` and `api` middleware groups, you may redefine the groups entirely. The example below will define the `web` and `api` middleware groups with their default middleware, allowing you to customize them as necessary: ->withMiddleware(function (Middleware $middleware) { $middleware->group('web', [ @@ -260,6 +282,7 @@ If you would like to manage middleware on `web` and `api` groups manually, you c \Illuminate\Routing\Middleware\SubstituteBindings::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, ]); + $middleware->group('api', [ // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // 'throttle:api', @@ -267,16 +290,6 @@ If you would like to manage middleware on `web` and `api` groups manually, you c ]); }) -Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware: - - Route::get('/', function () { - // ... - })->middleware('group-name'); - - Route::middleware(['group-name'])->group(function () { - // ... - }); - > [!NOTE] > By default, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `bootstrap/app.php` file. From d725349b2cc2eb421950001a2831336154843999 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 17:49:51 +0000 Subject: [PATCH 1496/2609] [11.x] Misc corrections for L11 (#9441) * Misc corrections for L11 * formatting * formatting --------- Co-authored-by: Taylor Otwell --- controllers.md | 2 +- csrf.md | 4 ++-- database.md | 4 ++-- fortify.md | 2 +- passwords.md | 4 +++- pennant.md | 6 +++--- queues.md | 20 ++++++++++++++------ 7 files changed, 26 insertions(+), 16 deletions(-) diff --git a/controllers.md b/controllers.md index bf1cef17c07..d85b60ef152 100644 --- a/controllers.md +++ b/controllers.md @@ -64,7 +64,7 @@ Once you have written a controller class and method, you may define a route to t 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. > [!NOTE] -> Controllers are not **required** to extend a base class. However, you will not have access to convenient features such as the `middleware` and `authorize` methods. +> Controllers are not **required** to extend a base class. However, it is sometimes convenient to extend a base controller class that contains methods that should be shared across all of your controllers. ### Single Action Controllers diff --git a/csrf.md b/csrf.md index e87608501e2..efdabdbe978 100644 --- a/csrf.md +++ b/csrf.md @@ -60,7 +60,7 @@ Anytime you define a "POST", "PUT", "PATCH", or "DELETE" HTML form in your appli ``` -The `App\Http\Middleware\VerifyCsrfToken` [middleware](/docs/{{version}}/middleware), which is included in the `web` middleware group by default, will automatically verify that the token in the request input matches the token stored in the session. When these two tokens match, we know that the authenticated user is the one initiating the request. +The `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` [middleware](/docs/{{version}}/middleware), which is included in the `web` middleware group by default, will automatically verify that the token in the request input matches the token stored in the session. When these two tokens match, we know that the authenticated user is the one initiating the request. ### CSRF Tokens & SPAs @@ -88,7 +88,7 @@ Typically, you should place these kinds of routes outside of the `web` middlewar ## X-CSRF-TOKEN -In addition to checking for the CSRF token as a POST parameter, the `App\Http\Middleware\VerifyCsrfToken` middleware will also check for the `X-CSRF-TOKEN` request header. You could, for example, store the token in an HTML `meta` tag: +In addition to checking for the CSRF token as a POST parameter, the `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` middleware, which is included in the `web` middleware group by default, will also check for the `X-CSRF-TOKEN` request header. You could, for example, store the token in an HTML `meta` tag: ```blade diff --git a/database.md b/database.md index 86d7dba524c..82abc1982d4 100644 --- a/database.md +++ b/database.md @@ -432,7 +432,7 @@ To get started, you should schedule the `db:monitor` command to [run every minut php artisan db:monitor --databases=mysql,pgsql --max=100 ``` -Scheduling this command alone is not enough to trigger a notification alerting you of the number of open connections. When the command encounters a database that has an open connection count that exceeds your threshold, a `DatabaseBusy` event will be dispatched. You should listen for this event within your application's `EventServiceProvider` in order to send a notification to you or your development team: +Scheduling this command alone is not enough to trigger a notification alerting you of the number of open connections. When the command encounters a database that has an open connection count that exceeds your threshold, a `DatabaseBusy` event will be dispatched. You should listen for this event within your application's `AppServiceProvider` in order to send a notification to you or your development team: ```php use App\Notifications\DatabaseApproachingMaxConnections; @@ -441,7 +441,7 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Notification; /** - * Register any other events for your application. + * Bootstrap any application services. */ public function boot(): void { diff --git a/fortify.md b/fortify.md index 49feb41c72f..27dd965df3d 100644 --- a/fortify.md +++ b/fortify.md @@ -532,7 +532,7 @@ If the request to resend the verification link email was successful, Fortify wil ### Protecting Routes -To specify that a route or group of routes requires that the user has verified their email address, you should attach Laravel's built-in `verified` middleware to the route. This middleware is registered within your application's `App\Http\Kernel` class: +To specify that a route or group of routes requires that the user has verified their email address, you should attach Laravel's built-in `verified` middleware to the route. The `verified` middleware alias is automatically registered by Laravel and serves as an alias for the `Illuminate\Routing\Middleware\ValidateSignature` middleware: ```php Route::get('/dashboard', function () { diff --git a/passwords.md b/passwords.md index c1e026ed243..1f2ae5c7489 100644 --- a/passwords.md +++ b/passwords.md @@ -160,7 +160,9 @@ php artisan auth:clear-resets If you would like to automate this process, consider adding the command to your application's [scheduler](/docs/{{version}}/scheduling): - $schedule->command('auth:clear-resets')->everyFifteenMinutes(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('auth:clear-resets')->everyFifteenMinutes(); ## Customization diff --git a/pennant.md b/pennant.md index cad9ee69b49..e21dfe0b25b 100644 --- a/pennant.md +++ b/pennant.md @@ -988,14 +988,14 @@ For example, you may find it useful to listen for this event and `report` or thr namespace App\Providers; -use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; +use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Event; use Laravel\Pennant\Events\RetrievingUnknownFeature; -class EventServiceProvider extends ServiceProvider +class AppServiceProvider extends ServiceProvider { /** - * Register any other events for your application. + * Bootstrap any application services. */ public function boot(): void { diff --git a/queues.md b/queues.md index 2a6c7b7ad9f..7ad96611d11 100644 --- a/queues.md +++ b/queues.md @@ -1491,19 +1491,27 @@ php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5 Without pruning, the `job_batches` table can accumulate records very quickly. To mitigate this, you should [schedule](/docs/{{version}}/scheduling) the `queue:prune-batches` Artisan command to run daily: - $schedule->command('queue:prune-batches')->daily(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('queue:prune-batches')->daily(); By default, all finished batches that are more than 24 hours old will be pruned. You may use the `hours` option when calling the command to determine how long to retain batch data. For example, the following command will delete all batches that finished over 48 hours ago: - $schedule->command('queue:prune-batches --hours=48')->daily(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('queue:prune-batches --hours=48')->daily(); Sometimes, your `jobs_batches` table may accumulate batch records for batches that never completed successfully, such as batches where a job failed and that job was never retried successfully. You may instruct the `queue:prune-batches` command to prune these unfinished batch records using the `unfinished` option: - $schedule->command('queue:prune-batches --hours=48 --unfinished=72')->daily(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('queue:prune-batches --hours=48 --unfinished=72')->daily(); Likewise, your `jobs_batches` table may also accumulate batch records for cancelled batches. You may instruct the `queue:prune-batches` command to prune these cancelled batch records using the `cancelled` option: - $schedule->command('queue:prune-batches --hours=48 --cancelled=72')->daily(); + use Illuminate\Support\Facades\Schedule; + + Schedule::command('queue:prune-batches --hours=48 --cancelled=72')->daily(); ### Storing Batches in DynamoDB @@ -2085,7 +2093,7 @@ To get started, you should schedule the `queue:monitor` command to [run every mi php artisan queue:monitor redis:default,redis:deployments --max=100 ``` -Scheduling this command alone is not enough to trigger a notification alerting you of the queue's overwhelmed status. When the command encounters a queue that has a job count exceeding your threshold, an `Illuminate\Queue\Events\QueueBusy` event will be dispatched. You may listen for this event within your application's `EventServiceProvider` in order to send a notification to you or your development team: +Scheduling this command alone is not enough to trigger a notification alerting you of the queue's overwhelmed status. When the command encounters a queue that has a job count exceeding your threshold, an `Illuminate\Queue\Events\QueueBusy` event will be dispatched. You may listen for this event within your application's `AppServiceProvider` in order to send a notification to you or your development team: ```php use App\Notifications\QueueHasLongWaitTime; @@ -2094,7 +2102,7 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Notification; /** - * Register any other events for your application. + * Bootstrap any application services. */ public function boot(): void { From d2962663496179c1a6bea8cb665a8f3f634f3c77 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 17:54:38 +0000 Subject: [PATCH 1497/2609] [11.x] Adjusts upgrade guide regarding publishing migration (#9440) * Adjusts upgrade guide * Add spark and cashier stripe * Update upgrade.md * Update upgrade.md * Update upgrade.md * Update upgrade.md * wip * formatting --------- Co-authored-by: Dries Vints Co-authored-by: Taylor Otwell --- upgrade.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/upgrade.md b/upgrade.md index a2cf2143343..3cb398201ca 100644 --- a/upgrade.md +++ b/upgrade.md @@ -67,18 +67,33 @@ You should update the following dependencies in your application's `composer.jso
    - `laravel/framework` to `^11.0` -- `laravel/sanctum` to `^4.0` +- `laravel/cashier-stripe` to `^15.0` (If installed) +- `laravel/passport` to `^12.0` (If installed) +- `laravel/sanctum` to `^4.0` (If installed) +- `laravel/spark-stripe` to `^5.0` (If installed) - `laravel/telescope` to `^5.0` (If installed)
    -If your application is using Laravel Telescope, you should run the following command to publish Telescope's migrations to your application. Telescope no longer automatically loads migrations from its own migrations directory: +If your application is using Laravel Cashier Stripe, Passport, Sanctum, Spark Stripe, or Telescope, you will need to publish their migrations to your application. Cashier Stripe, Passport, Sanctum, Spark Stripe, and Telescope **no longer automatically load migrations from their own migrations** directory. Therefore, you should run the following command to publish their migrations to your application: ```bash +php artisan vendor:publish --tag=cashier-migrations +php artisan vendor:publish --tag=passport-migrations +php artisan vendor:publish --tag=sanctum-migrations +php artisan vendor:publish --tag=spark-migrations php artisan vendor:publish --tag=telescope-migrations ``` -In addition, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. +In addition, you should review the upgrade guides for each of these packages to ensure you are aware of any additional breaking changes: + +- [Laravel Cashier Stripe](#cashier-stripe) +- [Laravel Passport](#passport) +- [Laravel Sanctum](#sanctum) +- [Laravel Spark Stripe](#spark-stripe) +- [Laravel Telescope](#telescope) + +Finally, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. ### Application Structure @@ -457,6 +472,65 @@ new ThrottlesExceptions($attempts, 2 * 60); new ThrottlesExceptionsWithRedis($attempts, 2 * 60); ``` + +### Cashier Stripe + + +#### Updating Cashier Stripe + +**Likelihood Of Impact: High** + +Laravel 11 no longer supports Cashier Stripe 14.x. Therefore, you should update your application's Laravel Cashier Stripe dependency to `^15.0` in your `composer.json` file. + +Cashier Stripe 15.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Cashier Stripe's migrations to your application: + +```shell +php artisan vendor:publish --tag=cashier-migrations +``` + +Please review the complete [Cashier Stripe upgrade guide](https://github.com/laravel/cashier-stripe/blob/15.x/UPGRADE.md) for additional breaking changes. + + +### Spark (Stripe) + + +#### Updating Spark Stripe + +**Likelihood Of Impact: High** + +Laravel 11 no longer supports Laravel Spark Stripe 4.x. Therefore, you should update your application's Laravel Spark Stripe dependency to `^5.0` in your `composer.json` file. + +Spark Stripe 5.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Spark Stripe's migrations to your application: + +```shell +php artisan vendor:publish --tag=spark-migrations +``` + +Please review the complete [Spark Stripe upgrade guide](https://spark.laravel.com/docs/spark-stripe/upgrade.html) for additional breaking changes. + + +### Passport + + +#### Updating Passport + +**Likelihood Of Impact: High** + +Laravel 11 no longer supports Laravel Passport 11.x. Therefore, you should update your application's Laravel Passport dependency to `^12.0` in your `composer.json` file. + +Passport 12.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Passport's migrations to your application: + +```shell +php artisan vendor:publish --tag=passport-migrations +``` + +In addition, the password grant type is disabled by default. You may enable it by invoking the `enablePasswordGrant` method in the `boot` method of your application's `AppServiceProvider`: + + public function boot(): void + { + Passport::enablePasswordGrant(); + } + ### Sanctum @@ -480,3 +554,19 @@ Then, in your application's `config/sanctum.php` configuration file, you should 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, ], + + +### Telescope + + +#### Updating Telescope + +**Likelihood Of Impact: High** + +Laravel 11 no longer supports Laravel Telescope 4.x. Therefore, you should update your application's Laravel Telescope dependency to `^5.0` in your `composer.json` file. + +Telescope 5.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Telescope's migrations to your application: + +```shell +php artisan vendor:publish --tag=telescope-migrations +``` From 72dbb28f9dbded332f8837e3cf419bb702708f0f Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 18:01:04 +0000 Subject: [PATCH 1498/2609] [11.x] Rewrites `sanctum` for L11 (#9437) * Rewrites `sanctum` * formatting --------- Co-authored-by: Taylor Otwell --- sanctum.md | 82 ++++++++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 48 deletions(-) diff --git a/sanctum.md b/sanctum.md index acac48dedd2..8922f2fc60a 100644 --- a/sanctum.md +++ b/sanctum.md @@ -54,39 +54,13 @@ Sanctum will only attempt to authenticate using cookies when the incoming reques ## Installation -> [!NOTE] -> The most recent versions of Laravel already include Laravel Sanctum. However, if your application's `composer.json` file does not include `laravel/sanctum`, you may follow the installation instructions below. - -You may install Laravel Sanctum via the Composer package manager: - -```shell -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 application's `config` directory: +You may install Laravel Sanctum via the `install:api` Artisan command: ```shell -php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" +php artisan install:api ``` -Finally, you should run your database migrations. Sanctum will create one database table in which to store API tokens: - -```shell -php artisan migrate -``` - -Next, if you plan to utilize Sanctum to authenticate a 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, - \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - - -#### 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 `App\Providers\AppServiceProvider` class. You may export the default migrations by executing the following command: `php artisan vendor:publish --tag=sanctum-migrations` +Next, if you plan to utilize Sanctum to authenticate a SPA, please refer to the [SPA Authentication](#spa-authentication) section of this documentation. ## Configuration @@ -103,7 +77,7 @@ Although not typically required, you are free to extend the `PersonalAccessToken // ... } -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: +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 your application's `AppServiceProvider` file: use App\Models\Sanctum\PersonalAccessToken; use Laravel\Sanctum\Sanctum; @@ -168,10 +142,17 @@ When handling an incoming request authenticated by Sanctum, you may determine if #### Token Ability Middleware -Sanctum also includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given ability. To get started, add the following middleware to the `$middlewareAliases` property of your application's `app/Http/Kernel.php` file: +Sanctum also includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given ability. To get started, define the following middleware aliases in your application's `bootstrap/app.php` file: + + use Laravel\Sanctum\Http\Middleware\CheckAbilities; + use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility; - 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class, - 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class, + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'abilities' => CheckAbilities::class, + 'ability' => CheckForAnyAbility::class, + ]); + }) The `abilities` middleware may be assigned to a route to verify that the incoming request's token has all of the listed abilities: @@ -210,9 +191,9 @@ You may be wondering why we suggest that you authenticate the routes within your use Illuminate\Http\Request; - Route::middleware('auth:sanctum')->get('/user', function (Request $request) { + Route::get('/user', function (Request $request) { return $request->user(); - }); + })->middleware('auth:sanctum'); ### Revoking Tokens @@ -248,7 +229,9 @@ return $user->createToken( If you have configured a token expiration time for your application, you may also wish to [schedule a task](/docs/{{version}}/scheduling) to prune your application's expired tokens. Thankfully, Sanctum includes a `sanctum:prune-expired` Artisan command that you may use to accomplish this. For example, you may configure a scheduled tasks to delete all expired token database records that have been expired for at least 24 hours: ```php -$schedule->command('sanctum:prune-expired --hours=24')->daily(); +use Illuminate\Support\Facades\Schedule; + +Schedule::command('sanctum:prune-expired --hours=24')->daily(); ``` @@ -261,7 +244,6 @@ For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses > [!WARNING] > In order to authenticate, your SPA and API must share the same top-level domain. However, they may be placed on different subdomains. Additionally, you should ensure that you send the `Accept: application/json` header and either the `Referer` or `Origin` header with your request. - ### Configuration @@ -276,20 +258,24 @@ First, you should configure which domains your SPA will be making requests from. #### Sanctum Middleware -Next, you should add Sanctum's middleware to your `api` middleware group within your `app/Http/Kernel.php` file. This middleware is responsible for ensuring that incoming requests from your SPA can authenticate using Laravel's session cookies, while still allowing requests from third parties or mobile applications to authenticate using API tokens: +Next, you should instruct Laravel that incoming requests from your SPA can authenticate using Laravel's session cookies, while still allowing requests from third parties or mobile applications to authenticate using API tokens. This can be easily accomplished by invoking the `statefulApi` middleware method in your application's `bootstrap/app.php` file: - 'api' => [ - \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], + ->withMiddleware(function (Middleware $middleware) { + $middleware->statefulApi(); + }) #### CORS and Cookies If you are having trouble authenticating with your application from a 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`. This may be accomplished by setting the `supports_credentials` option within your application's `config/cors.php` configuration file to `true`. +The `config/cors.php` configuration file is not published by default. If you need to customize Laravel's CORS options, you should publish the complete `cors` configuration file using the `config:publish` Artisan command: + +```bash +php artisan config:publish cors +``` + +Next, 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` and `withXSRFToken` options 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: @@ -337,9 +323,9 @@ To protect routes so that all incoming requests must be authenticated, you shoul use Illuminate\Http\Request; - Route::middleware('auth:sanctum')->get('/user', function (Request $request) { + Route::get('/user', function (Request $request) { return $request->user(); - }); + })->middleware('auth:sanctum'); ### Authorizing Private Broadcast Channels @@ -420,9 +406,9 @@ When the mobile application uses the token to make an API request to your applic 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) { + Route::get('/user', function (Request $request) { return $request->user(); - }); + })->middleware('auth:sanctum'); ### Revoking Tokens From 423602c0259af2ca8898284660152ac93eed1f55 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 18:31:18 +0000 Subject: [PATCH 1499/2609] wip (#9434) --- pulse.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pulse.md b/pulse.md index 6554742fcc6..91fc397d9ef 100644 --- a/pulse.md +++ b/pulse.md @@ -80,14 +80,14 @@ php artisan vendor:publish --tag=pulse-config ### Authorization -The Pulse dashboard may be accessed via the `/pulse` route. By default, you will only be able to access this dashboard in the `local` environment, so you will need to configure authorization for your production environments by customizing the `'viewPulse'` authorization gate. You can accomplish this within your application's `app/Providers/AuthServiceProvider.php` file: +The Pulse dashboard may be accessed via the `/pulse` route. By default, you will only be able to access this dashboard in the `local` environment, so you will need to configure authorization for your production environments by customizing the `'viewPulse'` authorization gate. You can accomplish this within your application's `app/Providers/AppServiceProvider.php` file: ```php use App\Models\User; use Illuminate\Support\Facades\Gate; /** - * Register any authentication / authorization services. + * Bootstrap any application services. */ public function boot(): void { @@ -165,7 +165,7 @@ public function boot(): void } ``` -> [!NOTE] +> [!NOTE] > You may completely customize how the authenticated user is captured and retrieved by implementing the `Laravel\Pulse\Contracts\ResolvesUsers` contract and binding it in Laravel's [service container](/docs/{{version}}/container#binding-a-singleton). @@ -191,7 +191,7 @@ If you wish to view all usage metrics on screen at the same time, you may includ To learn how to customize how Pulse retrieves and displays user information, consult our documentation on [resolving users](#dashboard-resolving-users). -> [!NOTE] +> [!NOTE] > If your application receives a lot of requests or dispatches a lot of jobs, you may wish to enable [sampling](#sampling). See the [user requests recorder](#user-requests-recorder), [user jobs recorder](#user-jobs-recorder), and [slow jobs recorder](#slow-jobs-recorder) documentation for more information. @@ -246,7 +246,7 @@ Most Pulse recorders will automatically capture entries based on framework event php artisan pulse:check ``` -> [!NOTE] +> [!NOTE] > To keep the `pulse:check` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the command does not stop running. @@ -401,7 +401,7 @@ PULSE_DB_CONNECTION=pulse ### Redis Ingest -> [!WARNING] +> [!WARNING] > The Redis Ingest requires Redis 6.2 or greater and `phpredis` or `predis` as the application's configured Redis client driver. By default, Pulse will store entries directly to the [configured database connection](#using-a-different-database) after the HTTP response has been sent to the client or a job has been processed; however, you may use Pulse's Redis ingest driver to send entries to a Redis stream instead. This can be enabled by configuring the `PULSE_INGEST_DRIVER` environment variable: @@ -422,7 +422,7 @@ When using the Redis ingest, you will need to run the `pulse:work` command to mo php artisan pulse:work ``` -> [!NOTE] +> [!NOTE] > To keep the `pulse:work` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the Pulse worker does not stop running. @@ -514,7 +514,7 @@ Once you have defined your Livewire component and template, the card may be incl ``` -> [!NOTE] +> [!NOTE] > If your card is included in a package, you will need to register the component with Livewire using the `Livewire::component` method. @@ -628,7 +628,7 @@ The available aggregation methods are: * `min` * `sum` -> [!NOTE] +> [!NOTE] > When building a card package that captures the currently authenticated user ID, you should use the `Pulse::resolveAuthenticatedUserId()` method, which respects any [user resolver customizations](#dashboard-resolving-users) made to the application. @@ -727,7 +727,7 @@ class Deployments /** * The events to listen for. * - * @var list + * @var array */ public array $listen = [ Deployment::class, From 76abbf7dc058d1666be053780b48d09c1789a9ee Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 18:32:27 +0000 Subject: [PATCH 1500/2609] [11.x] Rewrites `sail` for L11 (#9435) * Rewrites `sail` * Update sail.md --------- Co-authored-by: Taylor Otwell --- sail.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sail.md b/sail.md index e346d7a8bf6..58a363c6339 100644 --- a/sail.md +++ b/sail.md @@ -161,7 +161,7 @@ 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: +Composer commands may be executed using the `composer` command. Laravel Sail's application container includes a Composer installation: ```nothing sail composer require laravel/sanctum @@ -403,7 +403,7 @@ context: ./vendor/laravel/sail/runtimes/8.0 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.1/app +image: sail-8.2/app ``` After updating your application's `docker-compose.yml` file, you should rebuild your container images: @@ -443,14 +443,13 @@ Sometimes you may need to share your site publicly in order to preview your site sail share ``` -When sharing your site via the `share` command, you should configure your application's trusted proxies within the `TrustProxies` middleware. Otherwise, URL generation helpers such as `url` and `route` will be unable to determine the correct HTTP host that should be used during URL generation: +When sharing your site via the `share` command, you should configure your application's trusted proxies using the `trustProxies` middleware method in your application's `bootstrap/app.php` file. Otherwise, URL generation helpers such as `url` and `route` will be unable to determine the correct HTTP host that should be used during URL generation: - /** - * The trusted proxies for this application. - * - * @var array|string|null - */ - protected $proxies = '*'; + ->withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(at: [ + '*', + ]); + }) If you would like to choose the subdomain for your shared site, you may provide the `subdomain` option when executing the `share` command: From 14814da605cbbe3296d34cf46de62932446ceea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 5 Mar 2024 19:44:07 +0100 Subject: [PATCH 1501/2609] [10.x] Document FrankenPHP Docker install (#9454) * [10.x] Document FrankenPHP Docker install * formatting --------- Co-authored-by: Taylor Otwell --- octane.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/octane.md b/octane.md index 361b7b31bfb..79e5f64c3b7 100644 --- a/octane.md +++ b/octane.md @@ -84,6 +84,42 @@ services: SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port=80" # [tl! add] ``` + +#### FrankenPHP via Docker + +Using FrankenPHP's official Docker images can offer improved performance and the use additional extensions not included with static installations of FrankenPHP. In addition, the official Docker images provide support for running FrankenPHP on platforms it doesn't natively support, such as Windows. FrankenPHP's official Docker images are suitable for both local development and production usage. + +You may use the following Dockerfile as a starting point for containerizing your FrankenPHP powered Laravel application: + +```dockerfile +FROM dunglas/frankenphp + +RUN install-php-extensions \ + pcntl + # Add other PHP extensions here... + +COPY . /app + +ENTRYPOINT ["php", "artisan", "octane:frankenphp"] +``` + +Then, during development, you may utilize the following Docker Compose file to run your application: + +```yaml +# compose.yaml +services: + frankenphp: + build: + context: . + entrypoint: php artisan octane:frankenphp --max-requests=1 + ports: + - "8000:8000" + volumes: + - .:/app +``` + +You may consult [the official FrankenPHP documentation](https://frankenphp.dev/docs/docker/) for more information on running FrankenPHP with Docker. + ### RoadRunner From a23c888f161417c863b0a27f3021b4e76e77ac88 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Mar 2024 21:19:53 +0000 Subject: [PATCH 1502/2609] [11.x] Documents middleware aliases (#9442) * Documents middleware aliases * formatting * formatting --------- Co-authored-by: Taylor Otwell --- authentication.md | 4 +-- authorization.md | 2 +- middleware.md | 80 +++++++++++++++++++++++++++++++++-------------- verification.md | 2 +- 4 files changed, 61 insertions(+), 27 deletions(-) diff --git a/authentication.md b/authentication.md index 546836b3212..0ce8012f3f2 100644 --- a/authentication.md +++ b/authentication.md @@ -181,7 +181,7 @@ To determine if the user making the incoming HTTP request is authenticated, you ### 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 is an alias for the `Illuminate\Auth\Middleware\Authenticate` middleware class. Since this middleware is already aliased internally by Laravel, 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 is a [middleware alias](/docs/{{version}}/middleware#middleware-alias) for the `Illuminate\Auth\Middleware\Authenticate` class. Since this middleware is already aliased internally by Laravel, all you need to do is attach the middleware to a route definition: Route::get('/flights', function () { // Only authenticated users may access this route... @@ -457,7 +457,7 @@ In addition to calling the `logout` method, it is recommended that you invalidat 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 included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` route middleware alias: +Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` [middleware alias](/docs/{{version}}/middleware#middleware-alias): Route::middleware(['auth', 'auth.session'])->group(function () { Route::get('/', function () { diff --git a/authorization.md b/authorization.md index fde4e5d234c..6c1c79b202b 100644 --- a/authorization.md +++ b/authorization.md @@ -606,7 +606,7 @@ As previously discussed, some policy methods like `create` do not require a mode ### 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 may be attached to a route using the `can` route middleware alias, which is automatically registered by Laravel. Let's explore an example of using the `can` middleware to authorize that a user can update a post: +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 may be attached to a route using the `can` [middleware alias](/docs/{{version}}/middleware#middleware-alias), which is automatically registered by Laravel. Let's explore an example of using the `can` middleware to authorize that a user can update a post: use App\Models\Post; diff --git a/middleware.md b/middleware.md index eadd0ecc74f..a99d965b9a0 100644 --- a/middleware.md +++ b/middleware.md @@ -6,6 +6,7 @@ - [Global Middleware](#global-middleware) - [Assigning Middleware to Routes](#assigning-middleware-to-routes) - [Middleware Groups](#middleware-groups) + - [Middleware Aliases](#middleware-aliases) - [Sorting Middleware](#sorting-middleware) - [Middleware Parameters](#middleware-parameters) - [Terminable Middleware](#terminable-middleware) @@ -145,11 +146,11 @@ If you would like to manage Laravel's global middleware stack manually, you may If you would like to assign middleware to specific routes, you may invoke the `middleware` method when defining the route: - use App\Http\Middleware\Authenticate; + use App\Http\Middleware\EnsureTokenIsValid; Route::get('/profile', function () { // ... - })->middleware(Authenticate::class); + })->middleware(EnsureTokenIsValid::class); You may assign multiple middleware to the route by passing an array of middleware names to the `middleware` method: @@ -157,22 +158,6 @@ You may assign multiple middleware to the route by passing an array of middlewar // ... })->middleware([First::class, Second::class]); -For convenience, you may assign aliases to middleware in your application's `bootstrap/app.php` file. This allows you to define a short alias for the middleware, which can be especially useful for middleware with long class names: - - use App\Http\Middleware\EnsureUserIsSubscribed; - - ->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'subscribed' => EnsureUserIsSubscribed::class - ]); - }) - -Once the middleware alias has been defined in your application's `bootstrap/app.php` file, you may use the alias when assigning middleware to routes: - - Route::get('/profile', function () { - // ... - })->middleware('subscribed'); - #### Excluding Middleware @@ -245,11 +230,9 @@ Laravel includes predefined `web` and `api` middleware groups that contain commo | `Illuminate\View\Middleware\ShareErrorsFromSession` | `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` | `Illuminate\Routing\Middleware\SubstituteBindings` -| `Illuminate\Session\Middleware\AuthenticateSession` | The `api` Middleware Group |-------------- -| `Illuminate\Routing\Middleware\ThrottleRequests:api` | `Illuminate\Routing\Middleware\SubstituteBindings` If you would like to append or prepend middleware to these groups, you may use the `web` and `api` methods within your application's `bootstrap/app.php` file. The `web` and `api` methods are convenient alternatives to the `appendToGroup` method: @@ -262,11 +245,26 @@ If you would like to append or prepend middleware to these groups, you may use t EnsureUserIsSubscribed::class, ]); - $middleware->api(append: [ + $middleware->api(prepend: [ EnsureTokenIsValid::class, ]); }) +You may even replace one of Laravel's default middleware group entries with a custom middleware of your own: + + use App\Http\Middleware\StartCustomSession; + use Illuminate\Session\Middleware\StartSession; + + $middleware->web(replace: [ + StartSession::class => StartCustomSession::class, + ]); + +Or, you may remove a middleware entirely: + + $middleware->web(remove: [ + StartSession::class, + ]); + #### Manually Managing Laravel's Default Middleware Groups @@ -293,6 +291,42 @@ If you would like to manually manage all of the middleware within Laravel's defa > [!NOTE] > By default, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `bootstrap/app.php` file. + +### Middleware Aliases + +You may assign aliases to middleware in your application's `bootstrap/app.php` file. Middleware aliases allows you to define a short alias for a given middleware class, which can be especially useful for middleware with long class names: + + use App\Http\Middleware\EnsureUserIsSubscribed; + + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'subscribed' => EnsureUserIsSubscribed::class + ]); + }) + +Once the middleware alias has been defined in your application's `bootstrap/app.php` file, you may use the alias when assigning the middleware to routes: + + Route::get('/profile', function () { + // ... + })->middleware('subscribed'); + +For convenience, some of Laravel's built-in middleware are aliased by default. For example, the `auth` middleware is an alias for the `Illuminate\Auth\Middleware\Authenticate` middleware. Below is a list of the default middleware aliases: + +| Alias | Middleware +|-------|------------ +`auth` | `Illuminate\Auth\Middleware\Authenticate` +`auth.basic` | `Illuminate\Auth\Middleware\AuthenticateWithBasicAuth` +`auth.session` | `Illuminate\Session\Middleware\AuthenticateSession` +`cache.headers` | `Illuminate\Http\Middleware\SetCacheHeaders` +`can` | `Illuminate\Auth\Middleware\Authorize` +`guest` | `Illuminate\Auth\Middleware\RedirectIfAuthenticated` +`password.confirm` | `Illuminate\Auth\Middleware\RequirePassword` +`precognitive` | `Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests` +`signed` | `Illuminate\Routing\Middleware\ValidateSignature` +`subscribed` | `\Spark\Http\Middleware\VerifyBillableIsSubscribed` +`throttle` | `Illuminate\Routing\Middleware\ThrottleRequests` or `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` +`verified` | `Illuminate\Auth\Middleware\EnsureEmailIsVerified` + ### Sorting Middleware @@ -306,11 +340,11 @@ Rarely, you may need your middleware to execute in a specific order but not have \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, - \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, + \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, - \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Auth\Middleware\Authorize::class, ]); }) diff --git a/verification.md b/verification.md index 8807022c920..d8c2cca2c8d 100644 --- a/verification.md +++ b/verification.md @@ -108,7 +108,7 @@ Sometimes a user may misplace or accidentally delete the email address verificat ### Protecting Routes -[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware alias, which is an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` class. Since this alias is already automatically registered by Laravel, all you need to do is attach the `verified` middleware to a route definition. Typically, this middleware is paired with the `auth` middleware: +[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel includes a `verified` [middleware alias](/docs/{{version}}/middleware#middleware-alias), which is an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` middleware class. Since this alias is already automatically registered by Laravel, all you need to do is attach the `verified` middleware to a route definition. Typically, this middleware is paired with the `auth` middleware: Route::get('/profile', function () { // Only verified users may access this route... From 7a21c9cbe2164e560c9861e972d16bf6d306d938 Mon Sep 17 00:00:00 2001 From: Vilius <79197388+viliusvsx@users.noreply.github.com> Date: Wed, 6 Mar 2024 17:59:01 +0200 Subject: [PATCH 1503/2609] Update releases.md, fix laravel 11 release date (#9458) This PR adds Laravel 11 release date, March 12th, 2024 --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 6caff873b4c..73d13d96209 100644 --- a/releases.md +++ b/releases.md @@ -29,7 +29,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 8 | 7.3 - 8.1 | September 8th, 2020 | July 26th, 2022 | January 24th, 2023 | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | -| 11 | 8.2 - 8.3 | Q1 2024 | August 5th, 2025 | February 3rd, 2026 | +| 11 | 8.2 - 8.3 | March 12th, 2024 | August 5th, 2025 | February 3rd, 2026 |
    From 537f603d0769b50649c565d4b0fd80bbbd2a4caa Mon Sep 17 00:00:00 2001 From: Achmed Islamic Hernawan Date: Thu, 7 Mar 2024 00:19:24 +0800 Subject: [PATCH 1504/2609] update Bootstrap link in collections docs (#9456) --- collections.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/collections.md b/collections.md index bc1a9782f17..9b8fb4f28de 100644 --- a/collections.md +++ b/collections.md @@ -31,7 +31,7 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle $collection = collect([1, 2, 3]); -> [!NOTE] +> [!NOTE] > The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. @@ -300,7 +300,7 @@ The `chunk` method breaks the collection into multiple, smaller collections of a // [[1, 2, 3, 4], [5, 6, 7]] -This method is especially useful in [views](/docs/{{version}}/views) when working with a grid system such as [Bootstrap](https://getbootstrap.com/docs/4.1/layout/grid/). For example, imagine you have a collection of [Eloquent](/docs/{{version}}/eloquent) models you want to display in a grid: +This method is especially useful in [views](/docs/{{version}}/views) when working with a grid system such as [Bootstrap](https://getbootstrap.com/docs/5.3/layout/grid/). For example, imagine you have a collection of [Eloquent](/docs/{{version}}/eloquent) models you want to display in a grid: ```blade @foreach ($products->chunk(3) as $chunk) @@ -375,7 +375,7 @@ The `collect` method is primarily useful for converting [lazy collections](#lazy // [1, 2, 3] -> [!NOTE] +> [!NOTE] > The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. @@ -468,7 +468,7 @@ The `containsOneItem` method determines whether the collection contains a single This method has the same signature as the [`contains`](#method-contains) method; however, all values are compared using "strict" comparisons. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). @@ -579,7 +579,7 @@ The `diff` method compares the collection against another collection or a plain // [1, 3, 5] -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). @@ -799,7 +799,7 @@ Primitive types such as `string`, `int`, `float`, `bool`, and `array` may also b return $collection->ensure('int'); -> [!WARNING] +> [!WARNING] > The `ensure` method does not guarantee that elements of different types will not be added to the collection at a later time. @@ -838,7 +838,7 @@ The `except` method returns all items in the collection except for those with th For the inverse of `except`, see the [only](#method-only) method. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). @@ -1021,7 +1021,7 @@ The `forget` method removes an item from the collection by its key: // ['framework' => 'laravel'] -> [!WARNING] +> [!WARNING] > Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. @@ -1224,7 +1224,7 @@ The `intersect` method removes any values from the original collection that are // [0 => 'Desk', 2 => 'Chair'] -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). @@ -1413,7 +1413,7 @@ The `map` method iterates through the collection and passes each value to the gi // [2, 4, 6, 8, 10] -> [!WARNING] +> [!WARNING] > Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. @@ -1670,7 +1670,7 @@ The `only` method returns the items in the collection with the specified keys: For the inverse of `only`, see the [except](#method-except) method. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). @@ -2025,7 +2025,7 @@ The `reduce` method also passes array keys in associative collections to the giv }); // 4264 - + #### `reduceSpread()` {.collection-method} @@ -2242,7 +2242,7 @@ You may also pass a simple value to the `skipUntil` method to skip all items unt // [3, 4] -> [!WARNING] +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. @@ -2260,7 +2260,7 @@ The `skipWhile` method skips over items from the collection while the given call // [4] -> [!WARNING] +> [!WARNING] > If the callback never returns `false`, the `skipWhile` method will return an empty collection. @@ -2369,7 +2369,7 @@ The `sort` method sorts the collection. The sorted collection keeps the original If your sorting needs are more advanced, you may pass a callback to `sort` with your own algorithm. Refer to the PHP documentation on [`uasort`](https://secure.php.net/manual/en/function.uasort.php#refsect1-function.uasort-parameters), which is what the collection's `sort` method calls utilizes internally. -> [!NOTE] +> [!NOTE] > If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. @@ -2713,7 +2713,7 @@ You may also pass a simple value to the `takeUntil` method to get the items unti // [1, 2] -> [!WARNING] +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. @@ -2731,7 +2731,7 @@ The `takeWhile` method returns items in the collection until the given callback // [1, 2] -> [!WARNING] +> [!WARNING] > If the callback never returns `false`, the `takeWhile` method will return all items in the collection. @@ -2776,7 +2776,7 @@ The `toArray` method converts the collection into a plain PHP `array`. If the co ] */ -> [!WARNING] +> [!WARNING] > `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw array underlying the collection, use the [`all`](#method-all) method instead. @@ -2805,7 +2805,7 @@ The `transform` method iterates over the collection and calls the given callback // [2, 4, 6, 8, 10] -> [!WARNING] +> [!WARNING] > Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. @@ -2909,7 +2909,7 @@ Finally, you may also pass your own closure to the `unique` method to specify wh The `unique` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`uniqueStrict`](#method-uniquestrict) method to filter using "strict" comparisons. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). @@ -3418,7 +3418,7 @@ Likewise, we can use the `sum` higher order message to gather the total number o ### Introduction -> [!WARNING] +> [!WARNING] > Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). To supplement the already powerful `Collection` class, the `LazyCollection` class leverages PHP's [generators](https://www.php.net/manual/en/language.generators.overview.php) to allow you to work with very large datasets while keeping memory usage low. @@ -3609,7 +3609,7 @@ Almost all methods available on the `Collection` class are also available on the
    -> [!WARNING] +> [!WARNING] > Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. From 15293cc3218d1fe1aa97ccd90fabd8f429b2a24b Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 6 Mar 2024 16:30:58 +0000 Subject: [PATCH 1505/2609] Adds collision upgrade (#9460) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 3cb398201ca..17b0300af03 100644 --- a/upgrade.md +++ b/upgrade.md @@ -67,6 +67,7 @@ You should update the following dependencies in your application's `composer.jso
    - `laravel/framework` to `^11.0` +- `nunomaduro/collision` to `^8.1` - `laravel/cashier-stripe` to `^15.0` (If installed) - `laravel/passport` to `^12.0` (If installed) - `laravel/sanctum` to `^4.0` (If installed) From d8352cb5e592f101ee1357acf5277ad8169bb960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 6 Mar 2024 17:35:19 +0100 Subject: [PATCH 1506/2609] [10.x] Document how setup HTTPS with FrankenPHP and Sail (#9455) * [10.x] Document how setup HTTPS with FrankenPHP and Sail * Update octane.md * formatting --------- Co-authored-by: Taylor Otwell --- octane.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/octane.md b/octane.md index 79e5f64c3b7..be0154e289e 100644 --- a/octane.md +++ b/octane.md @@ -82,8 +82,28 @@ services: laravel.test: environment: SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port=80" # [tl! add] + XDG_CONFIG_HOME: /var/www/html/config # [tl! add] + XDG_DATA_HOME: /var/www/html/data # [tl! add] ``` +To enable HTTPS, HTTP/2, and HTTP/3, apply these modifications instead: + +```yaml +services: + laravel.test: + ports: + - '${APP_PORT:-80}:80' + - '${VITE_PORT:-5173}:${VITE_PORT:-5173}' + - '443:443' # [tl! add] + - '443:443/udp' # [tl! add] + environment: + SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --host=localhost --port=443 --admin-port=2019 --https" # [tl! add] + XDG_CONFIG_HOME: /var/www/html/config # [tl! add] + XDG_DATA_HOME: /var/www/html/data # [tl! add] +``` + +Typically, you should access your FrankenPHP Sail application via `https://localhost`, as using `https://127.0.0.1` requires additional configuration and is [discouraged](https://frankenphp.dev/docs/known-issues/#using-https127001-with-docker). + #### FrankenPHP via Docker From e2a343f80a9c31ae3294e03799a68ad09bcfe1bf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Mar 2024 11:16:44 -0600 Subject: [PATCH 1507/2609] formatting --- queries.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/queries.md b/queries.md index bab2ed2269d..4e4aad12b62 100644 --- a/queries.md +++ b/queries.md @@ -13,6 +13,7 @@ - [Where Clauses](#where-clauses) - [Or Where Clauses](#or-where-clauses) - [Where Not Clauses](#where-not-clauses) + - [Where Any / All Clauses](#where-any-all-clauses) - [JSON Where Clauses](#json-where-clauses) - [Additional Where Clauses](#additional-where-clauses) - [Logical Grouping](#logical-grouping) @@ -501,6 +502,51 @@ The `whereNot` and `orWhereNot` methods may be used to negate a given group of q }) ->get(); + +### Where Any / All Clauses + +Sometimes you may need to apply the same query constraints to multiple columns. For example, you may want to retrieve all records where any columns in a given list are `LIKE` a given value. You may accomplish this using the `whereAny` method: + + $users = DB::table('users') + ->where('active', true) + ->whereAny([ + 'name', + 'email', + 'phone', + ], 'LIKE', 'Example%'); + +The query above will result in the following SQL: + +```sql +SELECT * +FROM users +WHERE active = true AND ( + name LIKE 'Example%' OR + email LIKE 'Example%' OR + phone LIKE 'Example%' +) +``` + +Similarly, the `whereAll` method may be used to retrieve records where all of the given columns match a given constraint: + + $posts = DB::table('posts') + ->where('published', true) + ->whereAll([ + 'title', + 'content', + ], 'LIKE', '%Laravel%'); + +The query above will result in the following SQL: + +```sql +SELECT * +FROM posts +WHERE published = true AND ( + title LIKE '%Laravel%' AND + content LIKE '%Laravel%' +) +``` + ### JSON Where Clauses From ce8eb815edf2b9ee3529c9ee1f6a5a2ca9600fbf Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 7 Mar 2024 04:19:23 +1100 Subject: [PATCH 1508/2609] [10.x] Pennant purge --except docs (#9448) * Pennant purge --except docs * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 56038e1c11c..7a9d6ec2b44 100644 --- a/pennant.md +++ b/pennant.md @@ -796,7 +796,7 @@ If you would like to purge _all_ features from storage, you may invoke the `purg Feature::purge(); ``` -As it can be useful to purge features as part of your application's deployment pipeline, Pennant includes a `pennant:purge` Artisan command: +As it can be useful to purge features as part of your application's deployment pipeline, Pennant includes a `pennant:purge` Artisan command which will purge the provided features from storage: ```sh php artisan pennant:purge new-api @@ -804,6 +804,18 @@ php artisan pennant:purge new-api php artisan pennant:purge new-api purchase-button ``` +It is also possible to purge all features _except_ those in a given feature list. For example, imagine you wanted to purge all features but keep the values for the "new-api" and "purchase-button" features in storage. To accomplish this, you can pass those feature names to the `--except` option: + +```sh +php artisan pennant:purge --except=new-api --except=purchase-button +``` + +For convenience, the `pennant:purge` command also supports an `--except-registered` flag. This flag indicates that all features except those explicitly registered in a service provider should be purged: + +```sh +php artisan pennant:purge --except-registered +``` + ## Testing From 0261344e430a8c8c061848c1b8b262e011b9cd0e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Mar 2024 11:26:48 -0600 Subject: [PATCH 1509/2609] wip --- queries.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/queries.md b/queries.md index 4e4aad12b62..67817f9e719 100644 --- a/queries.md +++ b/queries.md @@ -513,7 +513,8 @@ Sometimes you may need to apply the same query constraints to multiple columns. 'name', 'email', 'phone', - ], 'LIKE', 'Example%'); + ], 'LIKE', 'Example%') + ->get(); The query above will result in the following SQL: @@ -534,7 +535,8 @@ Similarly, the `whereAll` method may be used to retrieve records where all of th ->whereAll([ 'title', 'content', - ], 'LIKE', '%Laravel%'); + ], 'LIKE', '%Laravel%') + ->get(); The query above will result in the following SQL: From 05d8e5c94d80c36904462fab4518f3fa7a4e624c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 6 Mar 2024 19:15:33 +0000 Subject: [PATCH 1510/2609] [11.x] `once` upgrade (#9461) * adds missing entries * Adds `Spatie Once Package` * formatting * Update upgrade.md * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/upgrade.md b/upgrade.md index 17b0300af03..d1532eda79d 100644 --- a/upgrade.md +++ b/upgrade.md @@ -36,6 +36,7 @@ - [Doctrine DBAL Removal](#doctrine-dbal-removal) - [Eloquent Model `casts` Method](#eloquent-model-casts-method) - [Spatial Types](#spatial-types) +- [Spatie Once Package](#spatie-once-package) - [The `Enumerable` Contract](#the-enumerable-contract) - [The `UserProvider` Contract](#the-user-provider-contract) - [The `Authenticatable` Contract](#the-authenticatable-contract) @@ -571,3 +572,10 @@ Telescope 5.0 no longer automatically loads migrations from its own migrations d ```shell php artisan vendor:publish --tag=telescope-migrations ``` + + +### Spatie Once Package + +**Likelihood Of Impact: Medium** + +Laravel 11 now provides its own [`once` function](/docs/{{version}}/helpers#method-once) to ensure that a given closure is only executed once. Therefore, if your application has a dependency on the `spatie/once` package, you should remove it from your application's `composer.json` file to avoid conflicts. From a6e4ad2e009dd6e77a21bcf5467dea26e34b5d2d Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 7 Mar 2024 16:02:11 +0000 Subject: [PATCH 1511/2609] Update broadcasting.md (#9465) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index a34884e546d..6f4c5564344 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -68,7 +68,7 @@ All of your application's event broadcasting configuration is stored in the `con #### Installation -by default, broadcasting is not enabled in new Laravel applications. You may enable broadcasting using the `install:broadcasting` Artisan command: +By default, broadcasting is not enabled in new Laravel applications. You may enable broadcasting using the `install:broadcasting` Artisan command: ```shell php artisan install:broadcasting From 603a949ab97b72b1928cfa9af54f971785128458 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Thu, 7 Mar 2024 16:39:35 +0000 Subject: [PATCH 1512/2609] update server variables (#9464) --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index 5ee5dafc002..8faa3be9946 100644 --- a/reverb.md +++ b/reverb.md @@ -131,7 +131,7 @@ If you need to specify a custom host or port, you may do so via the `--host` and php artisan reverb:start --host=127.0.0.1 --port=9000 ``` -Alternatively, you may define `REVERB_HOST` and `REVERB_PORT` environment variables in your application's `.env` configuration file. +Alternatively, you may define `REVERB_SERVER_HOST` and `REVERB_SERVER_PORT` environment variables in your application's `.env` configuration file. ### Debugging From aef0974daffe6d86dd7b9f7148f41aefeb51ad53 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 8 Mar 2024 03:49:05 +1100 Subject: [PATCH 1513/2609] pulse:restart (#9463) --- pulse.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pulse.md b/pulse.md index 6554742fcc6..96385d92483 100644 --- a/pulse.md +++ b/pulse.md @@ -249,6 +249,15 @@ php artisan pulse:check > [!NOTE] > To keep the `pulse:check` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the command does not stop running. +As the `pulse:check` command is a long-lived process, it will not see changes to your codebase without being restarted. You should gracefully restart the command by calling the `pulse:restart` command during your application's deployment process: + +```sh +php artisan pulse:restart +``` + +> [!NOTE] +> Pulse 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. + ### Recorders @@ -425,6 +434,15 @@ php artisan pulse:work > [!NOTE] > To keep the `pulse:work` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the Pulse worker does not stop running. +As the `pulse:work` command is a long-lived process, it will not see changes to your codebase without being restarted. You should gracefully restart the command by calling the `pulse:restart` command during your application's deployment process: + +```sh +php artisan pulse:restart +``` + +> [!NOTE] +> Pulse 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. + ### Sampling From e12fed1b0d0dd7079fdc512443ad55755cc99e20 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 7 Mar 2024 20:55:22 +0330 Subject: [PATCH 1514/2609] [11.x] Rewrite Laravel Passport installation (#9457) * rewrite installing Laravel Passport * formatting --------- Co-authored-by: Taylor Otwell --- passport.md | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/passport.md b/passport.md index ff5a9270e8d..315d906bbbf 100644 --- a/passport.md +++ b/passport.md @@ -61,24 +61,17 @@ However, if you are attempting to authenticate a single-page application, mobile ## Installation -To get started, install Passport via the Composer package manager: +You may install Laravel Passport via the `install:api` Artisan command: ```shell -composer require laravel/passport +php artisan install:api --passport ``` -Next, you should execute the `passport:install` 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: +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. -```shell -php artisan passport:install -``` - -Additionally, the `passport:install` command will create the encryption keys required to generate secure access tokens, as well as the "personal access" and "password grant" clients, which will be used to generate access tokens. - -> [!NOTE] -> If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). +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 `passport:install` 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. If your model is already using the `Laravel\Sanctum\HasApiTokens` trait, you may remove that trait: +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: -#### Client UUIDs - -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: - -```shell -php artisan passport:install --uuids -``` - ### Deploying Passport From 8a5910eadd7105013dc32b805e36d0f3d455cc1c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Mar 2024 15:05:46 -0600 Subject: [PATCH 1515/2609] wip --- strings.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/strings.md b/strings.md index a940880fecc..1d9422ecd86 100644 --- a/strings.md +++ b/strings.md @@ -837,6 +837,16 @@ The `Str::random` method generates a random string of the specified length. This $random = Str::random(40); +During testing, it may be useful to "fake" the value that is returned by the `Str::random` method. To accomplish this, you may use the `createRandomStringsUsing` method: + + Str::createRandomStringsUsing(function () { + return 'fake-random-string'; + }); + +To instruct the `random` method to return to generating random strings normally, you may invoke the `createRandomStringsNormally` method: + + Str::createRandomStringsNormally(); + #### `Str::remove()` {.collection-method} @@ -1224,6 +1234,18 @@ use Illuminate\Support\Str; $date = Carbon::createFromId((string) Str::ulid()); ``` +During testing, it may be useful to "fake" the value that is returned by the `Str::ulid` method. To accomplish this, you may use the `createUlidsUsing` method: + + use Symfony\Component\Uid\Ulid; + + Str::createUlidsUsing(function () { + return new Ulid('01HRDBNHHCKNW2AK4Z29SN82T9'); + }); + +To instruct the `ulid` method to return to generating ULIDs normally, you may invoke the `createUlidsNormally` method: + + Str::createUlidsNormally(); + #### `Str::unwrap()` {.collection-method} @@ -1248,6 +1270,18 @@ The `Str::uuid` method generates a UUID (version 4): return (string) Str::uuid(); +During testing, it may be useful to "fake" the value that is returned by the `Str::uuid` method. To accomplish this, you may use the `createUuidsUsing` method: + + use Ramsey\Uuid\Uuid; + + Str::createUuidsUsing(function () { + return Uuid::fromString('eadbfeac-5258-45c2-bab7-ccb9b5ef74f9'); + }); + +To instruct the `uuid` method to return to generating UUIDs normally, you may invoke the `createUuidsNormally` method: + + Str::createUuidsNormally(); + #### `Str::wordCount()` {.collection-method} From 27a810f9dd039292df6bf3927d15274243f5cccf Mon Sep 17 00:00:00 2001 From: Yoeri Boven Date: Fri, 8 Mar 2024 03:31:30 +0100 Subject: [PATCH 1516/2609] Update upgrade.md (#9466) --- upgrade.md | 1 - 1 file changed, 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index d1532eda79d..7325b555010 100644 --- a/upgrade.md +++ b/upgrade.md @@ -8,7 +8,6 @@
    - [Updating Dependencies](#updating-dependencies) -- [Updating Minimum Stability](#updating-minimum-stability) - [Application Structure](#application-structure) - [Floating-Point Types](#floating-point-types) - [Modifying Columns](#modifying-columns) From 864f938734e1c055c8549775e72dfbd1571d72a8 Mon Sep 17 00:00:00 2001 From: Jesper Noordsij <45041769+jnoordsij@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:53:36 +0100 Subject: [PATCH 1517/2609] Fix typo in Laravel 11 release notes (#9468) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index cb5e5769f9c..3bab7be79f3 100644 --- a/releases.md +++ b/releases.md @@ -150,7 +150,7 @@ Like routing and middleware, exception handling can now be customized from your #### Base `Controller` Class -The base controller included in new Laravel applications has been simplified. It no longer extends Laravel's internal `Controller` class, and the `AuthorizesRequests` and `ValidatesRequests` traits have been removed, as they may included on your application's individual controllers if desired: +The base controller included in new Laravel applications has been simplified. It no longer extends Laravel's internal `Controller` class, and the `AuthorizesRequests` and `ValidatesRequests` traits have been removed, as they may be included in your application's individual controllers if desired: Date: Fri, 8 Mar 2024 18:24:18 +0330 Subject: [PATCH 1518/2609] Update dusk.md (#9469) --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index f450babfa44..e1884e713b3 100644 --- a/dusk.md +++ b/dusk.md @@ -255,7 +255,7 @@ If you had test failures the last time you ran the `dusk` command, you may save php artisan dusk:fails ``` -The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://phpunit.readthedocs.io/en/10.1/annotations.html#group): +The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, such as allowing you to only run the tests for a given [group](https://docs.phpunit.de/en/10.5/annotations.html#group): ```shell php artisan dusk --group=foo From fdeb961799b9969361bc41525cfacfa6f685b533 Mon Sep 17 00:00:00 2001 From: Stephen Rees-Carter Date: Sat, 9 Mar 2024 02:20:55 +1000 Subject: [PATCH 1519/2609] Add XSS warnings to the markdown() functions (#9467) * Add XSS warnings to the markdown() functions * formatting --------- Co-authored-by: Taylor Otwell --- strings.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/strings.md b/strings.md index 1d9422ecd86..be97a8c241e 100644 --- a/strings.md +++ b/strings.md @@ -495,6 +495,19 @@ The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline H // Laravel +#### Markdown Security + +By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: + + use Illuminate\Support\Str; + + Str::inlineMarkdown('Inject: ', [ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, + ]); + + // Inject: alert("Hello XSS!"); + #### `Str::is()` {.collection-method} @@ -673,6 +686,19 @@ The `Str::markdown` method converts GitHub flavored Markdown into HTML using [Co //

    Taylor Otwell

    +#### Markdown Security + +By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: + + use Illuminate\Support\Str; + + Str::markdown('Inject: ', [ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, + ]); + + //

    Inject: alert("Hello XSS!");

    + #### `Str::mask()` {.collection-method} @@ -1686,6 +1712,19 @@ The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML u // Laravel +#### Markdown Security + +By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: + + use Illuminate\Support\Str; + + Str::of('Inject: ')->inlineMarkdown([ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, + ]); + + // Inject: alert("Hello XSS!"); + #### `is` {.collection-method} @@ -1911,6 +1950,19 @@ The `markdown` method converts GitHub flavored Markdown into HTML: //

    Taylor Otwell

    +#### Markdown Security + +By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: + + use Illuminate\Support\Str; + + Str::of('Inject: ')->markdown([ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, + ]); + + //

    Inject: alert("Hello XSS!");

    + #### `mask` {.collection-method} From 69674b439071bbd3e972f7b868e876753a540f9b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Mar 2024 11:27:14 -0600 Subject: [PATCH 1520/2609] wip --- reverb.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/reverb.md b/reverb.md index 8faa3be9946..b1bffec4391 100644 --- a/reverb.md +++ b/reverb.md @@ -12,6 +12,7 @@ - [Restarting](#restarting) - [Running Reverb in Production](#production) - [Open Files](#open-files) + - [Event Loop](#event-loop) - [Web Server](#web-server) - [Ports](#ports) - [Process Management](#process-management) @@ -94,15 +95,15 @@ For example, you may wish to maintain a single Laravel application which, via Re ### SSL -In most cases, secure WebSocket connections are likely to be handled by the upstream web server (Nginx, etc.) before the request is proxied to your Reverb server. +In most cases, secure WebSocket connections are handled by the upstream web server (Nginx, etc.) before the request is proxied to your Reverb server. -However, it can sometimes be useful, such as during local development, for the Reverb server to handle secure connections directly. If you are using [Laravel Herd](https://herd.laravel.com) and have secured the site or you are using [Laravel Valet](/docs/{{version}}/valet) and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may use the Herd / Valet certificate generated for your site to secure your Reverb connections. To do so, set the `REVERB_HOST` environment variable to your site's hostname or explicitly pass the hostname option when starting the Reverb server: +However, it can sometimes be useful, such as during local development, for the Reverb server to handle secure connections directly. If you are using [Laravel Herd's](https://herd.laravel.com) secure site feature or you are using [Laravel Valet](/docs/{{version}}/valet) and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may use the Herd / Valet certificate generated for your site to secure your Reverb connections. To do so, set the `REVERB_HOST` environment variable to your site's hostname or explicitly pass the hostname option when starting the Reverb server: ```sh php artisan reverb:start --host="0.0.0.0" --port=8080 --hostname="laravel.test" ``` -Since Herd and Valet domains resolve to localhost, running the commmand above will result in your Reverb server being accessible via the secure WebSocket protocol (wss) at `wss://laravel.test:8080`. +Since Herd and Valet domains resolve to `localhost`, running the commmand above will result in your Reverb server being accessible via the secure WebSocket protocol (`wss`) at `wss://laravel.test:8080`. You may also manually choose a certificate by defining `tls` options in your application's `config/reverb.php` configuration file. Within the array of `tls` options, you may provide any of the options supported by [PHP's SSL context options](https://www.php.net/manual/en/context.ssl.php): @@ -156,7 +157,7 @@ php artisan reverb:restart ## Running Reverb in Production -Due to the long-running nature of WebSocket servers, you may need to make some optimizations to your server / hosting environment to ensure your Reverb server can effectively handle the optimal number of connections for the resources available on your server. +Due to the long-running nature of WebSocket servers, you may need to make some optimizations to your server and hosting environment to ensure your Reverb server can effectively handle the optimal number of connections for the resources available on your server. > **Note** > If your site is managed by [Laravel Forge](https://forge.laravel.com), you may automatically optimize your server for Reverb directly from the "Application" panel. By enabling the Reverb integration, Forge will ensure your server is production-ready, including installing any required extensions and increasing the allowed number of connections. @@ -184,7 +185,7 @@ forge hard nofile 10000 ``` -#### Event Loop +### Event Loop Under the hood, Reverb uses a ReactPHP event loop to manage WebSocket connections on the server. By default, this event loop is powered by `stream_select`, which doesn't require any additional extensions. However, `stream_select` is typically limited to 1,024 open files. As such, if you plan to handle more than 1,000 concurrent connections, you will need to use an alternative event loop not bound to the same restrictions. From 2b729aed5978b14990cb61fce732b5392ed7d80d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Mar 2024 13:04:39 -0600 Subject: [PATCH 1521/2609] wip --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 6f4c5564344..aa71d14f4e8 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -161,7 +161,7 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst ### Reverb -[Laravel Echo](https://github.com/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 the Reverb broadcaster will leverage the Pusher SDK: +[Laravel Echo](https://github.com/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 Reverb utilizes the Pusher protocol for WebSocket subscriptions, channels, and messages: ```shell npm install --save-dev laravel-echo pusher-js From 3996a431add8b8cd748d5de9b37f3e92c59ef160 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Mar 2024 13:15:38 -0600 Subject: [PATCH 1522/2609] wip --- broadcasting.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/broadcasting.md b/broadcasting.md index aa71d14f4e8..0bf47529d28 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -220,6 +220,25 @@ window.Echo = new Echo({ }); ``` +Next, you should define the appropriate values for the Pusher environment variables in your application's `.env` file. If these variables do not already exist in your `.env` file, you should add them: + +```ini +PUSHER_APP_ID="your-app-id" +PUSHER_APP_KEY="your-app-key" +PUSHER_APP_SECRET="your-app-secret" +PUSHER_HOST= +PUSHER_PORT=443 +PUSHER_SCHEME="https" +PUSHER_APP_CLUSTER="mt1" + +VITE_APP_NAME="${APP_NAME}" +VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +VITE_PUSHER_HOST="${PUSHER_HOST}" +VITE_PUSHER_PORT="${PUSHER_PORT}" +VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" +VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" +``` + Once you have adjusted the Echo configuration according to your application's needs, you may compile your application's assets: ```shell From c771be8623d7ee61ff503f44aded77dd0abcac83 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Mar 2024 13:16:22 -0600 Subject: [PATCH 1523/2609] wip --- broadcasting.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 0bf47529d28..ab32e17babb 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -110,13 +110,13 @@ 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 -PUSHER_APP_ID=your-pusher-app-id -PUSHER_APP_KEY=your-pusher-key -PUSHER_APP_SECRET=your-pusher-secret +PUSHER_APP_ID="your-pusher-app-id" +PUSHER_APP_KEY="your-pusher-key" +PUSHER_APP_SECRET="your-pusher-secret" PUSHER_HOST= PUSHER_PORT=443 -PUSHER_SCHEME=https -PUSHER_APP_CLUSTER=mt1 +PUSHER_SCHEME="https" +PUSHER_APP_CLUSTER="mt1" ``` The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Channels, such as the cluster. @@ -223,9 +223,9 @@ window.Echo = new Echo({ Next, you should define the appropriate values for the Pusher environment variables in your application's `.env` file. If these variables do not already exist in your `.env` file, you should add them: ```ini -PUSHER_APP_ID="your-app-id" -PUSHER_APP_KEY="your-app-key" -PUSHER_APP_SECRET="your-app-secret" +PUSHER_APP_ID="your-pusher-app-id" +PUSHER_APP_KEY="your-pusher-key" +PUSHER_APP_SECRET="your-pusher-secret" PUSHER_HOST= PUSHER_PORT=443 PUSHER_SCHEME="https" From 0f5c9d4738699cfea91221ff64adcb320492cfdf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Mar 2024 13:40:45 -0600 Subject: [PATCH 1524/2609] wip --- routing.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/routing.md b/routing.md index 21dc5236d32..146e03a6637 100644 --- a/routing.md +++ b/routing.md @@ -1,9 +1,11 @@ # Routing - [Basic Routing](#basic-routing) + - [The Default Route Files](#the-default-route-files) - [Redirect Routes](#redirect-routes) - [View Routes](#view-routes) - - [The Route List](#the-route-list) + - [Listing Your Routes](#listing-your-routes) + - [Routing Customization](#routing-customization) - [Route Parameters](#route-parameters) - [Required Parameters](#required-parameters) - [Optional Parameters](#parameters-optional-parameters) @@ -40,9 +42,9 @@ The most basic Laravel routes accept a URI and a closure, providing a very simpl }); -#### The Default Route Files +### 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 Laravel using the configuration specified in your application's `bootstrap/app.php` file. 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. +All Laravel routes are defined in your route files, which are located in the `routes` directory. These files are automatically loaded by Laravel using the configuration specified in your application's `bootstrap/app.php` file. The `routes/web.php` file defines routes that are for your web interface. These routes are assigned the `web` [middleware group](/docs/{{version}}/middleware#laravels-default-middleware-groups), which provides features like session state and CSRF protection. 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: @@ -65,7 +67,7 @@ The `install:api` command installs [Laravel Sanctum](/docs/{{version}}/sanctum), return $request->user(); })->middleware(Authenticate::using('sanctum')); -The routes in `routes/api.php` are stateless and are assigned to the `api` middleware group. Additionally, the `/api` URI prefix is automatically applied to these routes, so you do not need to manually apply it to every route in the file. You may change the prefix by modifying your application's `bootstrap/app.php` file: +The routes in `routes/api.php` are stateless and are assigned to the `api` [middleware group](/docs/{{version}}/middleware#laravels-default-middleware-groups). Additionally, the `/api` URI prefix is automatically applied to these routes, so you do not need to manually apply it to every route in the file. You may change the prefix by modifying your application's `bootstrap/app.php` file: ->withRouting( api: __DIR__.'/../routes/api.php', @@ -149,8 +151,8 @@ If your route only needs to return a [view](/docs/{{version}}/views), you may us > [!WARNING] > When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. - -### The Route List + +### Listing Your Routes The `route:list` Artisan command can easily provide an overview of all of the routes that are defined by your application: @@ -185,6 +187,60 @@ Likewise, you may also instruct Laravel to only show routes that are defined by php artisan route:list --only-vendor ``` + +### Routing Customization + +By default, your application's routes are configured and loaded by the `bootstrap/app.php` file: + +```php +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + )->create(); +``` + +However, sometimes you may want to define an entirely new file to contain a subset of your application's routes. To accomplish this, you may provide a `then` closure to the `withRouting` method. Within this closure, you may register any additional routes that are necessary for your application: + +```php +use Illuminate\Support\Facades\Route; + +->withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + then: function () { + Route::middleware('api') + ->prefix('webhooks') + ->name('webhooks.') + ->group(base_path('routes/webhooks.php')); + }, +) +``` + +Or, you may even take complete control over route registration by providing a `using` closure to the `withRouting` method. When this argument is passed, no HTTP routes will be registered by the framework and you are responsible for manually registering all routes: + +```php +use Illuminate\Support\Facades\Route; + +->withRouting( + commands: __DIR__.'/../routes/console.php', + using: function () { + Route::middleware('api') + ->prefix('api') + ->group(base_path('routes/api.php')); + + Route::middleware('web') + ->group(base_path('routes/web.php')); + }, +) +``` + ## Route Parameters From 62783b8aa4b87b7765ec7c3c6538257460759623 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Mar 2024 19:56:28 +0000 Subject: [PATCH 1525/2609] Documents `fortify:install` Artisan command (#9471) --- fortify.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/fortify.md b/fortify.md index 27dd965df3d..4a0b0bf547c 100644 --- a/fortify.md +++ b/fortify.md @@ -4,7 +4,6 @@ - [What is Fortify?](#what-is-fortify) - [When Should I Use Fortify?](#when-should-i-use-fortify) - [Installation](#installation) - - [The Fortify Service Provider](#the-fortify-service-provider) - [Fortify Features](#fortify-features) - [Disabling Views](#disabling-views) - [Authentication](#authentication) @@ -75,10 +74,10 @@ To get started, install Fortify using the Composer package manager: composer require laravel/fortify ``` -Next, publish Fortify's resources using the `vendor:publish` command: +Next, publish Fortify's resources using the `fortify:install` Artisan command: ```shell -php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider" +php artisan fortify:install ``` This command will publish Fortify's actions to your `app/Actions` directory, which will be created if it does not exist. In addition, the `FortifyServiceProvider`, configuration file, and all necessary database migrations will be published. @@ -89,13 +88,6 @@ Next, you should migrate your database: php artisan migrate ``` - -### The Fortify Service Provider - -The `vendor:publish` command discussed above will also publish the `App\Providers\FortifyServiceProvider` class. You should ensure this class is registered within the array of service providers in your application's `bootstrap/providers.php` file. - -The Fortify service provider registers the actions that Fortify published and instructs Fortify to use them when their respective tasks are executed by Fortify. - ### Fortify Features From 8332d20b07cc639995649a335620b044151e6dd1 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Mar 2024 20:01:24 +0000 Subject: [PATCH 1526/2609] [11.x] Adjusts `breeze` installation (#9470) * Rewrites `breeze` * formatting --------- Co-authored-by: Taylor Otwell --- starter-kits.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index b91af96843e..acff95f6f4f 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -32,13 +32,15 @@ If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https:// ### Installation -First, you should [create a new Laravel application](/docs/{{version}}/installation), configure your database, and run your [database migrations](/docs/{{version}}/migrations). Once you have created a new Laravel application, you may install Laravel Breeze using Composer: +First, you should [create a new Laravel application](/docs/{{version}}/installation). If you create your application using the [Laravel installer](/docs/{{version}}/installation#creating-a-laravel-project), you will be prompted to install Laravel Breeze during the installation process. Otherwise, you will need to follow the manual installation instructions below. + +If you have already created a new Laravel application without a starter kit, you may manually install Laravel Breeze using Composer: ```shell 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 Composer has installed the Laravel Breeze package, you should 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. The `breeze:install` command will prompt you for your preferred frontend stack and testing framework: From 6847fb27e426d55e6471db15f97cd6b59cac1940 Mon Sep 17 00:00:00 2001 From: MD Masud Sikdar Date: Sun, 10 Mar 2024 05:50:04 +0600 Subject: [PATCH 1527/2609] [11.x] Fix typo in Laravel 11 release middleware notes (#9472) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 3bab7be79f3..ba8d2c2ed92 100644 --- a/releases.md +++ b/releases.md @@ -105,7 +105,7 @@ php artisan install:broadcasting Previously, new Laravel applications included nine middleware. These middleware performed a variety of tasks such as authenticating requests, trimming input strings, and validating CSRF tokens. -In Laravel 11, these middleware have been moved into the framework itself, so that they do not add bulk to your application's structure. New methods of customizing the behavior of these middleware have been added to the framework and may be invoked from your application's `bootstrap/app.php` file: +In Laravel 11, these middleware have been moved into the framework itself, so that they do not add bulk to your application's structure. New methods for customizing the behavior of these middleware have been added to the framework and may be invoked from your application's `bootstrap/app.php` file: ```php ->withMiddleware(function (Middleware $middleware) { From a5311a99723c410153f2c8c1b8a3eb2644165a12 Mon Sep 17 00:00:00 2001 From: Robert Stefanic Date: Mon, 11 Mar 2024 08:58:13 -0400 Subject: [PATCH 1528/2609] [10.x] Add note about upgrading laravel/ui dependency (#9473) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index cefe59712cf..d15b1be1cc9 100644 --- a/upgrade.md +++ b/upgrade.md @@ -76,6 +76,7 @@ You should update the following dependencies in your application's `composer.jso - `doctrine/dbal` to `^3.0` - `spatie/laravel-ignition` to `^2.0` - `laravel/passport` to `^11.0` ([Upgrade Guide](https://github.com/laravel/passport/blob/11.x/UPGRADE.md)) +- `laravel/ui` to `^4.0`
    From 8213f7691151345b5360af0d7905c4f6b7e989d3 Mon Sep 17 00:00:00 2001 From: Julius Kiekbusch Date: Mon, 11 Mar 2024 14:06:43 +0100 Subject: [PATCH 1529/2609] [11.x] Add Laravel Installer update instructions (#9475) * Add Laravel Installer update instructions * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/upgrade.md b/upgrade.md index 7325b555010..ccac287f89e 100644 --- a/upgrade.md +++ b/upgrade.md @@ -94,6 +94,12 @@ In addition, you should review the upgrade guides for each of these packages to - [Laravel Spark Stripe](#spark-stripe) - [Laravel Telescope](#telescope) +If you have manually installed the Laravel installer, you should update the installer via Composer: + +```bash +composer global require laravel/installer:^5.6 +``` + Finally, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. From 6f7d982efaa532a3b9d24181b66a7020f63aa8af Mon Sep 17 00:00:00 2001 From: Stefano Giraldo Date: Tue, 12 Mar 2024 03:32:51 +0100 Subject: [PATCH 1530/2609] Remove references to config getters (#9479) --- configuration.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/configuration.md b/configuration.md index 1d942531d9f..7245036249c 100644 --- a/configuration.md +++ b/configuration.md @@ -202,14 +202,6 @@ To set configuration values at runtime, you may invoke the `Config` facade's `se config(['app.timezone' => 'America/Chicago']); -To assist with static analysis, the `Config` facade also provides typed configuration retrieval methods. If the retrieved configuration value does not match the expected type, an exception will be thrown: - - Config::string('config-key'); - Config::integer('config-key'); - Config::float('config-key'); - Config::boolean('config-key'); - Config::array('config-key'); - ## Configuration Caching From 0dffac558db28a01a0754f09894b9ad3b0899d89 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 12 Mar 2024 02:38:14 +0000 Subject: [PATCH 1531/2609] [11.x] Adjusts for `install:broadcasting` changes (#9478) * Adjusts for `install: broadcasting` * formatting --------- Co-authored-by: Taylor Otwell --- broadcasting.md | 14 +++++++++----- reverb.md | 12 +++--------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index ab32e17babb..528d553caf9 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -161,13 +161,15 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst ### Reverb -[Laravel Echo](https://github.com/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 Reverb utilizes the Pusher protocol for WebSocket subscriptions, channels, and messages: +[Laravel Echo](https://github.com/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. Echo also leverages the `pusher-js` NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages. + +The `install:broadcasting` Artisan command automatically installs the `laravel-echo` and `pusher-js` packages for you; however, you may also install these packages manually via NPM: ```shell 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. The `install:broadcasting` Artisan command creates a `resources/js/echo.js` file that handles this for you: +Once `laravel-echo` and `pusher-js` are installed, you are ready to create a fresh Echo instance in your application's JavaScript. The `install:broadcasting` Artisan command creates a `resources/js/echo.js` file that handles this for you: ```js import Echo from 'laravel-echo'; @@ -198,7 +200,9 @@ npm run build ### Pusher Channels -[Laravel Echo](https://github.com/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: +[Laravel Echo](https://github.com/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. Echo also leverages the `pusher-js` NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages. + +The `install:broadcasting` Artisan command automatically installs the `laravel-echo` and `pusher-js` packages for you; however, you may also install these packages manually via NPM: ```shell npm install --save-dev laravel-echo pusher-js @@ -274,9 +278,9 @@ window.Echo = new Echo({ > [!NOTE] > The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). -[Laravel Echo](https://github.com/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. +[Laravel Echo](https://github.com/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. Echo also leverages the `pusher-js` NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages. -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: +The `install:broadcasting` Artisan command automatically installs the `laravel-echo` and `pusher-js` packages for you; however, you may also install these packages manually via NPM: ```shell npm install --save-dev laravel-echo pusher-js diff --git a/reverb.md b/reverb.md index b1bffec4391..b684f7327c0 100644 --- a/reverb.md +++ b/reverb.md @@ -29,22 +29,16 @@ > **Warning** > Laravel Reverb requires PHP 8.2+. -You may use the Composer package manager to install Reverb into your Laravel project. Since Reverb is currently in beta, you will need to explicitly install the beta release: +You may install Reverb using the `install:broadcasting` Artisan command: -```sh -composer require laravel/reverb:@beta ``` - -Once the package is installed, you may run Reverb's installation command to publish the configuration, add Reverb's required environment variables, and enable event broadcasting in your application: - -```sh -php artisan reverb:install +php artisan install:broadcasting ``` ## Configuration -The `reverb:install` command will automatically start Reverb using a sensible set of default configuration options. If you would like to make any configuration changes, you may do so by updating Reverb's environment variables or by updating the `config/reverb.php` configuration file. +Behind the scenes, the `install:broadcasting` Artisan command will run the `reverb:install` command, which will install Reverb with a sensible set of default configuration options. If you would like to make any configuration changes, you may do so by updating Reverb's environment variables or by updating the `config/reverb.php` configuration file. ### Application Credentials From a85fca844365fbcf2b1183c4f5f91d3970fccbef Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Mar 2024 21:56:48 -0500 Subject: [PATCH 1532/2609] wip --- errors.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/errors.md b/errors.md index aeb5f47b07a..12f32e80870 100644 --- a/errors.md +++ b/errors.md @@ -206,6 +206,24 @@ You may also use the `renderable` method to override the rendering behavior for }); }) + +#### Rendering Exceptions as JSON + +When rendering an exception, Laravel will automatically determine if the exception should be rendered as an HTML or JSON response based on the `Content-Type` header of the request. If you would like to customize how Laravel determines whether to render HTML or JSON exception responses, you may utilize the `shouldRenderJsonWhen` method: + + use Illuminate\Http\Request; + use Throwable; + + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) { + if ($request->is('admin/*')) { + return true; + } + + return $request->expectsJson(); + }); + }) + ### Reportable and Renderable Exceptions From 659a0efce48980c1eadce965c25e1845377332ad Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Mar 2024 22:33:17 -0500 Subject: [PATCH 1533/2609] update error handling docs --- errors.md | 41 ++++++++++++++++++++++++++++++----------- releases.md | 2 +- urls.md | 12 ++++-------- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/errors.md b/errors.md index 12f32e80870..ae553394d7f 100644 --- a/errors.md +++ b/errors.md @@ -34,22 +34,22 @@ During local development, you should set the `APP_DEBUG` environment variable to In Laravel, exception reporting is used to log exceptions or send them to an external service [Sentry](https://github.com/getsentry/sentry-laravel) or [Flare](https://flareapp.io). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. -If you need to report different types of exceptions in different ways, you may use the `reportable` exception method in your application's `bootstrap/app.php` to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will determine what type of exception the closure reports by examining the type-hint of the closure: +If you need to report different types of exceptions in different ways, you may use the `report` exception method in your application's `bootstrap/app.php` to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will determine what type of exception the closure reports by examining the type-hint of the closure: ->withExceptions(function (Exceptions $exceptions) { - $exceptions->reportable(function (InvalidOrderException $e) { + $exceptions->report(function (InvalidOrderException $e) { // ... }); }) -When you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application. If you wish to stop the propagation of the exception to the default logging stack, you may use the `stop` method when defining your reporting callback or return `false` from the callback: +When you register a custom exception reporting callback using the `report` method, Laravel will still log the exception using the default logging configuration for the application. If you wish to stop the propagation of the exception to the default logging stack, you may use the `stop` method when defining your reporting callback or return `false` from the callback: ->withExceptions(function (Exceptions $exceptions) { - $exceptions->reportable(function (InvalidOrderException $e) { + $exceptions->report(function (InvalidOrderException $e) { // ... })->stop(); - $exceptions->reportable(function (InvalidOrderException $e) { + $exceptions->report(function (InvalidOrderException $e) { return false; }); }) @@ -143,7 +143,7 @@ report($caught); // ignored When messages are written to your application's [logs](/docs/{{version}}/logging), the messages are written at a specified [log level](/docs/{{version}}/logging#log-levels), which indicates the severity or importance of the message being logged. -As noted above, even when you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application; however, since the log level can sometimes influence the channels on which a message is logged, you may wish to configure the log level that certain exceptions are logged at. +As noted above, even when you register a custom exception reporting callback using the `report` method, Laravel will still log the exception using the default logging configuration for the application; however, since the log level can sometimes influence the channels on which a message is logged, you may wish to configure the log level that certain exceptions are logged at. To accomplish this, you may use the `level` exception method in your application's `bootstrap/app.php` file. This method receives the exception type as its first argument and the log level as its second argument: @@ -178,26 +178,26 @@ Internally, Laravel already ignores some types of errors for you, such as except ### Rendering Exceptions -By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by using the `renderable` exception method in your application's `boostrap/app.php` file. +By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by using the `render` exception method in your application's `boostrap/app.php` file. -The closure passed to the `renderable` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will determine what type of exception the closure renders by examining the type-hint of the closure: +The closure passed to the `render` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will determine what type of exception the closure renders by examining the type-hint of the closure: use App\Exceptions\InvalidOrderException; use Illuminate\Http\Request; ->withExceptions(function (Exceptions $exceptions) { - $exceptions->renderable(function (InvalidOrderException $e, Request $request) { + $exceptions->render(function (InvalidOrderException $e, Request $request) { return response()->view('errors.invalid-order', [], 500); }); }) -You may also use the `renderable` method to override the rendering behavior for built-in Laravel or Symfony exceptions such as `NotFoundHttpException`. If the closure given to the `renderable` method does not return a value, Laravel's default exception rendering will be utilized: +You may also use the `render` method to override the rendering behavior for built-in Laravel or Symfony exceptions such as `NotFoundHttpException`. If the closure given to the `render` method does not return a value, Laravel's default exception rendering will be utilized: use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; ->withExceptions(function (Exceptions $exceptions) { - $exceptions->renderable(function (NotFoundHttpException $e, Request $request) { + $exceptions->render(function (NotFoundHttpException $e, Request $request) { if ($request->is('api/*')) { return response()->json([ 'message' => 'Record not found.' @@ -224,6 +224,25 @@ When rendering an exception, Laravel will automatically determine if the excepti }); }) + +#### Customizing the Exception Response + +Rarely, you may need to customize the entire HTTP response rendered by Laravel's exception handler. To accomplish this, you may register a response customization closure using the `respond` method: + + use Symfony\Component\HttpFoundation\Response; + + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->respond(function (Response $response) { + if ($response->getStatusCode() === 419) { + return back()->with([ + 'message' => 'The page expired, please try again.', + ]); + } + + return $response; + }); + }) + ### Reportable and Renderable Exceptions diff --git a/releases.md b/releases.md index ba8d2c2ed92..6db1442ef9d 100644 --- a/releases.md +++ b/releases.md @@ -141,7 +141,7 @@ Like routing and middleware, exception handling can now be customized from your ->withExceptions(function (Exceptions $exceptions) { $exceptions->dontReport(MissedFlightException::class); - $exceptions->reportable(function (InvalidOrderException $e) { + $exceptions->report(function (InvalidOrderException $e) { // ... }); }) diff --git a/urls.md b/urls.md index 10eaf68ef3f..5d454d5e75a 100644 --- a/urls.md +++ b/urls.md @@ -145,19 +145,15 @@ If your signed URLs do not include the domain in the URL hash, you should provid #### Responding to Invalid Signed Routes -When someone visits a signed URL that has expired, they will receive a generic error page for the `403` HTTP status code. However, you can customize this behavior by defining a custom "renderable" closure for the `InvalidSignatureException` exception in your exception handler. This closure should return an HTTP response: +When someone visits a signed URL that has expired, they will receive a generic error page for the `403` HTTP status code. However, you can customize this behavior by defining a custom "render" closure for the `InvalidSignatureException` exception in your application's `bootstrap/app.php` file: use Illuminate\Routing\Exceptions\InvalidSignatureException; - /** - * Register the exception handling callbacks for the application. - */ - public function register(): void - { - $this->renderable(function (InvalidSignatureException $e) { + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->render(function (InvalidSignatureException $e) { return response()->view('error.link-expired', [], 403); }); - } + }) ## URLs for Controller Actions From 04ad01474ea43f445af1cb45b5229b01f218f166 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Tue, 12 Mar 2024 14:20:05 +0000 Subject: [PATCH 1534/2609] [10.x] Adds Reverb docs (#9388) * add Reverb docs * wip * wip * wip * wip * typos Co-authored-by: Julius Kiekbusch * wip * update ssl support * update server variables * formatting * Update broadcasting.md * Update broadcasting.md Co-authored-by: Dwight Watson --------- Co-authored-by: Julius Kiekbusch Co-authored-by: Taylor Otwell Co-authored-by: Dwight Watson --- broadcasting.md | 58 +++++++++- documentation.md | 1 + reverb.md | 281 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 reverb.md diff --git a/broadcasting.md b/broadcasting.md index 9c0390bfd1f..66fad8b52e8 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -3,10 +3,12 @@ - [Introduction](#introduction) - [Server Side Installation](#server-side-installation) - [Configuration](#configuration) + - [Reverb](#reverb) - [Pusher Channels](#pusher-channels) - [Ably](#ably) - [Open Source Alternatives](#open-source-alternatives) - [Client Side Installation](#client-side-installation) + - [Reverb](#client-reverb) - [Pusher Channels](#client-pusher-channels) - [Ably](#client-ably) - [Concept Overview](#concept-overview) @@ -52,7 +54,7 @@ The core concepts behind broadcasting are simple: clients connect to named chann #### 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.com). However, community driven packages such as [soketi](https://docs.soketi.app/) provide additional broadcasting drivers that do not require commercial broadcasting providers. +By default, Laravel includes three server-side broadcasting drivers for you to choose from: [Laravel Reverb](https://reverb.laravel.com), [Pusher Channels](https://pusher.com/channels), and [Ably](https://ably.com). > [!NOTE] > Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). @@ -79,6 +81,23 @@ Before broadcasting any events, you will first need to register the `App\Provide 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. + +### Reverb + +You may install Reverb using the Composer package manager. Since Reverb is currently in beta, you will need to explicitly install the beta release: + +```sh +composer require laravel/reverb:@beta +``` + +Once the package is installed, you may run Reverb's installation command to publish the configuration, update your applications's broadcasting configuration, and add Reverb's required environment variables: + +```sh +php artisan reverb:install +``` + +You can find detailed Reverb installation and usage instructions in the [Reverb documentation](/docs/{{version}}/reverb). + ### Pusher Channels @@ -149,6 +168,43 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst ## Client Side Installation + +### Reverb + +[Laravel Echo](https://github.com/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 Reverb utilizes the Pusher protocol for WebSocket subscriptions, channels, and messages: + +```shell +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 and update the `broadcaster` configuration option to `reverb`: + +```js +import Echo from 'laravel-echo'; + +import Pusher from 'pusher-js'; +window.Pusher = Pusher; + +window.Echo = new Echo({ + broadcaster: 'reverb', + key: import.meta.env.VITE_REVERB_APP_KEY, + wsHost: import.meta.env.VITE_REVERB_HOST, + wsPort: import.meta.env.VITE_REVERB_PORT, + wssPort: import.meta.env.VITE_REVERB_PORT, + forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', + enabledTransports: ['ws', 'wss'], +}); +``` + +Next, you should compile your application's assets: + +```shell +npm run build +``` + +> [!WARNING] +> The Laravel Echo `reverb` broadcaster requires laravel-echo v1.16.0+. + ### Pusher Channels diff --git a/documentation.md b/documentation.md index 31a916ba59f..18b0c1ca65a 100644 --- a/documentation.md +++ b/documentation.md @@ -96,6 +96,7 @@ - [Precognition](/docs/{{version}}/precognition) - [Prompts](/docs/{{version}}/prompts) - [Pulse](/docs/{{version}}/pulse) + - [Reverb](/docs/{{version}}/reverb) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) - [Scout](/docs/{{version}}/scout) diff --git a/reverb.md b/reverb.md new file mode 100644 index 00000000000..e424007abe5 --- /dev/null +++ b/reverb.md @@ -0,0 +1,281 @@ +# Laravel Reverb + +- [Introduction](#introduction) +- [Installation](#installation) +- [Configuration](#configuration) + - [Application Credentials](#application-credentials) + - [Allowed Origins](#allowed-origins) + - [Additional Applications](#additional-applications) + - [SSL](#ssl) +- [Running the Server](#running-server) + - [Debugging](#debugging) + - [Restarting](#restarting) +- [Running Reverb in Production](#production) + - [Open Files](#open-files) + - [Event Loop](#event-loop) + - [Web Server](#web-server) + - [Ports](#ports) + - [Process Management](#process-management) + - [Scaling](#scaling) + + +## Introduction + +[Laravel Reverb](https://github.com/laravel/reverb) brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, and provides seamless integration with Laravel’s existing suite of event broadcasting tools. + + +## Installation + +> [!WARNING] +> Laravel Reverb requires PHP 8.2+ and Laravel 10.47+. + +You may use the Composer package manager to install Reverb into your Laravel project. Since Reverb is currently in beta, you will need to explicitly install the beta release: + +```sh +composer require laravel/reverb:@beta +``` + +Once the package is installed, you may run Reverb's installation command to publish the configuration, add Reverb's required environment variables, and enable event broadcasting in your application: + +```sh +php artisan reverb:install +``` + + +## Configuration + +The `reverb:install` command will automatically configure Reverb using a sensible set of default options. If you would like to make any configuration changes, you may do so by updating Reverb's environment variables or by updating the `config/reverb.php` configuration file. + + +### Application Credentials + +In order establish a connection to Reverb, a set of Reverb "application" credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You may define these credentials using the following environment variables: + +```ini +REVERB_APP_ID=my-app-id +REVERB_APP_KEY=my-app-key +REVERB_APP_SECRET=my-app-secret +``` + + +### Allowed Origins + +You may also define the origins from which client requests may originate by updating the value of the `allowed_origins` configuration value within the `apps` section of the `config/reverb.php` configuration file. Any requests from an origin not listed in your allowed origins will be rejected. You may allow all origins using `*`: + +```php +'apps' => [ + [ + 'id' => 'my-app-id', + 'allowed_origins' => ['laravel.com'], + // ... + ] +] +``` + + +### Additional Applications + +Typically, Reverb provides a WebSocket server for the application in which it is installed. However, it is possible to serve more than one application using a single Reverb installation. + +For example, you may wish to maintain a single Laravel application which, via Reverb, provides WebSocket connectivity for multiple applications. This can be achieved by defining multiple `apps` in your application's `config/reverb.php` configuration file: + +```php +'apps' => [ + [ + 'id' => 'my-app-one', + // ... + ], + [ + 'id' => 'my-app-two', + // ... + ], +], +``` + + +### SSL + +In most cases, secure WebSocket connections are likely handled by an upstream web server (Nginx, etc.) before the request is proxied to your Reverb server. + +However, it can sometimes be useful, such as during local development, for the Reverb server to handle secure connections directly. If you are using [Laravel Herd's](https://herd.laravel.com) secure site functionality, or you are using [Laravel Valet](/docs/{{version}}/valet) and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may use the Herd / Valet certificate generated for your site to secure your Reverb connections. To do so, set the `REVERB_HOST` environment variable to your site's hostname or explicitly pass the hostname option when starting the Reverb server: + +```sh +php artisan reverb:start --host="0.0.0.0" --port=8080 --hostname="laravel.test" +``` + +Since Herd and Valet domains resolve to `localhost`, running the commmand above will result in your Reverb server being accessible via the secure WebSocket protocol (wss) at `wss://laravel.test:8080`. + +You may also manually choose a certificate by defining `tls` options in your application's `config/reverb.php` configuration file. Within the array of `tls` options, you may provide any of the options supported by [PHP's SSL context options](https://www.php.net/manual/en/context.ssl.php): + +```php +'options' => [ + 'tls' => [ + 'local_cert' => '/path/to/cert.pem' + ], +], +``` + + +## Running the Server + +The Reverb server can be started using the `reverb:start` Artisan command: + +```sh +php artisan reverb:start +``` + +By default, the Reverb server will be started at `0.0.0.0:8080`, making it accessible from all network interfaces. + +If you need to specify a custom host or port, you may do so via the `--host` and `--port` options when starting the server: + +```sh +php artisan reverb:start --host=127.0.0.1 --port=9000 +``` + +Alternatively, you may define `REVERB_SERVER_HOST` and `REVERB_SERVER_PORT` environment variables in your application's `.env` configuration file. + + +### Debugging + +To improve performance, Reverb does not output any debug information by default. If you would like to see the stream of data passing through your Reverb server, you may provide the `--debug` option to the `reverb:start` command: + +```sh +php artisan reverb:start --debug +``` + + +### Restarting + +Since Reverb is a long-running process, changes to your code will not be reflected without restarting the server via the `reverb:restart` Artisan command. + +The `reverb:restart` command ensures all connections are gracefully terminated before stopping the server. If you are running Reverb with a process manager such as Supervisor, the server will be automatically restarted by the process manager after all connections have been terminated: + +```sh +php artisan reverb:restart +``` + + +## Running Reverb in Production + +Due to the long-running nature of WebSocket servers, you may need to make some optimizations to your server and hosting environment to ensure your Reverb server can effectively handle the optimal number of connections for the resources available on your server. + +> [!NOTE] +> If your site is managed by [Laravel Forge](https://forge.laravel.com), you may automatically optimize your server for Reverb directly from the "Application" panel. By enabling the Reverb integration, Forge will ensure your server is production-ready, including installing any required extensions and increasing the allowed number of connections. + + +### Open Files + +Each WebSocket connection is held in memory until either the client or server disconnects. In Unix and Unix-like environments, each connection is represented by a file. However, there are often limits on the number of allowed open files at both the operating system and application level. + + +#### Operating System + +On a Unix based operating system, you may determine the allowed number of open files using the `ulimit` command: + +```sh +ulimit -n +``` + +This command will display the open file limits allowed for different users. You may update these values by editing the `/etc/security/limits.conf` file. For example, updating the maximum number of open files to 10,000 for the `forge` user would look like the following: + +```ini +# /etc/security/limits.conf +forge soft nofile 10000 +forge hard nofile 10000 +``` + + +### Event Loop + +Under the hood, Reverb uses a ReactPHP event loop to manage WebSocket connections on the server. By default, this event loop is powered by `stream_select`, which doesn't require any additional extensions. However, `stream_select` is typically limited to 1,024 open files. As such, if you plan to handle more than 1,000 concurrent connections, you will need to use an alternative event loop not bound by the same restrictions. + +Reverb will automatically switch to an `ext-event`, `ext-ev`, or `ext-uv` powered loop when available. All of these PHP extensions are available for install via PECL: + +```sh +pecl install event +# or +pecl install ev +# or +pecl install uv +``` + + +### Web Server + +In most cases, Reverb runs on a non web-facing port on your server. So, in order to route traffic to Reverb, you should configure a reverse proxy. Assuming Reverb is running on host `0.0.0.0` and port `8080` and your server utilizes the Nginx web server, a reverse proxy can be defined for your Reverb server using the following Nginx site configuration: + +```nginx +server { + ... + + location / { + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header Scheme $scheme; + proxy_set_header SERVER_PORT $server_port; + proxy_set_header REMOTE_ADDR $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + proxy_pass http://0.0.0.0:8080; + } + + ... +} +``` + +Typically, web servers are configured to limit the number of allowed connections in order to prevent overloading the server. To increase the number of allowed connections on an Nginx web server to 10,000, the `worker_rlimit_nofile` and `worker_connections` values of the `nginx.conf` file should be updated: + +```nginx +user forge; +worker_processes auto; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; +worker_rlimit_nofile 10000; + +events { + worker_connections 10000; + multi_accept on; +} +``` + +The configuration above will allow up to 10,000 Nginx workers per process to be spawned. In addition, this configuration sets Nginx's open file limit to 10,000. + + +### Ports + +Unix-based operating systems typically limit the number of ports which can be opened on the server. You may see the current allowed range via the following command: + + ```sh + cat /proc/sys/net/ipv4/ip_local_port_range +# 32768 60999 +``` + +The output above shows the server can handle a maximum of 28,231 (60,999 - 32,768) connections since each connection requires a free port. Although we recommend [horizontal scaling](#scaling) to increase the number of allowed connections, you may increase the number of available open ports by updating the allowed port range in your server's `/etc/sysctl.conf` configuration file. + + +### Process Management + +In most cases, you should use a process manager such as Supervisor to ensure the Reverb server is continually running. If you are using Supervisor to run Reverb, you should update the `minfds` setting of your server's `supervisor.conf` file to ensure Supervisor is able to open the files required to handle connections to your Reverb server: + +```ini +[supervisord] +... +minfds=10000 +``` + + +### Scaling + +If you need to handle more connections than a single server will allow, you may scale your Reverb server horizontally. Utilizing the publish / subscribe capabilities of Redis, Reverb is able to manage connections across multiple servers. When a message is received by one of your application's Reverb servers, the server will use Redis to publish the incoming message to all other servers. + +To enable horizontal scaling, you should set the `REVERB_SCALING_ENABLED` environment variable to `true` in your application's `.env` configuration file: + +```env +REVERB_SCALING_ENABLED=true +``` + +Next, you should have a dedicated, central Redis server to which all of the Reverb servers will communicate. Reverb will use the [default Redis connection configured for your application](/docs/{{version}}/redis#configuration) to publish messages to all of your Reverb servers. + +Once you have enabled Reverb's scaling option and configured a Redis server, you may simply invoke the `reverb:start` command on multiple servers that are able to communicate with your Redis server. These Reverb servers should be placed behind a load balancer that distributes incoming requests evenly among the servers. From 53a5d8bf61f8d726f09720fc76d45efd65d5744e Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 12 Mar 2024 16:42:45 +0000 Subject: [PATCH 1535/2609] Fixes `pheanstalk` required version (#9486) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 7ad96611d11..af27febf33a 100644 --- a/queues.md +++ b/queues.md @@ -148,7 +148,7 @@ The following dependencies are needed for the listed queue drivers. These depend
    - Amazon SQS: `aws/aws-sdk-php ~3.0` -- Beanstalkd: `pda/pheanstalk ~4.0` +- Beanstalkd: `pda/pheanstalk ~5.0` - Redis: `predis/predis ~2.0` or phpredis PHP extension
    From 119b4b9781e0b84185fa43b7315f521aa5872e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20De=20Sp=C3=ADrito?= Date: Tue, 12 Mar 2024 14:54:14 -0300 Subject: [PATCH 1536/2609] Add missing "to" in "in order to" (#9485) --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index e424007abe5..2300d9abbf1 100644 --- a/reverb.md +++ b/reverb.md @@ -49,7 +49,7 @@ The `reverb:install` command will automatically configure Reverb using a sensibl ### Application Credentials -In order establish a connection to Reverb, a set of Reverb "application" credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You may define these credentials using the following environment variables: +In order to establish a connection to Reverb, a set of Reverb "application" credentials must be exchanged between the client and server. These credentials are configured on the server and are used to verify the request from the client. You may define these credentials using the following environment variables: ```ini REVERB_APP_ID=my-app-id From 39225a5d9b7a0cd45016c688cf5e4bbe1b31d23a Mon Sep 17 00:00:00 2001 From: Luke Downing Date: Tue, 12 Mar 2024 17:54:30 +0000 Subject: [PATCH 1537/2609] Typo: changes "commmand" to "command". (#9481) --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index b684f7327c0..83b1956a7f0 100644 --- a/reverb.md +++ b/reverb.md @@ -97,7 +97,7 @@ However, it can sometimes be useful, such as during local development, for the R php artisan reverb:start --host="0.0.0.0" --port=8080 --hostname="laravel.test" ``` -Since Herd and Valet domains resolve to `localhost`, running the commmand above will result in your Reverb server being accessible via the secure WebSocket protocol (`wss`) at `wss://laravel.test:8080`. +Since Herd and Valet domains resolve to `localhost`, running the command above will result in your Reverb server being accessible via the secure WebSocket protocol (`wss`) at `wss://laravel.test:8080`. You may also manually choose a certificate by defining `tls` options in your application's `config/reverb.php` configuration file. Within the array of `tls` options, you may provide any of the options supported by [PHP's SSL context options](https://www.php.net/manual/en/context.ssl.php): From 88067c9e6953c6bdb5801bcce8966330252ed19e Mon Sep 17 00:00:00 2001 From: Philip Theobald Date: Tue, 12 Mar 2024 12:54:41 -0500 Subject: [PATCH 1538/2609] Fixes Cashier stripe package name - just cashier not cashier-stripe (#9483) --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index ccac287f89e..3b681cfe43b 100644 --- a/upgrade.md +++ b/upgrade.md @@ -68,7 +68,7 @@ You should update the following dependencies in your application's `composer.jso - `laravel/framework` to `^11.0` - `nunomaduro/collision` to `^8.1` -- `laravel/cashier-stripe` to `^15.0` (If installed) +- `laravel/cashier` to `^15.0` (If installed) - `laravel/passport` to `^12.0` (If installed) - `laravel/sanctum` to `^4.0` (If installed) - `laravel/spark-stripe` to `^5.0` (If installed) From 34636da971bfe472b1d8669edb6e5d09664a7c75 Mon Sep 17 00:00:00 2001 From: Philip Theobald Date: Tue, 12 Mar 2024 12:55:26 -0500 Subject: [PATCH 1539/2609] Add updated versions for Breeze and Inertia (#9484) Co-authored-by: Taylor Otwell --- upgrade.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/upgrade.md b/upgrade.md index 3b681cfe43b..09912a3ee98 100644 --- a/upgrade.md +++ b/upgrade.md @@ -68,11 +68,13 @@ You should update the following dependencies in your application's `composer.jso - `laravel/framework` to `^11.0` - `nunomaduro/collision` to `^8.1` +- `laravel/breeze` to `^2.0` (If installed) - `laravel/cashier` to `^15.0` (If installed) - `laravel/passport` to `^12.0` (If installed) - `laravel/sanctum` to `^4.0` (If installed) - `laravel/spark-stripe` to `^5.0` (If installed) - `laravel/telescope` to `^5.0` (If installed) +- `inertiajs/inertia-laravel` to `^1.0` (If installed)
    From 08354a737e77855609585f72f9e5373f55539394 Mon Sep 17 00:00:00 2001 From: Hossein Golabzade Date: Tue, 12 Mar 2024 21:51:20 +0330 Subject: [PATCH 1540/2609] update composer create-project command to version ^10.0 (#9487) --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 5920f5ea777..ed1d5c45300 100644 --- a/installation.md +++ b/installation.md @@ -58,7 +58,7 @@ Before creating your first Laravel project, make sure that your local machine ha After you have installed PHP and Composer, you may create a new Laravel project via Composer's `create-project` command: ```nothing -composer create-project laravel/laravel example-app +composer create-project laravel/laravel:^10.0 example-app ``` Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: From 4c33dcd4c6ef9f7f563eaa084a39565af1e891cc Mon Sep 17 00:00:00 2001 From: Moemen Gaballah <31255104+Moemen-Gaballah@users.noreply.github.com> Date: Tue, 12 Mar 2024 20:21:41 +0200 Subject: [PATCH 1541/2609] # Shortcut to generate a model, migration, factory, seeder, policy, controller, and form requests... (#9482) Co-authored-by: Moemen Gaballa --- eloquent.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent.md b/eloquent.md index 4da807f7b88..8da638cc5aa 100644 --- a/eloquent.md +++ b/eloquent.md @@ -92,6 +92,7 @@ php artisan make:model Flight -mfsc # Shortcut to generate a model, migration, factory, seeder, policy, controller, and form requests... php artisan make:model Flight --all +php artisan make:model Flight -a # Generate a pivot model... php artisan make:model Member --pivot From 2fd3f0796826e31e9c1cd0021c8d53dc5a082624 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 12 Mar 2024 22:23:28 +0330 Subject: [PATCH 1542/2609] [11.x] Inspecting Database (#9477) * inspecting database * formatting --------- Co-authored-by: Taylor Otwell --- database.md | 18 ++++++++++++++++-- migrations.md | 18 +++++++++++------- releases.md | 15 +++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/database.md b/database.md index 82abc1982d4..c86488477e5 100644 --- a/database.md +++ b/database.md @@ -102,8 +102,8 @@ To see how read / write connections should be configured, let's look at this exa 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_0900_ai_ci', + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_0900_ai_ci'), 'prefix' => '', 'prefix_indexes' => true, 'strict' => true, @@ -412,6 +412,20 @@ If you would like to include table row counts and database view details within t php artisan db:show --counts --views ``` +In addition, you may use the following `Schema` methods to inspect your database: + + use Illuminate\Support\Facades\Schema; + + $tables = Schema::getTables(); + $views = Schema::getViews(); + $columns = Schema::getColumns('users'); + $indexes = Schema::getIndexes('users'); + $foreignKeys = Schema::getForeignKeys('users'); + +If you would like to inspect a database connection that is not your application's default connection, you may use the `connection` method: + + $columns = Schema::connection('sqlite')->getColumns('users'); + #### Table Overview diff --git a/migrations.md b/migrations.md index 9df9c3a4200..6df04a5d488 100644 --- a/migrations.md +++ b/migrations.md @@ -267,7 +267,7 @@ When creating the table, you may use any of the schema builder's [column methods #### Determining Table / Column Existence -You may determine the existence of a table or column using the `hasTable` and `hasColumn` methods: +You may determine the existence of a table, column, or index using the `hasTable`, `hasColumn`, and `hasIndex` methods: if (Schema::hasTable('users')) { // The "users" table exists... @@ -277,6 +277,10 @@ You may determine the existence of a table or column using the `hasTable` and `h // The "users" table exists and has an "email" column... } + if (Schema::hasIndex('users', ['email'], 'unique')) { + // The "users" table exists and has a unique index on the "email" column... + } + #### Database Connection and Table Options @@ -289,7 +293,7 @@ If you want to perform a schema operation on a database connection that is not y In addition, a few other properties and methods may be used to define other aspects of the table's creation. The `engine` property may be used to specify the table's storage engine when using MySQL: Schema::create('users', function (Blueprint $table) { - $table->engine = 'InnoDB'; + $table->engine('InnoDB'); // ... }); @@ -297,8 +301,8 @@ In addition, a few other properties and methods may be used to define other aspe The `charset` and `collation` properties may be used to specify the character set and collation for the created table when using MySQL: Schema::create('users', function (Blueprint $table) { - $table->charset = 'utf8mb4'; - $table->collation = 'utf8mb4_unicode_ci'; + $table->charset('utf8mb4'); + $table->collation('utf8mb4_unicode_ci'); // ... }); @@ -941,7 +945,7 @@ Modifier | Description `->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. -`->comment('my comment')` | Add a comment to a column (MySQL/PostgreSQL). +`->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). @@ -951,7 +955,7 @@ Modifier | Description `->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 (MySQL). -`->virtualAs($expression)` | Create a virtual generated column (MySQL / PostgreSQL / SQLite). +`->virtualAs($expression)` | Create a virtual generated column (MySQL / SQLite). `->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). @@ -1097,7 +1101,7 @@ Command | Description `$table->primary(['id', 'parent_id']);` | Adds composite keys. `$table->unique('email');` | Adds a unique index. `$table->index('state');` | Adds an index. -`$table->fullText('body');` | Adds a full text index (MySQL/PostgreSQL). +`$table->fullText('body');` | Adds a full text index (MySQL / PostgreSQL). `$table->fullText('body')->language('english');` | Adds a full text index of the specified language (PostgreSQL). `$table->spatialIndex('location');` | Adds a spatial index (except SQLite). diff --git a/releases.md b/releases.md index 6db1442ef9d..7574bfb6926 100644 --- a/releases.md +++ b/releases.md @@ -351,3 +351,18 @@ Laravel 11 includes improved support for MariaDB. In previous Laravel releases, For more information on Laravel's database drivers, check out the [database documentation](/docs/{{version}}/database). + +### Inspecting Databases and Improved Schema Operations + +_Improved schema operations and database inspection was contributed by [Hafez Divandari](https://github.com/hafezdivandari)_ + +Laravel 11 provides additional database schema operation and inspection methods, including the native modifying, renaming, and dropping of columns. Furthermore, advanced spatial types, non-default schema names, and native schema methods for manipulating tables, views, columns, indexes, and foreign keys are provided: + + use Illuminate\Support\Facades\Schema; + + $tables = Schema::getTables(); + $views = Schema::getViews(); + $columns = Schema::getColumns('users'); + $indexes = Schema::getIndexes('users'); + $foreignKeys = Schema::getForeignKeys('users'); + From 7b8cac3a18d4d9e0c98c0fe90e693283bfcf0515 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Mar 2024 14:29:32 -0500 Subject: [PATCH 1543/2609] wip --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index 83b1956a7f0..407dfa711a4 100644 --- a/reverb.md +++ b/reverb.md @@ -21,7 +21,7 @@ ## Introduction -[Laravel Reverb](https://github.com/laravel/reverb) brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, and provides seamless integration with Laravel’s existing suite of event broadcasting tools. +[Laravel Reverb](https://github.com/laravel/reverb) brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, and provides seamless integration with Laravel’s existing suite of [event broadcasting tools](/docs/{{version}}/broadcasting). ## Installation From 83dc2c8eaa97506ebf7b9f464277f7fc966170d2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Mar 2024 14:30:27 -0500 Subject: [PATCH 1544/2609] wip --- reverb.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/reverb.md b/reverb.md index 407dfa711a4..eeef5eb1126 100644 --- a/reverb.md +++ b/reverb.md @@ -26,9 +26,6 @@ ## Installation -> **Warning** -> Laravel Reverb requires PHP 8.2+. - You may install Reverb using the `install:broadcasting` Artisan command: ``` From 99bddf8c3b16b15e3cf78b8d64b6f8980097b96f Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 12 Mar 2024 20:38:57 +0100 Subject: [PATCH 1545/2609] [11.x] Document implicit commit usage with batches (#8234) * Update queues.md * Update queues.md * wip --------- Co-authored-by: Taylor Otwell --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index af27febf33a..7d61ab789ec 100644 --- a/queues.md +++ b/queues.md @@ -1280,7 +1280,7 @@ To dispatch a batch of jobs, you should use the `batch` method of the `Bus` faca 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. > [!WARNING] -> Since batch callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within the callbacks. +> Since batch callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within the callbacks. In addition, since batched jobs are wrapped within database transactions, database statements that trigger implicit commits should not be executed within the jobs. #### Naming Batches From b351d25d878380206484fc7b005b1a14f42e0616 Mon Sep 17 00:00:00 2001 From: Terje Nesthus Date: Tue, 12 Mar 2024 20:44:53 +0100 Subject: [PATCH 1546/2609] Added a line about jetstream upgrade to the upgrade docs (#9488) * Added a line about jetstream upgrade to the upgrade docs * Update upgrade.md --------- Co-authored-by: Terje Nesthus, working from home Co-authored-by: Taylor Otwell --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 09912a3ee98..318275ec893 100644 --- a/upgrade.md +++ b/upgrade.md @@ -70,6 +70,7 @@ You should update the following dependencies in your application's `composer.jso - `nunomaduro/collision` to `^8.1` - `laravel/breeze` to `^2.0` (If installed) - `laravel/cashier` to `^15.0` (If installed) +- `laravel/jetstream` to `^5.0` (If installed) - `laravel/passport` to `^12.0` (If installed) - `laravel/sanctum` to `^4.0` (If installed) - `laravel/spark-stripe` to `^5.0` (If installed) From f23b126d83acf6bf6c24eeb9bd4e89e3900a89ff Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 12 Mar 2024 15:44:00 -0500 Subject: [PATCH 1547/2609] add miscellaneous upgrade note (#9490) --- upgrade.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/upgrade.md b/upgrade.md index 318275ec893..2dfe7f8f165 100644 --- a/upgrade.md +++ b/upgrade.md @@ -587,3 +587,8 @@ php artisan vendor:publish --tag=telescope-migrations **Likelihood Of Impact: Medium** Laravel 11 now provides its own [`once` function](/docs/{{version}}/helpers#method-once) to ensure that a given closure is only executed once. Therefore, if your application has a dependency on the `spatie/once` package, you should remove it from your application's `composer.json` file to avoid conflicts. + + +### Miscellaneous + +We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/10.x...11.x) and choose which updates are important to you. From e4e7008dd408ad7327656d72612b36de61ed3c56 Mon Sep 17 00:00:00 2001 From: Philip Theobald Date: Tue, 12 Mar 2024 16:05:15 -0500 Subject: [PATCH 1548/2609] Add new Dusk version to upgrade guide (#9491) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 2dfe7f8f165..b5ef82e189c 100644 --- a/upgrade.md +++ b/upgrade.md @@ -70,6 +70,7 @@ You should update the following dependencies in your application's `composer.jso - `nunomaduro/collision` to `^8.1` - `laravel/breeze` to `^2.0` (If installed) - `laravel/cashier` to `^15.0` (If installed) +- `laravel/dusk` to `^8.0` (If installed) - `laravel/jetstream` to `^5.0` (If installed) - `laravel/passport` to `^12.0` (If installed) - `laravel/sanctum` to `^4.0` (If installed) From bfcdb6c719d51f74d87e3899728d215cd2faf424 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 13 Mar 2024 11:02:33 +0000 Subject: [PATCH 1549/2609] Fixes version --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 0c967418a56..a36b8b879a7 100644 --- a/installation.md +++ b/installation.md @@ -58,7 +58,7 @@ Before creating your first Laravel project, make sure that your local machine ha After you have installed PHP and Composer, you may create a new Laravel project via Composer's `create-project` command: ```nothing -composer create-project laravel/laravel:^10.0 example-app +composer create-project laravel/laravel:^11.0 example-app ``` Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: From 474701c4fa28905135a24ece53ace967a429a707 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 13 Mar 2024 14:23:05 +0100 Subject: [PATCH 1550/2609] Re-add getters which were removed --- configuration.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configuration.md b/configuration.md index 1fdde1e8df2..e77cd8ea5c2 100644 --- a/configuration.md +++ b/configuration.md @@ -203,6 +203,14 @@ To set configuration values at runtime, you may invoke the `Config` facade's `se config(['app.timezone' => 'America/Chicago']); +To assist with static analysis, the `Config` facade also provides typed configuration retrieval methods. If the retrieved configuration value does not match the expected type, an exception will be thrown: + + Config::string('config-key'); + Config::integer('config-key'); + Config::float('config-key'); + Config::boolean('config-key'); + Config::array('config-key'); + ## Configuration Caching From f908fe3b6d247b7e321f558940e829f1128cae64 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Wed, 13 Mar 2024 17:01:21 +0100 Subject: [PATCH 1551/2609] Prep Laravel v12 (#9495) --- releases.md | 323 +----------------------------- upgrade.md | 554 +--------------------------------------------------- 2 files changed, 13 insertions(+), 864 deletions(-) diff --git a/releases.md b/releases.md index fd82d4a68fb..ba7464ecd63 100644 --- a/releases.md +++ b/releases.md @@ -2,7 +2,7 @@ - [Versioning Scheme](#versioning-scheme) - [Support Policy](#support-policy) -- [Laravel 11](#laravel-11) +- [Laravel 12](#laravel-12) ## Versioning Scheme @@ -46,322 +46,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe (*) Supported PHP versions - -## Laravel 11 + +## Laravel 12 -Laravel 11 continues the improvements made in Laravel 10.x by introducing a streamlined application structure, per-second rate limiting, health routing, graceful encryption key rotation, queue testing improvements, [Resend](https://resend.com) mail transport, Prompt validator integration, new Artisan commands, and more. In addition, Laravel Reverb, a first-party, scalable WebSocket server has been introduced to provide robust real-time capabilities to your applications. - - -### PHP 8.2 - -Laravel 11.x requires a minimum PHP version of 8.2. - - -### Streamlined Application Structure - -_Laravel's streamlined application structure was developed by [Taylor Otwell](https://github.com/taylorotwell) and [Nuno Maduro](https://github.com/nunomaduro)_. - -Laravel 11 introduces a streamlined application structure for **new** Laravel applications, without requiring any changes to existing applications. The new application structure is intended to provide a leaner, more modern experience, while retaining many of the concepts that Laravel developers are already familiar with. Below we will discuss the highlights of Laravel's new application structure. - -#### The Application Bootstrap File - -The `bootstrap/app.php` file has been revitalized as a code-first application configuration file. From this file, you may now customize your application's routing, middleware, service providers, exception handling, and more. This file unifies a variety of high-level application behavior settings that were previously scattered throughout your application's file structure: - -```php -return Application::configure(basePath: dirname(__DIR__)) - ->withRouting( - web: __DIR__.'/../routes/web.php', - commands: __DIR__.'/../routes/console.php', - health: '/up', - ) - ->withMiddleware(function (Middleware $middleware) { - // - }) - ->withExceptions(function (Exceptions $exceptions) { - // - })->create(); -``` - - -#### Service Providers - -Instead of the default Laravel application structure containing five service providers, Laravel 11 only includes a single `AppServiceProvider`. The functionality of the previous service providers has been incorporated into the `bootstrap/app.php`, is handled automatically by the framework, or may be placed in your application's `AppServiceProvider`. - -For example, event discovery is now enabled by default, largely eliminating the need for manual registration of events and their listeners. However, if you do need to manually register events, you may simply do so in the `AppServiceProvider`. Similarly, route model bindings or authorization gates you may have previously registered in the `AuthServiceProvider` may also be registered in the `AppServiceProvider`. - - -#### Opt-in API and Broadcast Routing - -The `api.php` and `channels.php` route files are no longer present by default, as many applications do not require these files. Instead, they may be created using simple Artisan commands: - -```shell -php artisan install:api - -php artisan install:broadcasting -``` - - -#### Middleware - -Previously, new Laravel applications included nine middleware. These middleware performed a variety of tasks such as authenticating requests, trimming input strings, and validating CSRF tokens. - -In Laravel 11, these middleware have been moved into the framework itself, so that they do not add bulk to your application's structure. New methods for customizing the behavior of these middleware have been added to the framework and may be invoked from your application's `bootstrap/app.php` file: - -```php -->withMiddleware(function (Middleware $middleware) { - $middleware->validateCsrfTokens( - except: ['stripe/*'] - ); - - $middleware->web(append: [ - EnsureUserIsSubscribed::class, - ]) -}) -``` - -Since all middleware can be easily customized via your application's `bootstrap/app.php`, the need for a separate HTTP "kernel" class has been eliminated. - - -#### Scheduling - -Using a new `Schedule` facade, scheduled tasks may now be defined directly in your application's `routes/console.php` file, eliminating the need for a separate console "kernel" class: - -```php -use Illuminate\Support\Facades\Schedule; - -Schedule::command('emails:send')->daily(); -``` - - -#### Exception Handling - -Like routing and middleware, exception handling can now be customized from your application's `bootstrap/app.php` file instead of a separate exception handler class, reducing the overall number of files included in a new Laravel application: - -```php -->withExceptions(function (Exceptions $exceptions) { - $exceptions->dontReport(MissedFlightException::class); - - $exceptions->report(function (InvalidOrderException $e) { - // ... - }); -}) -``` - - -#### Base `Controller` Class - -The base controller included in new Laravel applications has been simplified. It no longer extends Laravel's internal `Controller` class, and the `AuthorizesRequests` and `ValidatesRequests` traits have been removed, as they may be included in your application's individual controllers if desired: - - -#### Application Defaults - -By default, new Laravel applications use SQLite for database storage, as well as the `database` driver for Laravel's session, cache, and queue. This allows you to begin building your application immediately after creating a new Laravel application, without being required to install additional software or create additional database migrations. - -In addition, over time, the `database` drivers for these Laravel services have become robust enough for production usage in many application contexts; therefore, they provide a sensible, unified choice for both local and production applications. - - -### Laravel Reverb - -_Laravel Reverb was developed by [Joe Dixon](https://github.com/joedixon)_. - -[Laravel Reverb](https://reverb.laravel.com) brings blazing-fast and scalable real-time WebSocket communication directly to your Laravel application, and provides seamless integration with Laravel’s existing suite of event broadcasting tools, such as Laravel Echo. - -```shell -php artisan reverb:start -``` - -In addition, Reverb supports horizontal scaling via Redis's publish / subscribe capabilities, allowing you to distribute your WebSocket traffic across multiple backend Reverb servers all supporting a single, high-demand application. - -For more information on Laravel Reverb, please consult the complete [Reverb documentation](/docs/{{version}}/reverb). - - -### Per-Second Rate Limiting - -_Per-second rate limiting was contributed by [Tim MacDonald](https://github.com/timacdonald)_. - -Laravel now supports "per-second" rate limiting for all rate limiters, including those for HTTP requests and queued jobs. Previously, Laravel's rate limiters were limited to "per-minute" granularity: - -```php -RateLimiter::for('invoices', function (Request $request) { - return Limit::perSecond(1); -}); -``` - -For more information on rate limiting in Laravel, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). - - -### Health Routing - -_Health routing was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. - -New Laravel 11 applications include a `health` routing directive, which instructs Laravel to define a simple health-check endpoint that may be invoked by third-party application health monitoring services or orchestration systems like Kubernetes. By default, this route is served at `/up`: - -```php -->withRouting( - web: __DIR__.'/../routes/web.php', - commands: __DIR__.'/../routes/console.php', - health: '/up', -) -``` - -When HTTP requests are made to this route, Laravel will also dispatch a `DiagnosingHealth` event, allowing you to perform additional health checks that are relevant to your application. - - -### Graceful Encryption Key Rotation - -_Graceful encryption key rotation was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. - -Since Laravel encrypts all cookies, including your application's session cookie, essentially every request to a Laravel application relies on encryption. However, because of this, rotating your application's encryption key would log all users out of your application. In addition, decrypting data that was encrypted by the previous encryption key becomes impossible. - -Laravel 11 allows you to define your application's previous encryption keys as a comma-delimited list via the `APP_PREVIOUS_KEYS` environment variable. - -When encrypting values, Laravel will always use the "current" encryption key, which is within the `APP_KEY` environment variable. When decrypting values, Laravel will first try the current key. If decryption fails using the current key, Laravel will try all previous keys until one of the keys is able to decrypt the value. - -This approach to graceful decryption allows users to keep using your application uninterrupted even if your encryption key is rotated. - -For more information on encryption in Laravel, check out the [encryption documentation](/docs/{{version}}/encryption). - - -### Prompt Validation - -_Prompt validator integration was contributed by [Andrea Marco Sartori](https://github.com/cerbero90)_. - -[Laravel Prompts](/docs/{{version}}/prompts) is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. - -Laravel Prompts supports input validation via closures: - -```php -$name = text( - label: 'What is your name?', - validate: fn (string $value) => match (true) { - strlen($value) < 3 => 'The name must be at least 3 characters.', - strlen($value) > 255 => 'The name must not exceed 255 characters.', - default => null - } -); -``` - -However, this can become cumbersome when dealing with many inputs or complicated validation scenarios. Therefore, in Laravel 11, you may utilize the full power of Laravel's [validator](/docs/{{version}}/validation) when validating prompt inputs: - -```php -$name = text('What is your name?', validate: [ - 'name' => 'required|min:3|max:255', -]); -``` - - -### Queue Interaction Testing - -_Queue interaction testing was contributed by [Taylor Otwell](https://github.com/taylorotwell)_. - -Previously, attempting to test that a queued job was released, deleted, or manually failed was cumbersome and required the definition of custom queue fakes and stubs. However, in Laravel 11, you may easily test for these queue interactions using the `withFakeQueueInteractions` method: - -```php -use App\Jobs\ProcessPodcast; - -$job = (new ProcessPodcast)->withFakeQueueInteractions(); - -$job->handle(); - -$job->assertReleased(delay: 30); -``` - -For more information on testing queued jobs, check out the [queue documentation](/docs/{{version}}/queues#testing). - - -### New Artisan Commands - -_Class creation Artisan commands were contributed by [Taylor Otwell](https://github.com/taylorotwell)_. - -New Artisan commands have been added to allow the quick creation of classes, enums, interfaces, and traits: - -```shell -php artisan make:class -php artisan make:enum -php artisan make:interface -php artisan make:trait -``` - - -### Model Casts Improvements - -_Model casts improvements were contributed by [Nuno Maduro](https://github.com/nunomaduro)_. - -Laravel 11 supports defining your model's casts using a method instead of a property. This allows for streamlined, fluent cast definitions, especially when using casts with arguments: - - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'options' => AsCollection::using(OptionCollection::class), - // AsEncryptedCollection::using(OptionCollection::class), - // AsEnumArrayObject::using(OptionEnum::class), - // AsEnumCollection::using(OptionEnum::class), - ]; - } - -For more information on attribute casting, review the [Eloquent documentation](/docs/{{version}}/eloquent-mutators#attribute-casting). - - -### The `once` Function - -_The `once` helper was contributed by [Taylor Otwell](https://github.com/taylorotwell)_ and _[Nuno Maduro](https://github.com/nunomaduro)_. - -The `once` helper function executes the given callback and caches the result in memory for the duration of the request. Any subsequent calls to the `once` function with the same callback will return the previously cached result: - - function random(): int - { - return once(function () { - return random_int(1, 1000); - }); - } - - random(); // 123 - random(); // 123 (cached result) - random(); // 123 (cached result) - -For more information on the `once` helper, check out the [helpers documentation](/docs/{{version}}/helpers#method-once). - - -### Improved Performance When Testing With In-Memory Databases - -_Improved in-memory database testing performance was contributed by [Anders Jenbo](https://github.com/AJenbo)_ - -Laravel 11 offers a significant speed boost when using the `:memory:` SQLite database during testing. To accomplish this, Laravel now maintains a reference to PHP's PDO object and reuses it across connections, often cutting total test run time in half. - - -### Improved Support for MariaDB - -_Improved support for MariaDB was contributed by [Jonas Staudenmeir](https://github.com/staudenmeir) and [Julius Kiekbusch](https://github.com/Jubeki)_ - -Laravel 11 includes improved support for MariaDB. In previous Laravel releases, you could use MariaDB via Laravel's MySQL driver. However, Laravel 11 now includes a dedicated MariaDB driver which provides better defaults for this database system. - -For more information on Laravel's database drivers, check out the [database documentation](/docs/{{version}}/database). - - -### Inspecting Databases and Improved Schema Operations - -_Improved schema operations and database inspection was contributed by [Hafez Divandari](https://github.com/hafezdivandari)_ - -Laravel 11 provides additional database schema operation and inspection methods, including the native modifying, renaming, and dropping of columns. Furthermore, advanced spatial types, non-default schema names, and native schema methods for manipulating tables, views, columns, indexes, and foreign keys are provided: - - use Illuminate\Support\Facades\Schema; - - $tables = Schema::getTables(); - $views = Schema::getViews(); - $columns = Schema::getColumns('users'); - $indexes = Schema::getIndexes('users'); - $foreignKeys = Schema::getForeignKeys('users'); +TBA... diff --git a/upgrade.md b/upgrade.md index b5ef82e189c..85f7b42833a 100644 --- a/upgrade.md +++ b/upgrade.md @@ -1,18 +1,13 @@ # Upgrade Guide -- [Upgrading To 11.0 From 10.x](#upgrade-11.0) +- [Upgrading To 12.0 From 11.x](#upgrade-12.0) ## High Impact Changes
    -- [Updating Dependencies](#updating-dependencies) -- [Application Structure](#application-structure) -- [Floating-Point Types](#floating-point-types) -- [Modifying Columns](#modifying-columns) -- [SQLite Minimum Version](#sqlite-minimum-version) -- [Updating Sanctum](#updating-sanctum) +- TBD
    @@ -21,9 +16,7 @@
    -- [Carbon 3](#carbon-3) -- [Password Rehashing](#password-rehashing) -- [Per-Second Rate Limiting](#per-second-rate-limiting) +- TBD
    @@ -32,21 +25,15 @@
    -- [Doctrine DBAL Removal](#doctrine-dbal-removal) -- [Eloquent Model `casts` Method](#eloquent-model-casts-method) -- [Spatial Types](#spatial-types) -- [Spatie Once Package](#spatie-once-package) -- [The `Enumerable` Contract](#the-enumerable-contract) -- [The `UserProvider` Contract](#the-user-provider-contract) -- [The `Authenticatable` Contract](#the-authenticatable-contract) +- TBD
    - -## Upgrading To 11.0 From 10.x + +## Upgrading To 12.0 From 11.x -#### Estimated Upgrade Time: 15 Minutes +#### Estimated Upgrade Time: ?? Minutes > [!NOTE] > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. @@ -56,540 +43,17 @@ **Likelihood Of Impact: High** -#### PHP 8.2.0 Required - -Laravel now requires PHP 8.2.0 or greater. - #### Composer Dependencies You should update the following dependencies in your application's `composer.json` file:
    -- `laravel/framework` to `^11.0` -- `nunomaduro/collision` to `^8.1` -- `laravel/breeze` to `^2.0` (If installed) -- `laravel/cashier` to `^15.0` (If installed) -- `laravel/dusk` to `^8.0` (If installed) -- `laravel/jetstream` to `^5.0` (If installed) -- `laravel/passport` to `^12.0` (If installed) -- `laravel/sanctum` to `^4.0` (If installed) -- `laravel/spark-stripe` to `^5.0` (If installed) -- `laravel/telescope` to `^5.0` (If installed) -- `inertiajs/inertia-laravel` to `^1.0` (If installed) - -
    - -If your application is using Laravel Cashier Stripe, Passport, Sanctum, Spark Stripe, or Telescope, you will need to publish their migrations to your application. Cashier Stripe, Passport, Sanctum, Spark Stripe, and Telescope **no longer automatically load migrations from their own migrations** directory. Therefore, you should run the following command to publish their migrations to your application: - -```bash -php artisan vendor:publish --tag=cashier-migrations -php artisan vendor:publish --tag=passport-migrations -php artisan vendor:publish --tag=sanctum-migrations -php artisan vendor:publish --tag=spark-migrations -php artisan vendor:publish --tag=telescope-migrations -``` - -In addition, you should review the upgrade guides for each of these packages to ensure you are aware of any additional breaking changes: - -- [Laravel Cashier Stripe](#cashier-stripe) -- [Laravel Passport](#passport) -- [Laravel Sanctum](#sanctum) -- [Laravel Spark Stripe](#spark-stripe) -- [Laravel Telescope](#telescope) - -If you have manually installed the Laravel installer, you should update the installer via Composer: - -```bash -composer global require laravel/installer:^5.6 -``` - -Finally, you may remove the `doctrine/dbal` Composer dependency if you have previously added it to your application, as Laravel is no longer dependent on this package. - - -### Application Structure - -Laravel 11 introduces a new default application structure with fewer default files. Namely, new Laravel applications contain fewer service providers, middleware, and configuration files. - -However, we do **not recommend** that Laravel 10 applications upgrading to Laravel 11 attempt to migrate their application structure, as Laravel 11 has been carefully tuned to also support the Laravel 10 application structure. - - -### Authentication - - -#### Password Rehashing - -Laravel 11 will automatically rehash your user's passwords during authentication if your hashing algorithm's "work factor" has been updated since the password was last hashed. - -Typically, this should not disrupt your application; however, you may disable this behavior by adding the `rehash_on_login` option to your application's `config/hashing.php` configuration file: - - 'rehash_on_login' => false, - - -#### The `UserProvider` Contract - -**Likelihood Of Impact: Low** - -The `Illuminate\Contracts\Auth\UserProvider` contract has received a new `rehashPasswordIfRequired` method. This method is responsible for re-hashing and storing the user's password in storage when the application's hashing algorithm work factor has changed. - -If your application or package defines a class that implements this interface, you should add the new `rehashPasswordIfRequired` method to your implementation. A reference implementation can be found within the `Illuminate\Auth\EloquentUserProvider` class: - -```php -public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false); -``` - - -#### The `Authenticatable` Contract - -**Likelihood Of Impact: Low** - -The `Illuminate\Contracts\Auth\Authenticatable` contract has received a new `getAuthPasswordName` method. This method is responsible for returning the name of your authenticatable entity's password column. - -If your application or package defines a class that implements this interface, you should add the new `getAuthPasswordName` method to your implementation: - -```php -public function getAuthPasswordName() -{ - return 'password'; -} -``` - -The default `User` model included with Laravel receives this method automatically since the method is included within the `Illuminate\Auth\Authenticatable` trait. - - -### Cache - - -#### Cache Key Prefixes - -**Likelihood Of Impact: Very Low** - -Previously, if a cache key prefix was defined for the DynamoDB, Memcached, or Redis cache stores, Laravel would append a `:` to the prefix. In Laravel 11, the cache key prefix does not receive the `:` suffix. If you would like to maintain the previous prefixing behavior, you can manually add the `:` suffix to your cache key prefix. - - -### Collections - - -#### The `Enumerable` Contract - -**Likelihood Of Impact: Low** - -The `dump` method of the `Illuminate\Support\Enumerable` contract has been updated to accept a variadic `...$args` argument. If you are implementing this interface you should update your implementation accordingly: - -```php -public function dump(...$args); -``` - - -### Database - - -#### SQLite 3.35.0+ - -**Likelihood Of Impact: High** - -If your application is utilizing an SQLite database, SQLite 3.35.0 or greater is required. - - -#### Eloquent Model `casts` Method - -**Likelihood Of Impact: Low** - -The base Eloquent model class now defines a `casts` method in order to support the definition of attribute casts. If one of your application's models is defining a `casts` relationship, it may conflict with the `casts` method now present on the base Eloquent model class. - - -#### Modifying Columns - -**Likelihood Of Impact: High** - -When modifying a column, you must now explicitly include all the modifiers you want to keep on the column definition after it is changed. Any missing attributes will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column, even if those attributes have been assigned to the column by a previous migration. - -For example, imagine you have a migration that creates a `votes` column with the `unsigned`, `default`, and `comment` attributes: - -```php -Schema::create('users', function (Blueprint $table) { - $table->integer('votes')->unsigned()->default(1)->comment('The vote count'); -}); -``` - -Later, you write a migration that changes the column to be `nullable` as well: - -```php -Schema::table('users', function (Blueprint $table) { - $table->integer('votes')->nullable()->change(); -}); -``` - -In Laravel 10, this migration would retain the `unsigned`, `default`, and `comment` attributes on the column. However, in Laravel 11, the migration must now also include all of the attributes that were previously defined on the column. Otherwise, they will be dropped: - -```php -Schema::table('users', function (Blueprint $table) { - $table->integer('votes') - ->unsigned() - ->default(1) - ->comment('The vote count') - ->nullable() - ->change(); -}); -``` - -The `change` method does not change the indexes of the column. Therefore, you may use index modifiers to explicitly add or drop an index when modifying the column: - -```php -// Add an index... -$table->bigIncrements('id')->primary()->change(); - -// Drop an index... -$table->char('postal_code', 10)->unique(false)->change(); -``` - -If you do not want to update all of the existing "change" migrations in your application to retain the column's existing attributes, you may simply [squash your migrations](/docs/{{version}}/migrations#squashing-migrations): - -```bash -php artisan schema:dump -``` - -Once your migrations have been squashed, Laravel will "migrate" the database using your application's schema file before running any pending migrations. - - -#### Floating-Point Types - -**Likelihood Of Impact: High** - -The `double` and `float` migration column types have been rewritten to be consistent across all databases. - -The `double` column type now creates a `DOUBLE` equivalent column without total digits and places (digits after decimal point), which is the standard SQL syntax. Therefore, you may remove the arguments for `$total` and `$places`: - -```php -$table->double('amount'); -``` - -The `float` column type now creates a `FLOAT` equivalent column without total digits and places (digits after decimal point), but with an optional `$precision` specification to determine storage size as a 4-byte single-precision column or an 8-byte double-precision column. Therefore, you may remove the arguments for `$total` and `$places` and specify the optional `$precision` to your desired value and according to your database's documentation: - -```php -$table->float('amount', precision: 53); -``` - -The `unsignedDecimal`, `unsignedDouble`, and `unsignedFloat` methods have been removed, as the unsigned modifier for these column types has been deprecated by MySQL, and was never standardized on other database systems. However, if you wish to continue using the deprecated unsigned attribute for these column types, you may chain the `unsigned` method onto the column's definition: - -```php -$table->decimal('amount', total: 8, places: 2)->unsigned(); -$table->double('amount')->unsigned(); -$table->float('amount', precision: 53)->unsigned(); -``` - - -#### Dedicated MariaDB Driver - -**Likelihood Of Impact: Very Low** - -Instead of always utilizing the MySQL driver when connecting to MariaDB databases, Laravel 11 adds a dedicated database driver for MariaDB. - -If your application connects to a MariaDB database, you may update the connection configuration to the new `mariadb` driver to benefit from MariaDB specific features in the future: - - 'driver' => 'mariadb', - 'url' => env('DB_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', '3306'), - // ... - -Currently, the new MariaDB driver behaves like the current MySQL driver with one exception: the `uuid` schema builder method creates native UUID columns instead of `char(36)` columns. - -If your existing migrations utilize the `uuid` schema builder method and you choose to use the new `mariadb` database driver, you should update your migration's invocations of the `uuid` method to `char` to avoid breaking changes or unexpected behavior: - -```php -Schema::table('users', function (Blueprint $table) { - $table->char('uuid', 36); - - // ... -}); -``` - - -#### Spatial Types - -**Likelihood Of Impact: Low** - -The spatial column types of database migrations have been rewritten to be consistent across all databases. Therefore, you may remove `point`, `lineString`, `polygon`, `geometryCollection`, `multiPoint`, `multiLineString`, `multiPolygon`, and `multiPolygonZ` methods from your migrations and use `geometry` or `geography` methods instead: - -```php -$table->geometry('shapes'); -$table->geography('coordinates'); -``` - -To explicitly restrict the type or the spatial reference system identifier for values stored in the column on MySQL, MariaDB, and PostgreSQL, you may pass the `subtype` and `srid` to the method: - -```php -$table->geometry('dimension', subtype: 'polygon', srid: 0); -$table->geography('latitude', subtype: 'point', srid: 4326); -``` - -The `isGeometry` and `projection` column modifiers of the PostgreSQL grammar have been removed accordingly. - - -#### Doctrine DBAL Removal - -**Likelihood Of Impact: Low** - -The following list of Doctrine DBAL related classes and methods have been removed. Laravel is no longer dependent on this package and registering custom Doctrines types is no longer necessary for the proper creation and alteration of various column types that previously required custom types: - -
    - -- `Illuminate\Database\Schema\Builder::$alwaysUsesNativeSchemaOperationsIfPossible` class property -- `Illuminate\Database\Schema\Builder::useNativeSchemaOperationsIfPossible()` method -- `Illuminate\Database\Connection::usingNativeSchemaOperations()` method -- `Illuminate\Database\Connection::isDoctrineAvailable()` method -- `Illuminate\Database\Connection::getDoctrineConnection()` method -- `Illuminate\Database\Connection::getDoctrineSchemaManager()` method -- `Illuminate\Database\Connection::getDoctrineColumn()` method -- `Illuminate\Database\Connection::registerDoctrineType()` method -- `Illuminate\Database\DatabaseManager::registerDoctrineType()` method -- `Illuminate\Database\PDO` directory -- `Illuminate\Database\DBAL\TimestampType` class -- `Illuminate\Database\Schema\Grammars\ChangeColumn` class -- `Illuminate\Database\Schema\Grammars\RenameColumn` class -- `Illuminate\Database\Schema\Grammars\Grammar::getDoctrineTableDiff()` method +- `laravel/framework` to `^12.0`
    -In addition, registering custom Doctrine types via `dbal.types` in your application's `database` configuration file is no longer required. - -If you were previously using Doctrine DBAL to inspect your database and its associated tables, you may use Laravel's new native schema methods (`Schema::getTables()`, `Schema::getColumns()`, `Schema::getIndexes()`, `Schema::getForeignKeys()`, etc.) instead. - - -#### Deprecated Schema Methods - -**Likelihood Of Impact: Very Low** - -The deprecated, Doctrine based `Schema::getAllTables()`, `Schema::getAllViews()`, and `Schema::getAllTypes()` methods have been removed in favor of new Laravel native `Schema::getTables()`, `Schema::getViews()`, and `Schema::getTypes()` methods. - -When using PostgreSQL and SQL Server, none of the new schema methods will accept a three-part reference (e.g. `database.schema.table`). Therefore, you should use `connection()` to declare the database instead: - -```php -Schema::connection('database')->hasTable('schema.table'); -``` - - -#### Schema Builder `getColumnType()` Method - -**Likelihood Of Impact: Very Low** - -The `Schema::getColumnType()` method now always returns actual type of the given column, not the Doctrine DBAL equivalent type. - - -#### Database Connection Interface - -**Likelihood Of Impact: Very Low** - -The `Illuminate\Database\ConnectionInterface` interface has received a new `scalar` method. If you are defining your own implementation of this interface, you should add the `scalar` method to your implementation: - -```php -public function scalar($query, $bindings = [], $useReadPdo = true); -``` - - -### Dates - - -#### Carbon 3 - -**Likelihood Of Impact: Medium** - -Laravel 11 supports both Carbon 2 and Carbon 3. Carbon is a date manipulation library utilized extensively by Laravel and packages throughout the ecosystem. If you install Carbon 3, you should review Carbon's [change log](https://github.com/briannesbitt/Carbon/releases/tag/3.0.0). - - -### Mail - - -#### The `Mailer` Contract - -**Likelihood Of Impact: Very Low** - -The `Illuminate\Contracts\Mail\Mailer` contract has received a new `sendNow` method. If your application or package is manually implementing this contract, you should add the new `sendNow` method to your implementation: - -```php -public function sendNow($mailable, array $data = [], $callback = null); -``` - - -### Packages - - -#### Publishing Service Providers to the Application - -**Likelihood Of Impact: Very Low** - -If you have written a Laravel package that manually publishes a service provider to the application's `app/Providers` directory and manually modifies the application's `config/app.php` configuration file to register the service provider, you should update your package to utilize the new `ServiceProvider::addProviderToBootstrapFile` method. - -The `addProviderToBootstrapFile` method will automatically add the service provider you have published to the application's `bootstrap/providers.php` file, since the `providers` array does not exist within the `config/app.php` configuration file in new Laravel 11 applications. - -```php -use Illuminate\Support\ServiceProvider; - -ServiceProvider::addProviderToBootstrapFile(Provider::class); -``` - - -### Queues - - -#### The `BatchRepository` Interface - -**Likelihood Of Impact: Very Low** - -The `Illuminate\Bus\BatchRepository` interface has received a new `rollBack` method. If you are implementing this interface within your own package or application, you should add this method to your implementation: - -```php -public function rollBack(); -``` - - -#### Synchronous Jobs in Database Transactions - -**Likelihood Of Impact: Very Low** - -Previously, synchronous jobs (jobs using the `sync` queue driver) would execute immediately, regardless of whether the `after_commit` configuration option of the queue connection was set to `true` or the `afterCommit` method was invoked on the job. - -In Laravel 11, synchronous queue jobs will now respect the "after commit" configuration of the queue connection or job. - - -### Rate Limiting - - -#### Per-Second Rate Limiting - -**Likelihood Of Impact: Medium** - -Laravel 11 supports per-second rate limiting instead of being limited to per-minute granularity. There are a variety of potential breaking changes you should be aware of related to this change. - -The `GlobalLimit` class constructor now accepts seconds instead of minutes. This class is not documented and would not typically be used by your application: - -```php -new GlobalLimit($attempts, 2 * 60); -``` - -The `Limit` class constructor now accepts seconds instead of minutes. All documented usages of this class are limited to static constructors such as `Limit::perMinute` and `Limit::perSecond`. However, if you are instantiating this class manually, you should update your application to provide seconds to the class's constructor: - -```php -new Limit($key, $attempts, 2 * 60); -``` - -The `Limit` class's `decayMinutes` property has been renamed to `decaySeconds` and now contains seconds instead of minutes. - -The `Illuminate\Queue\Middleware\ThrottlesExceptions` and `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` class constructors now accept seconds instead of minutes: - -```php -new ThrottlesExceptions($attempts, 2 * 60); -new ThrottlesExceptionsWithRedis($attempts, 2 * 60); -``` - - -### Cashier Stripe - - -#### Updating Cashier Stripe - -**Likelihood Of Impact: High** - -Laravel 11 no longer supports Cashier Stripe 14.x. Therefore, you should update your application's Laravel Cashier Stripe dependency to `^15.0` in your `composer.json` file. - -Cashier Stripe 15.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Cashier Stripe's migrations to your application: - -```shell -php artisan vendor:publish --tag=cashier-migrations -``` - -Please review the complete [Cashier Stripe upgrade guide](https://github.com/laravel/cashier-stripe/blob/15.x/UPGRADE.md) for additional breaking changes. - - -### Spark (Stripe) - - -#### Updating Spark Stripe - -**Likelihood Of Impact: High** - -Laravel 11 no longer supports Laravel Spark Stripe 4.x. Therefore, you should update your application's Laravel Spark Stripe dependency to `^5.0` in your `composer.json` file. - -Spark Stripe 5.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Spark Stripe's migrations to your application: - -```shell -php artisan vendor:publish --tag=spark-migrations -``` - -Please review the complete [Spark Stripe upgrade guide](https://spark.laravel.com/docs/spark-stripe/upgrade.html) for additional breaking changes. - - -### Passport - - -#### Updating Passport - -**Likelihood Of Impact: High** - -Laravel 11 no longer supports Laravel Passport 11.x. Therefore, you should update your application's Laravel Passport dependency to `^12.0` in your `composer.json` file. - -Passport 12.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Passport's migrations to your application: - -```shell -php artisan vendor:publish --tag=passport-migrations -``` - -In addition, the password grant type is disabled by default. You may enable it by invoking the `enablePasswordGrant` method in the `boot` method of your application's `AppServiceProvider`: - - public function boot(): void - { - Passport::enablePasswordGrant(); - } - - -### Sanctum - - -#### Updating Sanctum - -**Likelihood Of Impact: High** - -Laravel 11 no longer supports Laravel Sanctum 3.x. Therefore, you should update your application's Laravel Sanctum dependency to `^4.0` in your `composer.json` file. - -Sanctum 4.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Sanctum's migrations to your application: - -```shell -php artisan vendor:publish --tag=sanctum-migrations -``` - -Then, in your application's `config/sanctum.php` configuration file, you should update the references to the `authenticate_session`, `encrypt_cookies`, and `validate_csrf_token` middleware to the following: - - 'middleware' => [ - 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, - 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, - 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, - ], - - -### Telescope - - -#### Updating Telescope - -**Likelihood Of Impact: High** - -Laravel 11 no longer supports Laravel Telescope 4.x. Therefore, you should update your application's Laravel Telescope dependency to `^5.0` in your `composer.json` file. - -Telescope 5.0 no longer automatically loads migrations from its own migrations directory. Instead, you should run the following command to publish Telescope's migrations to your application: - -```shell -php artisan vendor:publish --tag=telescope-migrations -``` - - -### Spatie Once Package - -**Likelihood Of Impact: Medium** - -Laravel 11 now provides its own [`once` function](/docs/{{version}}/helpers#method-once) to ensure that a given closure is only executed once. Therefore, if your application has a dependency on the `spatie/once` package, you should remove it from your application's `composer.json` file to avoid conflicts. - ### Miscellaneous -We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/10.x...11.x) and choose which updates are important to you. +We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/11.x...master) and choose which updates are important to you. From 51c2103e2594f1655666b965fbfd5893a0cbec1b Mon Sep 17 00:00:00 2001 From: Theo Zebua <87429668+theozebua@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:04:02 +0700 Subject: [PATCH 1552/2609] Fix typo in `lifecycle.md` (#9492) --- lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifecycle.md b/lifecycle.md index e25ef3da79e..98c78f1dda3 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -33,7 +33,7 @@ Next, the incoming request is sent to either the HTTP kernel or the console kern The HTTP kernel defines an array of `bootstrappers` that will be run before the request is executed. These bootstrappers configure error handling, configure logging, [detect the application environment](/docs/{{version}}/configuration#environment-configuration), and perform other tasks that need to be done before the request is actually handled. Typically, these classes handle internal Laravel configuration that you do not need to worry about. -The HTTP kernel is also responsible for passing the request though the application's middleware stack. These middleware handle reading and writing the [HTTP session](/docs/{{version}}/session), determining if the application is in maintenance mode, [verifying the CSRF token](/docs/{{version}}/csrf), and more. We'll talk more about these soon. +The HTTP kernel is also responsible for passing the request through the application's middleware stack. These middleware handle reading and writing the [HTTP session](/docs/{{version}}/session), determining if the application is in maintenance mode, [verifying the CSRF token](/docs/{{version}}/csrf), and more. We'll talk more about these soon. The method signature for the HTTP kernel's `handle` method is quite simple: it receives a `Request` and returns a `Response`. Think of the kernel as being a big black box that represents your entire application. Feed it HTTP requests and it will return HTTP responses. From 0e27d90897987784eeb10a68cb566380e384666e Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Wed, 13 Mar 2024 17:55:44 +0000 Subject: [PATCH 1553/2609] remove duplicate section --- broadcasting.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 1a0e44a9797..b65e1a4cd5e 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -103,23 +103,6 @@ php artisan reverb:install You can find detailed Reverb installation and usage instructions in the [Reverb documentation](/docs/{{version}}/reverb). - -### Reverb - -You may install Reverb using the Composer package manager. Since Reverb is currently in beta, you will need to explicitly install the beta release: - -```sh -composer require laravel/reverb:@beta -``` - -Once the package is installed, you may run Reverb's installation command to publish the configuration, update your applications's broadcasting configuration, and add Reverb's required environment variables: - -```sh -php artisan reverb:install -``` - -You can find detailed Reverb installation and usage instructions in the [Reverb documentation](/docs/{{version}}/reverb). - ### Pusher Channels From 3924f89d8beae07b6e45afdb094fab5c53533f1d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 13 Mar 2024 20:46:53 +0000 Subject: [PATCH 1554/2609] [11.x] Adjusts installation docs (#9498) * Adjusts installation docs * Update installation.md --------- Co-authored-by: Taylor Otwell --- installation.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/installation.md b/installation.md index a36b8b879a7..e20a49e7c90 100644 --- a/installation.md +++ b/installation.md @@ -164,7 +164,13 @@ cd example-app ./vendor/bin/sail up ``` -Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. +Once the application's Docker containers have started, you should run your application's [database migrations](/docs/{{version}}/migrations): + +```shell +./vendor/bin/sail artisan migrate +``` + +Finally, you can access the application in your web browser at: http://localhost. > [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). @@ -195,7 +201,13 @@ cd example-app ./vendor/bin/sail up ``` -Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. +Once the application's Docker containers have started, you should run your application's [database migrations](/docs/{{version}}/migrations): + +```shell +./vendor/bin/sail artisan migrate +``` + +Finally, you can access the application in your web browser at: http://localhost. > [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). @@ -235,7 +247,13 @@ cd example-app ./vendor/bin/sail up ``` -Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. +Once the application's Docker containers have started, you should run your application's [database migrations](/docs/{{version}}/migrations): + +```shell +./vendor/bin/sail artisan migrate +``` + +Finally, you can access the application in your web browser at: http://localhost. > [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). From 1eac7051b63500e86d9afa69aae37baab1d17136 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 14 Mar 2024 07:47:57 +1100 Subject: [PATCH 1555/2609] Sleep::syncWithCarbon (#9462) * Sleep::syncWithCarbon * Update helpers.md * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/helpers.md b/helpers.md index 8009bb168f0..b438f67c930 100644 --- a/helpers.md +++ b/helpers.md @@ -2507,4 +2507,16 @@ Sleep::whenFakingSleep(function (Duration $duration) { }); ``` +As progressing time is a common requirement, the `fake` method accepts a `syncWithCarbon` argument to keep Carbon in sync when sleeping within a test: + +```php +Sleep::fake(syncWithCarbon: true); + +$start = now(); + +Sleep::for(1)->second(); + +$start->diffForHumans(); // 1 second ago +``` + Laravel uses the `Sleep` class internally whenever it is pausing execution. For example, the [`retry`](#method-retry) helper uses the `Sleep` class when sleeping, allowing for improved testability when using that helper. From 058d5ba709ddf88676f75e9e64aa6876ef11ce0b Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Thu, 14 Mar 2024 15:50:10 +0000 Subject: [PATCH 1556/2609] [11.x] Documents Reverb's Pulse integration (#9496) * documents pulse integration * formatting --------- Co-authored-by: Taylor Otwell --- reverb.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/reverb.md b/reverb.md index 1fe0195c0d3..1db9ace28cf 100644 --- a/reverb.md +++ b/reverb.md @@ -10,6 +10,7 @@ - [Running the Server](#running-server) - [Debugging](#debugging) - [Restarting](#restarting) +- [Monitoring](#monitoring) - [Running Reverb in Production](#production) - [Open Files](#open-files) - [Event Loop](#event-loop) @@ -145,6 +146,40 @@ The `reverb:restart` command ensures all connections are gracefully terminated b php artisan reverb:restart ``` + +## Monitoring + +Reverb may be monitored via an integration with [Laravel Pulse](/docs/{{version}}/pulse). By enabling Reverb's Pulse integration, you may track the number of connections and messages being handled by your server. + +To enable the integration, you should first ensure you have [installed Pulse](/docs/{{version}}/pulse#installation). Then, add any of Reverb's recorders to your application's `config/pulse.php` configuration file: + +```php +use Laravel\Reverb\Pulse\Recorders\ReverbConnections; +use Laravel\Reverb\Pulse\Recorders\ReverbMessages; + +'recorders' => [ + ReverbConnections::class => [ + 'sample_rate' => 1, + ], + + ReverbMessages::class => [ + 'sample_rate' => 1, + ], + + ... +], +``` + +Next, add the Pulse cards for each recorder to your [Pulse dashboard](/docs/{{version}}/pulse#dashboard-customization): + +```blade + + + + ... + +``` + ## Running Reverb in Production From 2099c290d28e19aac91eed068a986231fa2447f7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Mar 2024 11:01:08 -0500 Subject: [PATCH 1557/2609] wip --- releases.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/releases.md b/releases.md index fd82d4a68fb..6a3663c05ed 100644 --- a/releases.md +++ b/releases.md @@ -230,6 +230,15 @@ This approach to graceful decryption allows users to keep using your application For more information on encryption in Laravel, check out the [encryption documentation](/docs/{{version}}/encryption). + +### Automatic Password Rehashing + +_Automatic password rehashing was contributed by [Stephen Rees-Carter](https://github.com/valorin)_. + +Laravel's default password hashing algorithm is bcrypt. The "work factor" for bcrypt hashes can be adjusted via the `config/hashing.php` configuration file or the `BCRYPT_ROUNDS` environment variable. + +Typically, the bcrypt work factor should be increased over time as CPU / GPU processing power increases. If you increase the bcrypt work factor for your application, Laravel will now gracefully and automatically rehash user passwords as users authenticate with your application. + ### Prompt Validation From 32738337ef728723fb8b69f098db4a14f226c0b3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 15 Mar 2024 16:45:49 -0500 Subject: [PATCH 1558/2609] wip --- sanctum.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sanctum.md b/sanctum.md index 8922f2fc60a..3ea81b51ae1 100644 --- a/sanctum.md +++ b/sanctum.md @@ -330,9 +330,17 @@ To protect routes so that all incoming requests must be authenticated, you shoul ### Authorizing Private Broadcast Channels -If your SPA needs to authenticate with [private / presence broadcast channels](/docs/{{version}}/broadcasting#authorizing-channels), you should place the `Broadcast::routes` method call within your `routes/api.php` file: - - Broadcast::routes(['middleware' => ['auth:sanctum']]); +If your SPA needs to authenticate with [private / presence broadcast channels](/docs/{{version}}/broadcasting#authorizing-channels), you should remove the `channels` entry from the `withRouting` method contained in your application's `bootstrap/app.php` file. Instead, you should invoke the `withBroadcasting` method so that you may specify the correct middleware for your application's broadcasting routes: + + return Application::configure(basePath: dirname(__DIR__)) + ->withRouting( + web: __DIR__.'/../routes/web.php', + // ... + ) + ->withBroadcasting( + __DIR__.'/../routes/channels.php', + ['middleware' => ['auth:sanctum']], + ) Next, in order for Pusher's authorization requests to succeed, you will need to provide a custom Pusher `authorizer` when initializing [Laravel Echo](/docs/{{version}}/broadcasting#client-side-installation). This allows your application to configure Pusher to use the `axios` instance that is [properly configured for cross-domain requests](#cors-and-cookies): From 4a8819912900e8e7a01ffc72b5fa5147401609e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C5=9Eim=C5=9Fek?= Date: Sun, 17 Mar 2024 13:21:38 +0300 Subject: [PATCH 1559/2609] Fix typo for 'boostrap' --- errors.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/errors.md b/errors.md index ae553394d7f..547a6cd4a8c 100644 --- a/errors.md +++ b/errors.md @@ -157,7 +157,7 @@ To accomplish this, you may use the `level` exception method in your application ### Ignoring Exceptions by Type -When building your application, there will be some types of exceptions you never want to report. To ignore these exceptions, you may use the `dontReport` exception method in your application's `boostrap/app.php` file. Any class provided to this method will never be reported; however, they may still have custom rendering logic: +When building your application, there will be some types of exceptions you never want to report. To ignore these exceptions, you may use the `dontReport` exception method in your application's `bootstrap/app.php` file. Any class provided to this method will never be reported; however, they may still have custom rendering logic: use App\Exceptions\InvalidOrderException; @@ -167,7 +167,7 @@ When building your application, there will be some types of exceptions you never ]); }) -Internally, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP errors or 419 HTTP responses generated by invalid CSRF tokens. If you would like to instruct Laravel to stop ignoring a given type of exception, you may use the `stopIgnoring` exception method in your application's `boostrap/app.php` file: +Internally, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP errors or 419 HTTP responses generated by invalid CSRF tokens. If you would like to instruct Laravel to stop ignoring a given type of exception, you may use the `stopIgnoring` exception method in your application's `bootstrap/app.php` file: use Symfony\Component\HttpKernel\Exception\HttpException; @@ -178,7 +178,7 @@ Internally, Laravel already ignores some types of errors for you, such as except ### Rendering Exceptions -By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by using the `render` exception method in your application's `boostrap/app.php` file. +By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this by using the `render` exception method in your application's `bootstrap/app.php` file. The closure passed to the `render` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will determine what type of exception the closure renders by examining the type-hint of the closure: @@ -246,7 +246,7 @@ Rarely, you may need to customize the entire HTTP response rendered by Laravel's ### Reportable and Renderable Exceptions -Instead of defining custom reporting and rendering behavior in your application's `boostrap/app.php` file, you may define `report` and `render` methods directly on your application's exceptions. When these methods exist, they will automatically be called by the framework: +Instead of defining custom reporting and rendering behavior in your application's `bootstrap/app.php` file, you may define `report` and `render` methods directly on your application's exceptions. When these methods exist, they will automatically be called by the framework: Date: Mon, 18 Mar 2024 11:20:06 -0400 Subject: [PATCH 1560/2609] remove another duplicate section (#9515) 39cbb5dee15de938f269a674de650119be653806 introduced multiple duplicate sections. 0e27d90897987784eeb10a68cb566380e384666e fixed one of them. This fixes another --- broadcasting.md | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index b65e1a4cd5e..2854cb8c5c1 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -211,45 +211,6 @@ The `install:broadcasting` Artisan command automatically installs the `laravel-e npm install --save-dev laravel-echo pusher-js ``` -Once `laravel-echo` and `pusher-js` are installed, you are ready to create a fresh Echo instance in your application's JavaScript. The `install:broadcasting` Artisan command creates a `resources/js/echo.js` file that handles this for you: - -```js -import Echo from 'laravel-echo'; - -import Pusher from 'pusher-js'; -window.Pusher = Pusher; - -window.Echo = new Echo({ - broadcaster: 'reverb', - key: import.meta.env.VITE_REVERB_APP_KEY, - wsHost: import.meta.env.VITE_REVERB_HOST, - wsPort: import.meta.env.VITE_REVERB_PORT, - wssPort: import.meta.env.VITE_REVERB_PORT, - forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', - enabledTransports: ['ws', 'wss'], -}); -``` - -Next, you only need to compile your application's assets: - -```shell -npm run build -``` - -> [!NOTE] -> To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). - - -### Pusher Channels - -[Laravel Echo](https://github.com/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. Echo also leverages the `pusher-js` NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages. - -The `install:broadcasting` Artisan command automatically installs the `laravel-echo` and `pusher-js` packages for you; however, you may also install these packages manually via NPM: - -```shell -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. The `install:broadcasting` command creates an Echo configuration file at `resources/js/echo.js`; however, the default configuration in this file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Pusher: ```js From 63c2841f8e2e0e0fc015b69f563e76c3bad77e43 Mon Sep 17 00:00:00 2001 From: Pascal Baljet Date: Mon, 18 Mar 2024 16:23:36 +0100 Subject: [PATCH 1561/2609] Added upgrade note about `AuthenticationException::redirectTo()` (#9514) * Added upgrade note about `AuthenticationException::redirectTo()` * formatting --------- Co-authored-by: Taylor Otwell --- upgrade.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/upgrade.md b/upgrade.md index b5ef82e189c..7d38cd788a7 100644 --- a/upgrade.md +++ b/upgrade.md @@ -156,6 +156,20 @@ public function getAuthPasswordName() The default `User` model included with Laravel receives this method automatically since the method is included within the `Illuminate\Auth\Authenticatable` trait. + + +#### The `AuthenticationException` Class + +**Likelihood Of Impact: Very Low** + +The `redirectTo` method of the `Illuminate\Auth\AuthenticationException` class now requires an `Illuminate\Http\Request` instance as its first argument. If you are manually catching this exception and calling the `redirectTo` method, you should update your code accordingly: + +```php +if ($e instanceof AuthenticationException) { + $path = $e->redirectTo($request); +} +``` + ### Cache From a7b6ffc9e44a60670355b581db5ebc0001f5bd85 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 18 Mar 2024 15:29:26 +0000 Subject: [PATCH 1562/2609] Fixes bootstrap sentence (#9507) --- structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure.md b/structure.md index 5ba87f5441c..784cf1e953b 100644 --- a/structure.md +++ b/structure.md @@ -46,7 +46,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. You should not typically need to modify any files within this 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 Config Directory From 007a880fa791ac0d4a797e7c6b31394495173d05 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 18 Mar 2024 09:30:40 -0600 Subject: [PATCH 1563/2609] wip --- sanctum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index 3ea81b51ae1..c42e750f811 100644 --- a/sanctum.md +++ b/sanctum.md @@ -339,7 +339,7 @@ If your SPA needs to authenticate with [private / presence broadcast channels](/ ) ->withBroadcasting( __DIR__.'/../routes/channels.php', - ['middleware' => ['auth:sanctum']], + ['prefix' => 'api', 'middleware' => ['auth:sanctum']], ) Next, in order for Pusher's authorization requests to succeed, you will need to provide a custom Pusher `authorizer` when initializing [Laravel Echo](/docs/{{version}}/broadcasting#client-side-installation). This allows your application to configure Pusher to use the `axios` instance that is [properly configured for cross-domain requests](#cors-and-cookies): From 64a71f1beaf1287a4bfa063cbd066deb8397b4a5 Mon Sep 17 00:00:00 2001 From: Nikolay Nikolaev Date: Tue, 19 Mar 2024 22:00:02 +0200 Subject: [PATCH 1564/2609] Fix small typos in Sanctum (#9517) --- sanctum.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sanctum.md b/sanctum.md index c42e750f811..7f5e8677f91 100644 --- a/sanctum.md +++ b/sanctum.md @@ -35,7 +35,7 @@ Laravel Sanctum exists to solve two separate problems. Let's discuss each before #### API Tokens -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. +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 anytime. 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. @@ -173,14 +173,14 @@ For convenience, the `tokenCan` method will always return `true` if the incoming However, this does not necessarily mean that your application has to allow the user to perform the action. Typically, your application's [authorization policies](/docs/{{version}}/authorization#creating-policies) will determine if the token has been granted the permission to perform the abilities as well as check that the user instance itself should be allowed to perform the action. -For example, if we imagine an application that manages servers, this might mean checking that token is authorized to update servers **and** that the server belongs to the user: +For example, if we imagine an application that manages servers, this might mean checking that the token is authorized to update servers **and** that the server belongs to the user: ```php return $request->user()->id === $server->user_id && $request->user()->tokenCan('server:update') ``` -At first, allowing the `tokenCan` method to be called and always return `true` for first-party UI initiated requests may seem strange; however, it is convenient to be able to always assume an API token is available and can be inspected via the `tokenCan` method. By taking this approach, you may always call the `tokenCan` method within your application's authorizations policies without worrying about whether the request was triggered from your application's UI or was initiated by one of your API's third-party consumers. +At first, allowing the `tokenCan` method to be called and always return `true` for first-party UI initiated requests may seem strange; however, it is convenient to be able to always assume an API token is available and can be inspected via the `tokenCan` method. By taking this approach, you may always call the `tokenCan` method within your application's authorization policies without worrying about whether the request was triggered from your application's UI or was initiated by one of your API's third-party consumers. ### Protecting Routes @@ -226,7 +226,7 @@ return $user->createToken( )->plainTextToken; ``` -If you have configured a token expiration time for your application, you may also wish to [schedule a task](/docs/{{version}}/scheduling) to prune your application's expired tokens. Thankfully, Sanctum includes a `sanctum:prune-expired` Artisan command that you may use to accomplish this. For example, you may configure a scheduled tasks to delete all expired token database records that have been expired for at least 24 hours: +If you have configured a token expiration time for your application, you may also wish to [schedule a task](/docs/{{version}}/scheduling) to prune your application's expired tokens. Thankfully, Sanctum includes a `sanctum:prune-expired` Artisan command that you may use to accomplish this. For example, you may configure a scheduled task to delete all expired token database records that have been expired for at least 24 hours: ```php use Illuminate\Support\Facades\Schedule; @@ -311,7 +311,7 @@ Once CSRF protection has been initialized, you should make a `POST` request to y 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. -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. +Of course, if your user's session expires due to lack of activity, subsequent requests to the Laravel application may receive a 401 or 419 HTTP error response. In this case, you should redirect the user to your SPA's login page. > [!WARNING] > 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. @@ -319,7 +319,7 @@ Of course, if your user's session expires due to lack of activity, subsequent re ### 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 API routes within your `routes/api.php` file. This guard will ensure that incoming requests are authenticated as either 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; From 4a5c99bb538faa6649ef43e48901e3ab5c2f51ca Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 19 Mar 2024 20:01:46 +0000 Subject: [PATCH 1565/2609] Add "Laravel Reverb" to list of repositories in Contribution Guide (#9516) --- contributions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributions.md b/contributions.md index a5685b47a23..712e4a87c45 100644 --- a/contributions.md +++ b/contributions.md @@ -44,6 +44,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Pennant](https://github.com/laravel/pennant) - [Laravel Pint](https://github.com/laravel/pint) - [Laravel Prompts](https://github.com/laravel/prompts) +- [Laravel Reverb](https://github.com/laravel/reverb) - [Laravel Sail](https://github.com/laravel/sail) - [Laravel Sanctum](https://github.com/laravel/sanctum) - [Laravel Scout](https://github.com/laravel/scout) From cc80f1e87240d3348a851bb28da3cbb3382b1960 Mon Sep 17 00:00:00 2001 From: Bilfeldt Date: Tue, 19 Mar 2024 21:29:46 +0100 Subject: [PATCH 1566/2609] Document Arr::mapSpread (#9489) * Document Arr::mapSpread * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/helpers.md b/helpers.md index b438f67c930..3446e518818 100644 --- a/helpers.md +++ b/helpers.md @@ -55,6 +55,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::keyBy](#method-array-keyby) [Arr::last](#method-array-last) [Arr::map](#method-array-map) +[Arr::mapSpread](#method-array-map-spread) [Arr::mapWithKeys](#method-array-map-with-keys) [Arr::only](#method-array-only) [Arr::pluck](#method-array-pluck) @@ -552,6 +553,29 @@ The `Arr::map` method iterates through the array and passes each value and key t // ['first' => 'James', 'last' => 'Kirk'] + +#### `Arr::mapSpread()` {.collection-method} + +The `Arr::mapSpread` method iterates over the array, passing each nested item value into the given closure. The closure is free to modify the item and return it, thus forming a new array of modified items: + + use Illuminate\Support\Arr; + + $array = [ + [0, 1], + [2, 3], + [4, 5], + [6, 7], + [8, 9], + ]; + + $mapped = Arr::mapSpread($array, function (int $even, int $odd) { + return $even + $odd; + }); + + /* + [1, 5, 9, 13, 17] + */ + #### `Arr::mapWithKeys()` {.collection-method} From 4e841f2531c6f3c166c2b619a9eeb7c9d01e8fb4 Mon Sep 17 00:00:00 2001 From: JR Date: Wed, 20 Mar 2024 16:17:13 +0100 Subject: [PATCH 1567/2609] Change DATABASE_URL to DB_URL in database.md (#9521) With Laravel 11, the env var DATABASE_URL has changed. It has been replaced with DB_URL --- database.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.md b/database.md index c86488477e5..dd528b6546b 100644 --- a/database.md +++ b/database.md @@ -75,7 +75,7 @@ These URLs typically follow a standard schema convention: driver://username:password@host:port/database?options ``` -For convenience, Laravel supports these URLs as an alternative to configuring your database with multiple configuration options. If the `url` (or corresponding `DATABASE_URL` environment variable) configuration option is present, it will be used to extract the database connection and credential information. +For convenience, Laravel supports these URLs as an alternative to configuring your database with multiple configuration options. If the `url` (or corresponding `DB_URL` environment variable) configuration option is present, it will be used to extract the database connection and credential information. ### Read and Write Connections From 9e8f0abbd8162676f3457440ce9e738932a4be03 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 21 Mar 2024 02:27:17 +1100 Subject: [PATCH 1568/2609] [11.x] Context (#9499) * wip * wip * wip * wip * wip * wip * wip * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell --- context.md | 327 +++++++++++++++++++++++++++++++++++++++++++++++ documentation.md | 1 + 2 files changed, 328 insertions(+) create mode 100644 context.md diff --git a/context.md b/context.md new file mode 100644 index 00000000000..9ffbde48be4 --- /dev/null +++ b/context.md @@ -0,0 +1,327 @@ +# Context + +- [Introduction](#introduction) + - [How it Works](#how-it-works) +- [Capturing Context](#capturing-context) + - [Stacks](#stacks) +- [Retrieving Context](#retrieving-context) + - [Determining Item Existence](#determining-item-existence) +- [Removing Context](#removing-context) +- [Hidden Context](#hidden-context) +- [Events](#events) + - [Dehydrating](#dehydrating) + - [Hydrating](#hydrating) + + +## Introduction + +Laravel's "context" capabilities enable you to capture, retrieve, and share information throughout requests, jobs, and commands executing within your application. This captured information is also included in logs written by your application, giving you deeper insight into the surrounding code execution history that occurred before a log entry was written and allowing you to trace execution flows throughout a distributed system. + + +### How it Works + +The best way to understand Laravel's context capabilities is to see it in action using the built-in logging features. To get started, you may [add information to the context](#capturing-context) using the `Context` facade. In this example, we will use a [middleware](/docs/{{version}}/middleware) to add the request URL and a unique trace ID to the context on every incoming request: + +```php +url()); + Context::add('trace_id', Str::uuid()->toString()); + + return $next($request); + } +} +``` + +Information added to the context is automatically appended as metadata to any [log entries](/docs/{{version}}/logging) that are written throughout the request. Appending context as metadata allows information passed to individual log entries to be differentiated from the information shared via `Context`. For example, imagine we write the following log entry: + +```php +Log::info('User authenticated.', ['auth_id' => Auth::id()]); +``` + +The written log will contain the `auth_id` passed to the log entry, but it will also contain the context's `url` and `trace_id` as metadata: + +``` +User authenticated. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"} +``` + +Information added to the context is also made available to jobs dispatched to the queue. For example, imagine we dispatch a `ProcessPodcast` job to the queue after adding some information to the context: + +```php +// In our middleware... +Context::add('url', $request->url()); +Context::add('trace_id', Str::uuid()->toString()); + +// In our controller... +ProcessPodcast::dispatch($podcast); +``` + +When the job is dispatched, any information currently stored in the context is captured and shared with the job. The captured information is then hydrated back into the current context while the job is executing. So, if our job's handle method was to write to the log: + +```php +class ProcessPodcast implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + // ... + + /** + * Execute the job. + */ + public function handle(): void + { + Log::info('Processing podcast.', [ + 'podcast_id' => $this->podcast->id, + ]); + + // ... + } +} +``` + +The resulting log entry would contain the information that was added to the context during the request that originally dispatched the job: + +``` +Processing podcast. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"} +``` + +Although we have focused on the built-in logging related features of Laravel's context, the following documentation will illustrate how context allows you to share information across the HTTP request / queued job boundary and even how to add [hidden context data](#hidden-context) that is not written with log entries. + + +## Capturing Context + +You may store information in the current context using the `Context` facade's `add` method: + +```php +use Illuminate\Support\Facades\Context; + +Context::add('key', 'value'); +``` + +To add multiple items at once, you may pass an associative array to the `add` method: + +```php +Context::add([ + 'first_key' => 'value', + 'second_key' => 'value', +]); +``` + +The `add` method will override any existing value that shares the same key. If you only wish to add information to the context if the key does not already exist, you may use the `addIf` method: + +```php +Context::add('key', 'first'); + +Context::get('key'); +// "first" + +Context::addIf('key', 'second'); + +Context::get('key'); +// "first" +``` + + +### Stacks + +Context offers the ability to create "stacks", which are lists of data stored in the order that they where added. You can add information to a stack by invoking the `push` method: + +```php +use Illuminate\Support\Facades\Context; + +Context::push('breadcrumbs', 'first_value'); + +Context::push('breadcrumbs', 'second_value', 'third_value'); + +Context::get('breadcrumbs'); +// [ +// 'first_value', +// 'second_value', +// 'third_value', +// ] +``` + +Stacks can be useful to capture historical information about a request, such as events that are happening throughout your application. For example, you could create an event listener to push to a stack every time a query is executed, capturing the query SQL and duration as a tuple: + +```php +use Illuminate\Support\Facades\Context; +use Illuminate\Support\Facades\DB; + +DB::listen(function ($event) { + Context::push('queries', [$event->time, $event->sql]); +}); +``` + + +## Retrieving Context + +You may retrieve information from the context using the `Context` facade's `get` method: + +```php +use Illuminate\Support\Facades\Context; + +$value = Context::get('key'); +``` + +The `only` method may be used to retrieve a subset of the information in the context: + +```php +$data = Context::only(['first_key', 'second_key']); +``` + +If you would like to retrieve all of the information stored in the context, you may invoke the `all` method: + +```php +$data = Context::all(); +``` + + +### Determining Item Existence + +You may use the `has` method to determine if the context has any value stored for the given key: + +```php +use Illuminate\Support\Facades\Context; + +if (Context::has('key')) { + // ... +} +``` + +The `has` method will return `true` regardless of the value stored. So, for example, a key with a `null` value will be considered present: + +```php +Context::add('key', null); + +Context::has('key'); +// true +``` + + +## Removing Context + +The `forget` method may be used to remove a key and its value from the current context: + +```php +use Illuminate\Support\Facades\Context; + +Context::add(['first_key' => 1, 'second_key' => 2]); + +Context::forget('first_key'); + +Context::all(); + +// ['second_key' => 2] +``` + +You may forget several keys at once by providing an array to the `forget` method: + +```php +Context::forget(['first_key', 'second_key']); +``` + + +## Hidden Context + +Context offers the ability to store "hidden" data. This hidden information is not appended to logs, and is not accessible via the data retrieval methods documented above. Context provides a different set of methods to interact with hidden context information: + +```php +use Illuminate\Support\Facades\Context; + +Context::addHidden('key', 'value'); + +Context::getHidden('key'); +// 'value' + +Context::get('key'); +// null +``` + +The "hidden" methods mirror the functionality of the non-hidden methods documented above: + +```php +Context::addHidden(/* ... */); +Context::addHiddenIf(/* ... */); +Context::pushHidden(/* ... */); +Context::getHidden(/* ... */); +Context::onlyHidden(/* ... */); +Context::allHidden(/* ... */); +Context::hasHidden(/* ... */); +Context::forgetHidden(/* ... */); +``` + + +## Events + +Context dispatches two events that allow you to hook into the dehydrating and hydrating process of the context. + +To illustrate how these events may be used, imagine that in a middleware of your application you set the `app.locale` configuration value based on the incoming HTTP request's `Accept-Language` header. Context's events allow you to capture this value during the request and restore it on the queue, ensuring notifications sent on the queue have the correct `app.locale` value. We can use context's events and [hidden](#hidden-context) data to achieve this, which the following documentation will illustrate. + + +### Dehydrating + +Whenever a job is dispatched to the queue the data in the context is "dehydrated" and captured alongside the job's payload. The `Context::dehydrating` method allows you to register a closure that will be invoked during the dehydration process. Within this closure, you may make changes to the data that will be shared with the queued job. + +Typically, you should register `dehydrating` callbacks within the `boot` method of your application's `AppServiceProvider` class: + +```php +use Illuminate\Log\Context\Repository; +use Illuminate\Support\Facades\Config; +use Illuminate\Support\Facades\Context; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Context::dehydrating(function (Repository $context) { + $context->addHidden('locale', Config::get('app.locale')); + }); +} +``` + +> [!NOTE] +> You should not use the `Context` facade within the `dehydrating` callback, as that will change the context of the current process. Ensure you only make changes to the repository passed to the callback. + + +### Hydrating + +Whenever a queued job begins executing on the queue, any context that was shared with the job will be "hydrated" back into the current context. The `Context::hydrating` method allows you to register a closure that will be invoked during the hydration process. + +Typically, you should register `hydrating` callbacks within the `boot` method of your application's `AppServiceProvider` class: + +```php +use Illuminate\Log\Context\Repository; +use Illuminate\Support\Facades\Config; +use Illuminate\Support\Facades\Context; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Context::hydrating(function (Repository $context) { + if ($context->hasHidden('locale')) { + Config::set('app.locale', $context->getHidden('locale')); + } + }); +} +``` + +> [!NOTE] +> You should not use the `Context` facade within the `hydrating` callback and instead ensure you only make changes to the repository passed to the callback. diff --git a/documentation.md b/documentation.md index 2447a08eab7..a120930b14b 100644 --- a/documentation.md +++ b/documentation.md @@ -34,6 +34,7 @@ - [Broadcasting](/docs/{{version}}/broadcasting) - [Cache](/docs/{{version}}/cache) - [Collections](/docs/{{version}}/collections) + - [Context](/docs/{{version}}/context) - [Contracts](/docs/{{version}}/contracts) - [Events](/docs/{{version}}/events) - [File Storage](/docs/{{version}}/filesystem) From 6b12e7e8f9ccd1733651b6e3fe7ac9ed6451c653 Mon Sep 17 00:00:00 2001 From: Andy Hinkle Date: Wed, 20 Mar 2024 10:27:53 -0500 Subject: [PATCH 1569/2609] typo on test example (#9519) --- http-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index 20c3b122ca0..a852528720a 100644 --- a/http-tests.md +++ b/http-tests.md @@ -106,7 +106,7 @@ You may use the `withHeaders` method to customize the request's headers before i ```php tab=Pest withHeaders([ 'X-Header' => 'Value', ])->post('/user', ['name' => 'Sally']); From 0c42c5992a51e26ecc23072f11cce44de2b64f70 Mon Sep 17 00:00:00 2001 From: Andrea De Luca <53230888+se09deluca@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:08:08 +0100 Subject: [PATCH 1570/2609] Adds octane in composer dependency update list (#9520) * docs: adds octane in composer dependency update list Support for Laravel 11 has been released in version 2.3.0. https://github.com/laravel/octane/releases/tag/v2.3.0 * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index 7d38cd788a7..8a001ebb627 100644 --- a/upgrade.md +++ b/upgrade.md @@ -72,6 +72,7 @@ You should update the following dependencies in your application's `composer.jso - `laravel/cashier` to `^15.0` (If installed) - `laravel/dusk` to `^8.0` (If installed) - `laravel/jetstream` to `^5.0` (If installed) +- `laravel/octane` to `^2.3` (If installed) - `laravel/passport` to `^12.0` (If installed) - `laravel/sanctum` to `^4.0` (If installed) - `laravel/spark-stripe` to `^5.0` (If installed) From c2dd8fe627ff1b7578fb5aecfbf1f5c73797a167 Mon Sep 17 00:00:00 2001 From: Richard Browne Date: Wed, 20 Mar 2024 20:15:11 +0000 Subject: [PATCH 1571/2609] Add reference to the new helper method `sreenshotElement` (#9518) * Add reference to the new helper method `sreenshotElement` * Update dusk.md --------- Co-authored-by: Taylor Otwell --- dusk.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dusk.md b/dusk.md index 83302eb8d0c..84405b767b8 100644 --- a/dusk.md +++ b/dusk.md @@ -553,6 +553,10 @@ The `responsiveScreenshots` method may be used to take a series of screenshots a $browser->responsiveScreenshots('filename'); +The `screenshotElement` method may be used to take a screenshot of a specific element on the page: + + $browser->screenshotElement('#selector', 'filename'); + ### Storing Console Output to Disk From 176005f5c8f4ae09759618cebfaf5c017d05eae5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 21 Mar 2024 12:39:14 -0600 Subject: [PATCH 1572/2609] rebuild docs --- sail.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sail.md b/sail.md index 58a363c6339..13b0969eb87 100644 --- a/sail.md +++ b/sail.md @@ -3,6 +3,7 @@ - [Introduction](#introduction) - [Installation and Setup](#installation) - [Installing Sail Into Existing Applications](#installing-sail-into-existing-applications) + - [Rebuilding Sail Images](#rebuilding-sail-images) - [Configuring A Shell Alias](#configuring-a-shell-alias) - [Starting and Stopping Sail](#starting-and-stopping-sail) - [Executing Commands](#executing-sail-commands) @@ -84,6 +85,19 @@ If you would like to develop within a [Devcontainer](https://code.visualstudio.c php artisan sail:install --devcontainer ``` + +### Rebuilding Sail Images + +Sometimes you may want to completely rebuild your Sail images to ensure all of the image's packages and software is up to date. You may accomplish this using the `build` command: + +```shell +docker compose down -v + +sail build --no-cache + +sail up +``` + ### Configuring A Shell Alias From 0008c6907b5bbac424582154ef17ac475b2a6215 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 21 Mar 2024 12:39:30 -0600 Subject: [PATCH 1573/2609] wip --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 13b0969eb87..a2a87744b2b 100644 --- a/sail.md +++ b/sail.md @@ -88,7 +88,7 @@ php artisan sail:install --devcontainer ### Rebuilding Sail Images -Sometimes you may want to completely rebuild your Sail images to ensure all of the image's packages and software is up to date. You may accomplish this using the `build` command: +Sometimes you may want to completely rebuild your Sail images to ensure all of the image's packages and software are up to date. You may accomplish this using the `build` command: ```shell docker compose down -v From 0c8838c2148fb25718b7a89cb3242cdbaaf04749 Mon Sep 17 00:00:00 2001 From: Joel Clermont Date: Fri, 22 Mar 2024 13:33:03 -0500 Subject: [PATCH 1574/2609] Correct Papertrail env key names (#9528) --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index da1fe24bba0..2ea1ff8e682 100644 --- a/logging.md +++ b/logging.md @@ -100,7 +100,7 @@ Name | Description | Defau #### Configuring the Papertrail Channel -The `papertrail` channel requires `host` and `port` configuration options. These may be defined via the `LOG_PAPERTRAIL_URL` and `LOG_PAPERTRAIL_PORT` environment variables. You can obtain these values from [Papertrail](https://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-php-apps/#send-events-from-php-app). +The `papertrail` channel requires `host` and `port` configuration options. These may be defined via the `PAPERTRAIL_URL` and `PAPERTRAIL_PORT` environment variables. You can obtain these values from [Papertrail](https://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-php-apps/#send-events-from-php-app). #### Configuring the Slack Channel From b16e67c6a3fcac0f876cb523fc26327dfa7541da Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Sat, 23 Mar 2024 01:04:55 +0600 Subject: [PATCH 1575/2609] [11.x] Document ListManagementOptions in SES mail driver (#9525) * Document ListManagementOptions in SES mail driver * formatting --------- Co-authored-by: Taylor Otwell --- mail.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mail.md b/mail.md index 7b572f0652d..7bd51b3741b 100644 --- a/mail.md +++ b/mail.md @@ -139,6 +139,22 @@ To utilize AWS [temporary credentials](https://docs.aws.amazon.com/IAM/latest/Us 'token' => env('AWS_SESSION_TOKEN'), ], +To interact with SES's [subscription management features](https://docs.aws.amazon.com/ses/latest/dg/sending-email-subscription-management.html), you may return the `X-Ses-List-Management-Options` header in the array returned by the [`headers`](#headers) method of a mail message: + +```php +/** + * Get the message headers. + */ +public function headers(): Headers +{ + return new Headers( + text: [ + 'X-Ses-List-Management-Options' => 'contactListName=MyContactList;topicName=MyTopic', + ], + ); +} +``` + If you would like to define [additional options](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sesv2-2019-09-27.html#sendemail) that Laravel should pass to the AWS SDK's `SendEmail` method when sending an email, you may define an `options` array within your `ses` configuration: 'ses' => [ From fd9f558677b1ed3460047e0a5e03c17e29052383 Mon Sep 17 00:00:00 2001 From: Darren Taylor Date: Sun, 24 Mar 2024 15:57:39 +0100 Subject: [PATCH 1576/2609] Change deterministic header name (#9530) --- errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors.md b/errors.md index 547a6cd4a8c..b2a11adf98b 100644 --- a/errors.md +++ b/errors.md @@ -209,7 +209,7 @@ You may also use the `render` method to override the rendering behavior for buil #### Rendering Exceptions as JSON -When rendering an exception, Laravel will automatically determine if the exception should be rendered as an HTML or JSON response based on the `Content-Type` header of the request. If you would like to customize how Laravel determines whether to render HTML or JSON exception responses, you may utilize the `shouldRenderJsonWhen` method: +When rendering an exception, Laravel will automatically determine if the exception should be rendered as an HTML or JSON response based on the `Accept` header of the request. If you would like to customize how Laravel determines whether to render HTML or JSON exception responses, you may utilize the `shouldRenderJsonWhen` method: use Illuminate\Http\Request; use Throwable; From df33e6150a5e16d5a1aac81bed024b5269523116 Mon Sep 17 00:00:00 2001 From: Mohamed Ilies <35309918+medilies@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:01:09 +0100 Subject: [PATCH 1577/2609] [11.x] Document the list rule (#9529) * Document the list rule * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation.md b/validation.md index a1aaa7b3681..09600ce4e2e 100644 --- a/validation.md +++ b/validation.md @@ -916,6 +916,7 @@ Below is a list of all available validation rules and their function: [JSON](#rule-json) [Less Than](#rule-lt) [Less Than Or Equal](#rule-lte) +[List](#rule-list) [Lowercase](#rule-lowercase) [MAC Address](#rule-mac) [Max](#rule-max) @@ -1471,6 +1472,11 @@ The field under validation must be less than or equal to the given _field_. The The field under validation must be lowercase. + +#### list + +The field under validation must be an array that is a list. An array is considered a list if its keys consist of consecutive numbers from 0 to `count($array) - 1`. + #### mac_address From 2c7f7d13d2993511fc6a1dd720cab60d0fb8e73d Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 25 Mar 2024 17:58:09 +0100 Subject: [PATCH 1578/2609] [11.x] Add MariaDB to supported DB engine list (#9532) * Add MariaDB to supported DB engine list * Update pulse.md --------- Co-authored-by: Taylor Otwell --- pulse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pulse.md b/pulse.md index 40cc7333e06..79f787a71a5 100644 --- a/pulse.md +++ b/pulse.md @@ -33,7 +33,7 @@ For in-depth debugging of individual events, check out [Laravel Telescope](/docs ## Installation > [!WARNING] -> Pulse's first-party storage implementation currently requires a MySQL or PostgreSQL database. If you are using a different database engine, you will need a separate MySQL or PostgreSQL database for your Pulse data. +> Pulse's first-party storage implementation currently requires a MySQL, MariaDB, or PostgreSQL database. If you are using a different database engine, you will need a separate MySQL, MariaDB, or PostgreSQL database for your Pulse data. Since Pulse is currently in beta, you may need to adjust your application's `composer.json` file to allow beta package releases to be installed: From 1a0274085049801e3933b3b4b75c08e3d4e27eef Mon Sep 17 00:00:00 2001 From: Talha Mujahid Date: Mon, 25 Mar 2024 09:59:28 -0700 Subject: [PATCH 1579/2609] update providers for L11 (#9531) * update providers for L11 * Update structure.md --------- Co-authored-by: Taylor Otwell --- structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure.md b/structure.md index 784cf1e953b..322c2712fd8 100644 --- a/structure.md +++ b/structure.md @@ -172,7 +172,7 @@ This directory does not exist by default, but will be created for you if you exe The `Providers` directory contains all of the [service providers](/docs/{{version}}/providers) for your application. Service providers bootstrap your application by binding services in the service container, registering events, or performing any other tasks to prepare your application for incoming requests. -In a fresh Laravel application, this directory will already contain several providers. You are free to add your own providers to this directory as needed. +In a fresh Laravel application, this directory will already contain the `AppServiceProvider`. You are free to add your own providers to this directory as needed. #### The Rules Directory From a1c1cdfe15e5cbb715a97b67397b68d48a8a391e Mon Sep 17 00:00:00 2001 From: Sergey Danilchenko Date: Tue, 26 Mar 2024 15:32:39 +0200 Subject: [PATCH 1580/2609] [11.x] cURL minimum version and workarounds (#9533) * [11.x] cURL minimum version and workarounds * Update upgrade.md --------- Co-authored-by: Sergey Danilchenko Co-authored-by: Taylor Otwell --- upgrade.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/upgrade.md b/upgrade.md index 8a001ebb627..064f853be10 100644 --- a/upgrade.md +++ b/upgrade.md @@ -60,6 +60,10 @@ Laravel now requires PHP 8.2.0 or greater. +#### curl 7.34.0 Required + +Laravel's HTTP client now requires curl 7.34.0 or greater. + #### Composer Dependencies You should update the following dependencies in your application's `composer.json` file: From f95ebd2b67649a1c326e045be295ac3310d71c60 Mon Sep 17 00:00:00 2001 From: Ashley Shenton Date: Tue, 26 Mar 2024 13:39:10 +0000 Subject: [PATCH 1581/2609] [11.x] Document all Pennant events (#9526) * docs: restore correct pennant event names with current syntax * docs: add missing pennant events * Formatting * formatting --------- Co-authored-by: Tim MacDonald Co-authored-by: Taylor Otwell --- pennant.md | 63 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/pennant.md b/pennant.md index 2738ee2a64f..1669713113b 100644 --- a/pennant.md +++ b/pennant.md @@ -985,15 +985,17 @@ Once the driver has been registered, you may use the `redis` driver in your appl Pennant dispatches a variety of events that can be useful when tracking feature flags throughout your application. -### `Laravel\Pennant\Events\RetrievingKnownFeature` +### `Laravel\Pennant\Events\FeatureRetrieved` -This event is dispatched the first time a known feature is retrieved during a request for a specific scope. This event can be useful to create and track metrics against the feature flags that are being used throughout your application. +This event is dispatched whenever a [feature is checked](#checking-features). This event may be useful for creating and tracking metrics against a feature flag's usage throughout your application. -### `Laravel\Pennant\Events\RetrievingUnknownFeature` +### `Laravel\Pennant\Events\FeatureResolved` -This event is dispatched the first time an unknown feature is retrieved during a request for a specific scope. This event can be useful if you have intended to remove a feature flag, but may have accidentally left some stray references to it throughout your application. +This event is dispatched the first time a feature's value is resolved for a specific scope. -For example, you may find it useful to listen for this event and `report` or throw an exception when it occurs: +### `Laravel\Pennant\Events\UnknownFeatureResolved` + +This event is dispatched the first time an unknown feature is resolved for a specific scope. Listening to this event may be useful if you have intended to remove a feature flag but have accidentally left stray references to it throughout your application: ```php feature}]."); + Event::listen(function (UnknownFeatureResolved $event) { + Log::error("Resolving unknown feature [{$event->feature}]."); }); } } ``` -### `Laravel\Pennant\Events\DynamicallyDefiningFeature` +### `Laravel\Pennant\Events\DynamicallyRegisteringFeatureClass` + +This event is dispatched when a [class based feature](#class-based-features) is dynamically checked for the first time during a request. + +### `Laravel\Pennant\Events\UnexpectedNullScopeEncountered` + +This event is dispatched when a `null` scope is passed to a feature definition that [doesn't support null](#nullable-scope). + +This situation is handled gracefully and the feature will return `false`. However, if you would like to opt out of this feature's default graceful behavior, you may register a listener for this event in the `boot` method of your application's `AppServiceProvider`: + +```php +use Illuminate\Support\Facades\Log; +use Laravel\Pennant\Events\UnexpectedNullScopeEncountered; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Event::listen(UnexpectedNullScopeEncountered::class, fn () => abort(500)); +} + +``` + +### `Laravel\Pennant\Events\FeatureUpdated` + +This event is dispatched when updating a feature for a scope, usually by calling `activate` or `deactivate`. + +### `Laravel\Pennant\Events\FeatureUpdatedForAllScopes` + +This event is dispatched when updating a feature for all scopes, usually by calling `activateForEveryone` or `deactivateForEveryone`. + +### `Laravel\Pennant\Events\FeatureDeleted` + +This event is dispatched when deleting a feature for a scope, usually by calling `forget`. + +### `Laravel\Pennant\Events\FeaturesPurged` + +This event is dispatched when purging specific features. + +### `Laravel\Pennant\Events\AllFeaturesPurged` -This event is dispatched when a class based feature is being dynamically checked for the first time during a request. +This event is dispatched when purging all features. From f42fff8056e34fad51df3f54869cf7327a175190 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 26 Mar 2024 10:57:08 -0500 Subject: [PATCH 1582/2609] wip --- installation.md | 2 +- valet.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/installation.md b/installation.md index e20a49e7c90..2d83cc20832 100644 --- a/installation.md +++ b/installation.md @@ -53,7 +53,7 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ## Creating a Laravel Project -Before creating your first Laravel project, make sure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS, PHP and Composer can be installed in minutes via [Laravel Herd](https://herd.laravel.com). In addition, we recommend [installing Node and NPM](https://nodejs.org). +Before creating your first Laravel project, make sure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS or Windows, PHP and Composer can be installed in minutes via [Laravel Herd](https://herd.laravel.com). In addition, we recommend [installing Node and NPM](https://nodejs.org). After you have installed PHP and Composer, you may create a new Laravel project via Composer's `create-project` command: diff --git a/valet.md b/valet.md index 9750fed374f..1676404a460 100644 --- a/valet.md +++ b/valet.md @@ -23,7 +23,7 @@ ## Introduction > [!NOTE] -> Looking for an even easier way to develop Laravel applications on macOS? Check out [Laravel Herd](https://herd.laravel.com). Herd includes everything you need to get started with Laravel development, including Valet, PHP, and Composer. +> Looking for an even easier way to develop Laravel applications on macOS or Windows? Check out [Laravel Herd](https://herd.laravel.com). Herd includes everything you need to get started with Laravel development, including Valet, PHP, and Composer. [Laravel Valet](https://github.com/laravel/valet) is a 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. From 1e7e43c5f50d60f0464e217124fa1b018c5773fd Mon Sep 17 00:00:00 2001 From: Vishal Gadhiya <53233027+vishal-gadhiya@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:25:53 +0530 Subject: [PATCH 1583/2609] Update throttle method invalid param (#9537) --- errors.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/errors.md b/errors.md index b2a11adf98b..038caec7b8e 100644 --- a/errors.md +++ b/errors.md @@ -321,7 +321,7 @@ To take a random sample rate of exceptions, you may use the `throttle` exception use Throwable; ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable Throwable) { + $exceptions->throttle(function (Throwable $e) { return Lottery::odds(1, 1000); }); }) @@ -333,7 +333,7 @@ It is also possible to conditionally sample based on the exception type. If you use Throwable; ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable Throwable) { + $exceptions->throttle(function (Throwable $e) { if ($e instanceof ApiMonitoringException) { return Lottery::odds(1, 1000); } @@ -347,7 +347,7 @@ You may also rate limit exceptions logged or sent to an external error tracking use Throwable; ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable Throwable) { + $exceptions->throttle(function (Throwable $e) { if ($e instanceof BroadcastException) { return Limit::perMinute(300); } @@ -361,7 +361,7 @@ By default, limits will use the exception's class as the rate limit key. You can use Throwable; ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable Throwable) { + $exceptions->throttle(function (Throwable $e) { if ($e instanceof BroadcastException) { return Limit::perMinute(300)->by($e->getMessage()); } @@ -378,7 +378,7 @@ Of course, you may return a mixture of `Lottery` and `Limit` instances for diffe use Throwable; ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable Throwable) { + $exceptions->throttle(function (Throwable $e) { return match (true) { $e instanceof BroadcastException => Limit::perMinute(300), $e instanceof ApiMonitoringException => Lottery::odds(1, 1000), From 47013efeb45692da0f9b6018f7db63e15d537521 Mon Sep 17 00:00:00 2001 From: MHO <17177411+mho22@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:58:06 +0100 Subject: [PATCH 1584/2609] Modified withCommands DIR path (#9540) --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index 31dbe1f4e97..201e20de4be 100644 --- a/artisan.md +++ b/artisan.md @@ -645,7 +645,7 @@ Sometimes, you may need more manual control over how a progress bar is advanced. By default, Laravel automatically registers all commands within the `app/Console/Commands` directory. However, you can instruct Laravel to scan other directories for Artisan commands using the `withCommands` method in your application's `bootstrap/app.php` file: ->withCommands([ - __DIR__.'../app/Domain/Orders/Commands', + __DIR__.'/../app/Domain/Orders/Commands', ]) If necessary, you may also manually register commands by providing the command's class name to the `withCommands` method: From 1b2fde410b26def379b90defa484aa687748a7aa Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Wed, 27 Mar 2024 19:40:22 +0000 Subject: [PATCH 1585/2609] [11.x] Add "aws/aws-sdk-php" install step for DynamoDB cache setup (#9536) * Add "aws/aws-sdk-php" install step for DynamoDB cache setup. * formatting --------- Co-authored-by: Taylor Otwell --- cache.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cache.md b/cache.md index f5e1940e335..701461a267b 100644 --- a/cache.md +++ b/cache.md @@ -90,6 +90,25 @@ Before using the [DynamoDB](https://aws.amazon.com/dynamodb) cache driver, you m This table should also have a string partition key with a name that corresponds to the value of the `stores.dynamodb.attributes.key` configuration item within your application's `cache` configuration file. By default, the partition key should be named `key`. +Next, install the AWS SDK so that your Laravel application can communicate with DynamoDB: + +```shell +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 +'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), +], +``` + ## Cache Usage From a5b64ac5c1194ea57268e67e130c77f1459eec38 Mon Sep 17 00:00:00 2001 From: Stephen Rees-Carter Date: Thu, 28 Mar 2024 05:49:40 +1000 Subject: [PATCH 1586/2609] Add Automatic Rehashing and Mixed Hash verification (#9538) * Add Automatic Rehashing and Mixed Hash verification * formatting --------- Co-authored-by: Taylor Otwell --- authentication.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/authentication.md b/authentication.md index 0ce8012f3f2..dcdbff7f4d0 100644 --- a/authentication.md +++ b/authentication.md @@ -25,6 +25,7 @@ - [Adding Custom User Providers](#adding-custom-user-providers) - [The User Provider Contract](#the-user-provider-contract) - [The Authenticatable Contract](#the-authenticatable-contract) +- [Automatic Password Rehashing](#automatic-password-rehashing) - [Social Authentication](/docs/{{version}}/socialite) - [Events](#events) @@ -716,6 +717,25 @@ This interface is simple. The `getAuthIdentifierName` method should return the n This interface allows the authentication system to work with any "user" class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes an `App\Models\User` class in the `app/Models` directory which implements this interface. + +## Automatic Password Rehashing + +Laravel's default password hashing algorithm is bcrypt. The "work factor" for bcrypt hashes can be adjusted via your application's `config/hashing.php` configuration file or the `BCRYPT_ROUNDS` environment variable. + +Typically, the bcrypt work factor should be increased over time as CPU / GPU processing power increases. If you increase the bcrypt work factor for your application, Laravel will gracefully and automatically rehash user passwords as users authenticate with your application via Laravel's starter kits or when you [manually authenticate users](#authenticating-users) via the `attempt` method. + +Typically, automatic password rehashing should not disrupt your application; however, you may disable this behavior by publishing the `hashing` configuration file: + +```shell +php artisan config:publish hashing +``` + +Once the configuration file has been published, you may set the `rehash_on_login` configuration value to `false`: + +```php +'rehash_on_login' => false, +``` + ## Events From e55e751e9b3cd52b867ac590c3089e6fd1b21e2c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 28 Mar 2024 15:30:17 +0000 Subject: [PATCH 1587/2609] [11.x] Documents `withSchedule` (#9542) * Documents `withSchedule` * Fixes typo * Update scheduling.md * Update scheduling.md * Update scheduling.md * Update scheduling.md * Update scheduling.md --------- Co-authored-by: Taylor Otwell --- scheduling.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scheduling.md b/scheduling.md index 147547f76c0..ec35a5bc564 100644 --- a/scheduling.md +++ b/scheduling.md @@ -43,6 +43,14 @@ In addition to scheduling using closures, you may also schedule [invokable objec Schedule::call(new DeleteRecentUsers)->daily(); +If you prefer to reserve your `routes/console.php` file for command definitions only, you may use the `withSchedule` method in your application's `bootstrap/app.php` file to define your scheduled tasks. This method accepts a closure that receives an instance of the scheduler: + + use Illuminate\Console\Scheduling\Schedule; + + ->withSchedule(function (Schedule $schedule) { + $schedule->call(new DeleteRecentUsers)->daily(); + }) + If you would like to view an overview of your scheduled tasks and the next time they are scheduled to run, you may use the `schedule:list` Artisan command: ```bash From d64b900d275a5b9b7ae451c9f36658d3355d7dd4 Mon Sep 17 00:00:00 2001 From: Md Abul Hassan <142471724+imabulhasan99@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:13:20 +0600 Subject: [PATCH 1588/2609] update Exceptions directory descriptions (#9545) --- structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure.md b/structure.md index 322c2712fd8..8e601d47dfc 100644 --- a/structure.md +++ b/structure.md @@ -130,7 +130,7 @@ This directory does not exist by default, but will be created for you by the `ev #### The Exceptions Directory -The `Exceptions` directory contains your application's exception handler and is also a good place to place any exceptions thrown by your application. If you would like to customize how your exceptions are logged or rendered, you should modify the `Handler` class in this directory. +The `Exceptions` directory contains all of the custom exceptions for your application. These exceptions may be generated using the `make:exception` command. #### The Http Directory From acb24cafa93b72d5e22977815a753df4f438f3ed Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Fri, 29 Mar 2024 10:30:14 -0400 Subject: [PATCH 1589/2609] Remove dead link (#9543) --- broadcasting.md | 1 - 1 file changed, 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 2854cb8c5c1..8c476358cc1 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -19,7 +19,6 @@ - [Broadcast Conditions](#broadcast-conditions) - [Broadcasting and Database Transactions](#broadcasting-and-database-transactions) - [Authorizing Channels](#authorizing-channels) - - [Defining Authorization Routes](#defining-authorization-routes) - [Defining Authorization Callbacks](#defining-authorization-callbacks) - [Defining Channel Classes](#defining-channel-classes) - [Broadcasting Events](#broadcasting-events) From 1b0efc2e03b470a3e8f5b95c43475caa9b2f469b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 31 Mar 2024 19:31:34 -0500 Subject: [PATCH 1590/2609] wip --- documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation.md b/documentation.md index a120930b14b..2ea03e6a683 100644 --- a/documentation.md +++ b/documentation.md @@ -104,4 +104,4 @@ - [Socialite](/docs/{{version}}/socialite) - [Telescope](/docs/{{version}}/telescope) - [Valet](/docs/{{version}}/valet) -- [API Documentation](/api/master) +- [API Documentation](/api/11.x) From 302303f8a4c3cefeb66de6c6d1e5b0dcc2e65ae2 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 1 Apr 2024 04:09:08 +0330 Subject: [PATCH 1591/2609] [11.x] Add Conditional Context to context (#9547) * Update context.md * Update context.md * Update context.md * Update context.md --------- Co-authored-by: Taylor Otwell --- context.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/context.md b/context.md index 9ffbde48be4..3eb26590d85 100644 --- a/context.md +++ b/context.md @@ -136,6 +136,22 @@ Context::get('key'); // "first" ``` + +#### Conditional Context + +The `when` method may be used to add data to the context based on a given condition. The first closure provided to the `when` method will be invoked if the given condition evaluates to `true`, while the second closure will be invoked if the condition evaluates to `false`: + +```php +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Context; + +Context::when( + Auth::user()->isAdmin(), + fn ($context) => $context->add('permissions', Auth::user()->permissions), + fn ($context) => $context->add('permissions', []), +); +``` + ### Stacks From 7ae837a04acf2154a762a4dcd2198b0b1c1e981a Mon Sep 17 00:00:00 2001 From: Hridoy Roy Date: Mon, 1 Apr 2024 19:41:08 +0600 Subject: [PATCH 1592/2609] modify-example-of-db-connection-name (#9549) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 8da638cc5aa..0a5cd0d4135 100644 --- a/eloquent.md +++ b/eloquent.md @@ -333,7 +333,7 @@ By default, all Eloquent models will use the default database connection that is * * @var string */ - protected $connection = 'sqlite'; + protected $connection = 'mysql'; } From 6468971aa4c589a828f34eb13ada6a36d884a31c Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 2 Apr 2024 14:43:30 +0100 Subject: [PATCH 1593/2609] [11.x] Documents `doesntExpectOutput` (#9541) * Documents `doesntExpectOutput` * Update console-tests.md * Update console-tests.md --------- Co-authored-by: Taylor Otwell --- console-tests.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/console-tests.md b/console-tests.md index 98c8f59a601..8ae16b7ceef 100644 --- a/console-tests.md +++ b/console-tests.md @@ -90,6 +90,28 @@ public function test_console_command(): void } ``` +You may also assert that a console command does not generate any output using the `doesntExpectOutput` method: + +```php tab=Pest +test('console command', function () { + $this->artisan('example') + ->doesntExpectOutput() + ->assertExitCode(0); +}); +``` + +```php tab=PHPUnit +/** + * Test a console command. + */ +public function test_console_command(): void +{ + $this->artisan('example') + ->doesntExpectOutput() + ->assertExitCode(0); +} +``` + #### Confirmation Expectations From 79c1572fbd7c1c42701ddc5052507fa839a4e764 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 2 Apr 2024 08:48:02 -0500 Subject: [PATCH 1594/2609] wip --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 3446e518818..23cd72599c4 100644 --- a/helpers.md +++ b/helpers.md @@ -155,6 +155,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [class_uses_recursive](#method-class-uses-recursive) [collect](#method-collect) [config](#method-config) +[context](#method-context) [cookie](#method-cookie) [csrf_field](#method-csrf-field) [csrf_token](#method-csrf-token) @@ -1706,6 +1707,21 @@ You may set configuration variables at runtime by passing an array of key / valu config(['app.debug' => true]); + +#### `context()` {.collection-method} + +The `context` function gets the value from the [current context](/docs/{{version}}/context). A default value may be specified and is returned if the context key does not exist: + + $value = context('trace_id'); + + $value = config('trace_id', $default); + +You may set context values by passing an array of key / value pairs: + + use Illuminate\Support\Str; + + context(['trace_id' => Str::uuid()->toString()]); + #### `cookie()` {.collection-method} From 8b31be3964737a4c3961652b1f60b81c17fbf96f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 2 Apr 2024 08:51:00 -0500 Subject: [PATCH 1595/2609] wip --- strings.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/strings.md b/strings.md index a86e9fd0a87..a6427439a99 100644 --- a/strings.md +++ b/strings.md @@ -97,6 +97,9 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::title](#method-title-case) [Str::toBase64](#method-str-to-base64) [Str::toHtmlString](#method-str-to-html-string) +[Str::trim](#method-str-trim) +[Str::ltrim](#method-str-ltrim) +[Str::rtrim](#method-str-rtrim) [Str::ucfirst](#method-str-ucfirst) [Str::ucsplit](#method-str-ucsplit) [Str::upper](#method-str-upper) @@ -1207,6 +1210,39 @@ The `Str::toHtmlString` method converts the string instance to an instance of `I $htmlString = Str::of('Nuno Maduro')->toHtmlString(); + +#### `Str::trim()` {.collection-method} + +The `Str::trim` method strips whitespace (or other characters) from the beginning and end of the given string. Unlike PHP's native `trim` function, the `Str::trim` method also removes unicode whitespace characters: + + use Illuminate\Support\Str; + + $string = Str::trim(' foo bar '); + + // foo bar + + +#### `Str::ltrim()` {.collection-method} + +The `Str::ltrim` method strips whitespace (or other characters) from the beginning of the given string. Unlike PHP's native `ltrim` function, the `Str::ltrim` method also removes unicode whitespace characters: + + use Illuminate\Support\Str; + + $string = Str::ltrim(' foo bar'); + + // foo bar + + +#### `Str::rtrim()` {.collection-method} + +The `Str::rtrim` method strips whitespace (or other characters) from the end of the given string. Unlike PHP's native `rtrim` function, the `Str::rtrim` method also removes unicode whitespace characters: + + use Illuminate\Support\Str; + + $string = Str::rtrim('foo bar '); + + // foo bar + #### `Str::ucfirst()` {.collection-method} From 531994848f419abf2937d184e83a9f063a2aaf72 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 2 Apr 2024 08:53:59 -0500 Subject: [PATCH 1596/2609] wip --- strings.md | 76 +++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/strings.md b/strings.md index a6427439a99..102f67d31f3 100644 --- a/strings.md +++ b/strings.md @@ -157,7 +157,6 @@ Laravel includes a variety of functions for manipulating string values. Many of [length](#method-fluent-str-length) [limit](#method-fluent-str-limit) [lower](#method-fluent-str-lower) -[ltrim](#method-fluent-str-ltrim) [markdown](#method-fluent-str-markdown) [mask](#method-fluent-str-mask) [match](#method-fluent-str-match) @@ -180,7 +179,6 @@ Laravel includes a variety of functions for manipulating string values. Many of [replaceMatches](#method-fluent-str-replace-matches) [replaceStart](#method-fluent-str-replace-start) [replaceEnd](#method-fluent-str-replace-end) -[rtrim](#method-fluent-str-rtrim) [scan](#method-fluent-str-scan) [singular](#method-fluent-str-singular) [slug](#method-fluent-str-slug) @@ -200,6 +198,8 @@ Laravel includes a variety of functions for manipulating string values. Many of [title](#method-fluent-str-title) [toBase64](#method-fluent-str-to-base64) [trim](#method-fluent-str-trim) +[ltrim](#method-fluent-str-ltrim) +[rtrim](#method-fluent-str-rtrim) [ucfirst](#method-fluent-str-ucfirst) [ucsplit](#method-fluent-str-ucsplit) [unwrap](#method-fluent-str-unwrap) @@ -1219,7 +1219,7 @@ The `Str::trim` method strips whitespace (or other characters) from the beginnin $string = Str::trim(' foo bar '); - // foo bar + // 'foo bar' #### `Str::ltrim()` {.collection-method} @@ -1228,9 +1228,9 @@ The `Str::ltrim` method strips whitespace (or other characters) from the beginni use Illuminate\Support\Str; - $string = Str::ltrim(' foo bar'); + $string = Str::ltrim(' foo bar '); - // foo bar + // 'foo bar ' #### `Str::rtrim()` {.collection-method} @@ -1239,9 +1239,9 @@ The `Str::rtrim` method strips whitespace (or other characters) from the end of use Illuminate\Support\Str; - $string = Str::rtrim('foo bar '); + $string = Str::rtrim(' foo bar '); - // foo bar + // ' foo bar' #### `Str::ucfirst()` {.collection-method} @@ -1954,21 +1954,6 @@ The `lower` method converts the given string to lowercase: // 'laravel' - -#### `ltrim` {.collection-method} - -The `ltrim` method trims the left side of the string: - - use Illuminate\Support\Str; - - $string = Str::of(' Laravel ')->ltrim(); - - // 'Laravel ' - - $string = Str::of('/Laravel/')->ltrim('/'); - - // 'Laravel/' - #### `markdown` {.collection-method} @@ -2328,21 +2313,6 @@ The `replaceEnd` method replaces the last occurrence of the given value only if // Hello World - -#### `rtrim` {.collection-method} - -The `rtrim` method trims the right side of the given string: - - use Illuminate\Support\Str; - - $string = Str::of(' Laravel ')->rtrim(); - - // ' Laravel' - - $string = Str::of('/Laravel/')->rtrim('/'); - - // '/Laravel' - #### `scan` {.collection-method} @@ -2574,7 +2544,7 @@ The `toBase64` method converts the given string to Base64: #### `trim` {.collection-method} -The `trim` method trims the given string: +The `trim` method trims the given string. Unlike PHP's native `trim` function, Laravel's `trim` method also removes unicode whitespace characters: use Illuminate\Support\Str; @@ -2586,6 +2556,36 @@ The `trim` method trims the given string: // 'Laravel' + +#### `ltrim` {.collection-method} + +The `ltrim` method trims the left side of the string. Unlike PHP's native `ltrim` function, Laravel's `ltrim` method also removes unicode whitespace characters: + + use Illuminate\Support\Str; + + $string = Str::of(' Laravel ')->ltrim(); + + // 'Laravel ' + + $string = Str::of('/Laravel/')->ltrim('/'); + + // 'Laravel/' + + +#### `rtrim` {.collection-method} + +The `rtrim` method trims the right side of the given string. Unlike PHP's native `rtrim` function, Laravel's `rtrim` method also removes unicode whitespace characters: + + use Illuminate\Support\Str; + + $string = Str::of(' Laravel ')->rtrim(); + + // ' Laravel' + + $string = Str::of('/Laravel/')->rtrim('/'); + + // '/Laravel' + #### `ucfirst` {.collection-method} From 11570baec044cd95004a810f9a14bfd5745cd3e7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 2 Apr 2024 09:53:02 -0500 Subject: [PATCH 1597/2609] wip --- queues.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/queues.md b/queues.md index 7d61ab789ec..b0cfbf76660 100644 --- a/queues.md +++ b/queues.md @@ -858,6 +858,27 @@ If you would like to specify the connection and queue that should be used for th new ReleasePodcast, ])->onConnection('redis')->onQueue('podcasts')->dispatch(); + +#### Adding Jobs to the Chain + +Occasionally, you may need to prepend or append a job to an existing job chain from within another job in that chain. You may accomplish this using the `prependToChain` and `appendToChain` methods: + +```php +/** + * Execute the job. + */ +public function handle(): void +{ + // ... + + // Prepend to the current chain, run job immediately after current job... + $this->prependToChain(new TranscribePodcast); + + // Append to the current chain, run job at end of chain... + $this->appendToChain(new TranscribePodcast); +} +``` + #### Chain Failures @@ -2273,6 +2294,29 @@ You may use the `assertDispatchedWithoutChain` method to assert that a job was p Bus::assertDispatchedWithoutChain(ShipOrder::class); + +#### Testing Chain Modifications + +If a chained job [prepends or appends jobs to an existing chain](#adding-jobs-to-the-chain), you may use the job's `assertHasChain` method to assert that the job has the expected chain of remaining jobs: + +```php +$job = new ProcessPodcast; + +$job->handle(); + +$job->assertHasChain([ + new TranscribePodcast, + new OptimizePodcast, + new ReleasePodcast, +]); +``` + +The `assertDoesntHaveChain` method may be used to assert that the job's remaining chain is empty: + +```php +$job->assertDoesntHaveChain(); +``` + #### Testing Chained Batches From 3d22ad917154c3e2412bcf5dc78d15d1bc5159f6 Mon Sep 17 00:00:00 2001 From: kicaj Date: Wed, 3 Apr 2024 16:56:36 +0200 Subject: [PATCH 1598/2609] Update routing.md (#9553) --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 146e03a6637..900280948e2 100644 --- a/routing.md +++ b/routing.md @@ -65,7 +65,7 @@ The `install:api` command installs [Laravel Sanctum](/docs/{{version}}/sanctum), Route::get('/user', function (Request $request) { return $request->user(); - })->middleware(Authenticate::using('sanctum')); + })->middleware('auth:sanctum'); The routes in `routes/api.php` are stateless and are assigned to the `api` [middleware group](/docs/{{version}}/middleware#laravels-default-middleware-groups). Additionally, the `/api` URI prefix is automatically applied to these routes, so you do not need to manually apply it to every route in the file. You may change the prefix by modifying your application's `bootstrap/app.php` file: From 0df96b2accc89769a080994e55807b732fac4907 Mon Sep 17 00:00:00 2001 From: frital Date: Wed, 3 Apr 2024 16:57:56 +0200 Subject: [PATCH 1599/2609] [11.x] Fix method name mentioned in Context: should be "hydrated" instead of "hydrating" (#9552) * Update context.md Method "hydrating" don't exist. Correct name is "hydrated". * Update context.md --------- Co-authored-by: Taylor Otwell --- context.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/context.md b/context.md index 3eb26590d85..65a786ed0c9 100644 --- a/context.md +++ b/context.md @@ -10,7 +10,7 @@ - [Hidden Context](#hidden-context) - [Events](#events) - [Dehydrating](#dehydrating) - - [Hydrating](#hydrating) + - [Hydrated](#hydrated) ## Introduction @@ -284,7 +284,7 @@ Context::forgetHidden(/* ... */); ## Events -Context dispatches two events that allow you to hook into the dehydrating and hydrating process of the context. +Context dispatches two events that allow you to hook into the hydration and dehydration process of the context. To illustrate how these events may be used, imagine that in a middleware of your application you set the `app.locale` configuration value based on the incoming HTTP request's `Accept-Language` header. Context's events allow you to capture this value during the request and restore it on the queue, ensuring notifications sent on the queue have the correct `app.locale` value. We can use context's events and [hidden](#hidden-context) data to achieve this, which the following documentation will illustrate. @@ -314,12 +314,12 @@ public function boot(): void > [!NOTE] > You should not use the `Context` facade within the `dehydrating` callback, as that will change the context of the current process. Ensure you only make changes to the repository passed to the callback. - -### Hydrating + +### Hydrated -Whenever a queued job begins executing on the queue, any context that was shared with the job will be "hydrated" back into the current context. The `Context::hydrating` method allows you to register a closure that will be invoked during the hydration process. +Whenever a queued job begins executing on the queue, any context that was shared with the job will be "hydrated" back into the current context. The `Context::hydrated` method allows you to register a closure that will be invoked during the hydration process. -Typically, you should register `hydrating` callbacks within the `boot` method of your application's `AppServiceProvider` class: +Typically, you should register `hydrated` callbacks within the `boot` method of your application's `AppServiceProvider` class: ```php use Illuminate\Log\Context\Repository; @@ -331,7 +331,7 @@ use Illuminate\Support\Facades\Context; */ public function boot(): void { - Context::hydrating(function (Repository $context) { + Context::hydrated(function (Repository $context) { if ($context->hasHidden('locale')) { Config::set('app.locale', $context->getHidden('locale')); } @@ -340,4 +340,4 @@ public function boot(): void ``` > [!NOTE] -> You should not use the `Context` facade within the `hydrating` callback and instead ensure you only make changes to the repository passed to the callback. +> You should not use the `Context` facade within the `hydrated` callback and instead ensure you only make changes to the repository passed to the callback. From 8b85410b19d8f6dba8023274a0f167503b74624e Mon Sep 17 00:00:00 2001 From: Jasper Zonneveld Date: Wed, 3 Apr 2024 17:51:08 +0200 Subject: [PATCH 1600/2609] [11.x] Document `when` method of ThrottlesExceptions middleware (#9551) * Document `when` method of ThrottlesExceptions middleware * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/queues.md b/queues.md index b0cfbf76660..d44257315f2 100644 --- a/queues.md +++ b/queues.md @@ -656,6 +656,23 @@ Internally, this middleware uses Laravel's cache system to implement rate limiti return [(new ThrottlesExceptions(10, 10))->by('key')]; } +By default, this middleware will throttle every exception. You can modify this behaviour by invoking the `when` method when attaching the middleware to your job. The exception will then only be throttled if closure provided to the `when` method returns `true`: + + use Illuminate\Http\Client\HttpClientException; + use Illuminate\Queue\Middleware\ThrottlesExceptions; + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [(new ThrottlesExceptions(10, 10))->when( + fn (Throwable $throwable) => $throwable instanceof HttpClientException + )]; + } + > [!NOTE] > If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. From 2cda2495b7737431b295d64dc4e0e38e35ce5e48 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Thu, 4 Apr 2024 18:37:11 +0100 Subject: [PATCH 1601/2609] Add note that it is now possible to schedule tasks in console.php (#9558) --- structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure.md b/structure.md index 8e601d47dfc..50c181be912 100644 --- a/structure.md +++ b/structure.md @@ -75,7 +75,7 @@ The `routes` directory contains all of the route definitions for your applicatio The `web.php` file contains routes that Laravel 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 all your routes will most likely be defined in the `web.php` file. -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. You may also [schedule](/docs/{{version}}/scheduling) tasks in the `console.php` file. Optionally, you may install additional route files for API routes (`api.php`) and broadcasting channels (`channels.php`), via the `install:api` and `install:broadcasting` Artisan commands. From b53c156a3dd1fa549a86be6122f8ee1e2ee1696a Mon Sep 17 00:00:00 2001 From: Jasper Zonneveld Date: Thu, 4 Apr 2024 19:47:42 +0200 Subject: [PATCH 1602/2609] [11.x] Document `report` method of ThrottlesExceptions middleware (#9557) * Document `report` method of ThrottlesExceptions middleware * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/queues.md b/queues.md index d44257315f2..57c3f6fecde 100644 --- a/queues.md +++ b/queues.md @@ -673,6 +673,23 @@ By default, this middleware will throttle every exception. You can modify this b )]; } +If you would like to have the throttled exceptions reported to your application's exception handler, you can do so by invoking the `report` method when attaching the middleware to your job. Optionally, you may provide a closure to the `report` method and the exception will only be reported if the given closure returns `true`: + + use Illuminate\Http\Client\HttpClientException; + use Illuminate\Queue\Middleware\ThrottlesExceptions; + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [(new ThrottlesExceptions(10, 10))->report( + fn (Throwable $throwable) => $throwable instanceof HttpClientException + )]; + } + > [!NOTE] > If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. From e0b27fc391f86472f457fc6c2799368711c6ef3f Mon Sep 17 00:00:00 2001 From: Nabisalay <121075018+Nabisalay@users.noreply.github.com> Date: Sun, 7 Apr 2024 22:41:32 +0500 Subject: [PATCH 1603/2609] Update Laravel 11 documentation for broadcasting installation (#9562) * minor adjustments for clarity and consistency in broadcasting documentation * formatting --------- Co-authored-by: Taylor Otwell --- broadcasting.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 8c476358cc1..b1ee1284df3 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -67,7 +67,9 @@ Event broadcasting is accomplished by a server-side broadcasting driver that bro ### 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: [Laravel Reverb](/docs/{{version}}/reverb), [Pusher Channels](https://pusher.com/channels), [Ably](https://ably.com), 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. +All of your application's event broadcasting configuration is stored in the `config/broadcasting.php` configuration file. Don't worry if this directory does not exist in your application; it will be created when you run the `install:broadcasting` Artisan command. + +Laravel supports several broadcast drivers out of the box: [Laravel Reverb](/docs/{{version}}/reverb), [Pusher Channels](https://pusher.com/channels), [Ably](https://ably.com), and a `log` driver for local development and debugging. Additionally, a `null` driver is included which allows you to disable broadcasting during testing. A configuration example is included for each of these drivers in the `config/broadcasting.php` configuration file. #### Installation @@ -78,7 +80,7 @@ By default, broadcasting is not enabled in new Laravel applications. You may ena php artisan install:broadcasting ``` -The `install:broadcasting` command will create a `routes/channels.php` file where you may register your application's broadcast authorization routes and callbacks. +The `install:broadcasting` command will create the `config/broadcasting.php` configuration file. In addition, the command will create the `routes/channels.php` file where you may register your application's broadcast authorization routes and callbacks. #### Queue Configuration From 11cf8df9ba1e84e5b6f6b85cef8c7f4e5a526d13 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Mon, 8 Apr 2024 14:30:07 +0100 Subject: [PATCH 1604/2609] [11.x] Refines Reverb documentation (#9565) * typo * differentiate variables * Update reverb.md * Update reverb.md --------- Co-authored-by: Taylor Otwell --- reverb.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/reverb.md b/reverb.md index 1db9ace28cf..48065d6dba1 100644 --- a/reverb.md +++ b/reverb.md @@ -74,11 +74,11 @@ For example, you may wish to maintain a single Laravel application which, via Re ```php 'apps' => [ [ - 'id' => 'my-app-one', + 'app_id' => 'my-app-one', // ... ], [ - 'id' => 'my-app-two', + 'app_id' => 'my-app-two', // ... ], ], @@ -126,6 +126,16 @@ php artisan reverb:start --host=127.0.0.1 --port=9000 Alternatively, you may define `REVERB_SERVER_HOST` and `REVERB_SERVER_PORT` environment variables in your application's `.env` configuration file. +The `REVERB_SERVER_HOST` and `REVERB_SERVER_PORT` environment variables should not be confused with `REVERB_HOST` and `REVERB_PORT`. The former specify the host and port on which to run the Reverb server itself, while the latter pair instruct Laravel where to send broadcast messages. For example, in a production environment, you may route requests from your public Reverb hostname on port `443` to a Reverb server operating on `0.0.0.0:8080`. In this scenario, your environment variables would be defined as follows: + +```ini +REVERB_SERVER_HOST=0.0.0.0 +REVERB_SERVER_PORT=8080 + +REVERB_HOST=ws.laravel.com +REVERB_PORT=443 +``` + ### Debugging From 67abb8787da00c7294fadda4fcf53fd49f571def Mon Sep 17 00:00:00 2001 From: Stephen Rees-Carter Date: Mon, 8 Apr 2024 23:41:24 +1000 Subject: [PATCH 1605/2609] Add mixed hash verification information to hashing (#9563) * Add mixed hash verification information to hashing * formatting --------- Co-authored-by: Taylor Otwell --- hashing.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hashing.md b/hashing.md index f2a79e72685..d2fdeec32d4 100644 --- a/hashing.md +++ b/hashing.md @@ -6,6 +6,7 @@ - [Hashing Passwords](#hashing-passwords) - [Verifying That a Password Matches a Hash](#verifying-that-a-password-matches-a-hash) - [Determining if a Password Needs to be Rehashed](#determining-if-a-password-needs-to-be-rehashed) +- [Hash Algorithm Verification](#hash-algorithm-verification) ## Introduction @@ -98,3 +99,14 @@ The `needsRehash` method provided by the `Hash` facade allows you to determine i if (Hash::needsRehash($hashed)) { $hashed = Hash::make('plain-text'); } + + +## Hash Algorithm Verification + +To prevent hash algorithm manipulation, Laravel's `Hash::check` method will first verify the given hash was generated using the application's selected hashing algorithm. If the algorithms are different, a `RuntimeException` exception will be thrown. + +This is the expected behavior for most applications, where the hashing algorithm is not expected to change and different algorithms can be an indication of a malicious attack. However, if you need to support multiple hashing algorithms within your application, such as when migrating from one algorithm to another, you can disable hash algorithm verification by setting the `HASH_VERIFY` environment variable to `false`: + +```ini +HASH_VERIFY=false +``` From 5f49bc6610d86ab145d99407df04d94c0c83a52d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 8 Apr 2024 15:07:40 +0100 Subject: [PATCH 1606/2609] Removes `^11.0` from installation step (#9567) --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 2d83cc20832..56c43e782a9 100644 --- a/installation.md +++ b/installation.md @@ -58,7 +58,7 @@ Before creating your first Laravel project, make sure that your local machine ha After you have installed PHP and Composer, you may create a new Laravel project via Composer's `create-project` command: ```nothing -composer create-project laravel/laravel:^11.0 example-app +composer create-project laravel/laravel example-app ``` Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: From 5208fb2976c6ee1b7a3c2e69de3ebf72e28e3658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Klindt=20S=C3=B8rensen?= <4467174+emilklindt@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:22:15 +0200 Subject: [PATCH 1607/2609] [11.x] Add maintenance mode on multiple servers (#9568) * Add maintenance mode on multiple servers * formatting --------- Co-authored-by: Taylor Otwell --- configuration.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/configuration.md b/configuration.md index e77cd8ea5c2..cc6a81a35fd 100644 --- a/configuration.md +++ b/configuration.md @@ -301,6 +301,20 @@ When accessing this hidden route, you will then be redirected to the `/` route o > [!NOTE] > Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?` or `&`. + +#### Maintenance Mode on Multiple Servers + +By default, Laravel determines if your application is in maintenance mode using a file-based system. This means to activate maintenance mode, the `php artisan down` command has to be executed on each server hosting your application. + +Alternatively, Laravel offers a cache-based method for handling maintenance mode. This method requires running the `php artisan down` command on just one server. To use this approach, modify the "driver" setting in the `config/app.php` file of your application to `cache`. Then, select a cache `store` that is accessible by all your servers. This ensures the maintenance mode status is consistently maintained across every server: + +```php +'maintenance' => [ + 'driver' => 'cache', + 'store' => 'database', +], +``` + #### Pre-Rendering the Maintenance Mode View From 5aebf54d1c9e4d8e53f093617243ff9c4cd30ec2 Mon Sep 17 00:00:00 2001 From: vixducis Date: Tue, 9 Apr 2024 16:33:53 +0200 Subject: [PATCH 1608/2609] Correction for unique method in eloquent collections (#9570) --- eloquent-collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-collections.md b/eloquent-collections.md index 435c1644873..10f2192e1c5 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -225,7 +225,7 @@ The `toQuery` method returns an Eloquent query builder instance containing a `wh #### `unique($key = null, $strict = false)` {.collection-method} -The `unique` method returns all of the unique models in the collection. Any models of the same type with the same primary key as another model in the collection are removed: +The `unique` method returns all of the unique models in the collection. Any models with the same primary key as another model in the collection are removed: $users = $users->unique(); From ab898e8342e7265e23a8917f90d431df6d1e12db Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 9 Apr 2024 15:43:02 -0500 Subject: [PATCH 1609/2609] wip --- prompts.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/prompts.md b/prompts.md index 1dd91c714f0..b5848aacca9 100644 --- a/prompts.md +++ b/prompts.md @@ -4,6 +4,7 @@ - [Installation](#installation) - [Available Prompts](#available-prompts) - [Text](#text) + - [Textarea](#textarea) - [Password](#password) - [Confirm](#confirm) - [Select](#select) @@ -115,6 +116,75 @@ $name = text( ); ``` + +### Textarea + +The `textarea` function will prompt the user with the given question, accept their input via a multi-line textarea, and then return it: + +```php +use function Laravel\Prompts\textarea; + +$story = textarea('Tell me a story.'); +``` + +You may also include placeholder text, a default value, and an informational hint: + +```php +$story = textarea( + label: 'Tell me a story.', + placeholder: 'This is a story about...', + hint: 'This will be displayed on your profile.' +); +``` + + +#### Required Values + +If you require a value to be entered, you may pass the `required` argument: + +```php +$story = textarea( + label: 'Tell me a story.', + required: true +); +``` + +If you would like to customize the validation message, you may also pass a string: + +```php +$story = textarea( + label: 'Tell me a story.', + required: 'A story is required.' +); +``` + + +#### Additional Validation + +Finally, if you would like to perform additional validation logic, you may pass a closure to the `validate` argument: + +```php +$story = textarea( + label: 'Tell me a story.', + validate: fn (string $value) => match (true) { + strlen($value) < 250 => 'The story must be at least 250 characters.', + strlen($value) > 10000 => 'The story must not exceed 10,000 characters.', + default => null + } +); +``` + +The closure will receive the value that has been entered and may return an error message, or `null` if the validation passes. + +Alternatively, you may leverage the power of Laravel's [validator](/docs/{{version}}/validation). To do so, provide an array containing the name of the attribute and the desired validation rules to the `validate` argument: + +```php +$story = textarea( + label: 'Tell me a story.', + validate: ['story' => 'required|max:10000'] +); +``` + ### Password From 34b063294f87120ff74f5fa79a421ac6a3c757a1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 9 Apr 2024 15:45:20 -0500 Subject: [PATCH 1610/2609] wip --- context.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/context.md b/context.md index 65a786ed0c9..18a6a4569ce 100644 --- a/context.md +++ b/context.md @@ -200,6 +200,12 @@ The `only` method may be used to retrieve a subset of the information in the con $data = Context::only(['first_key', 'second_key']); ``` +The `pull` method may be used to retrieve information from the context and immediately remove it from the context: + +```php +$value = Context::pull('key'); +``` + If you would like to retrieve all of the information stored in the context, you may invoke the `all` method: ```php @@ -275,6 +281,7 @@ Context::addHidden(/* ... */); Context::addHiddenIf(/* ... */); Context::pushHidden(/* ... */); Context::getHidden(/* ... */); +Context::pullHidden(/* ... */); Context::onlyHidden(/* ... */); Context::allHidden(/* ... */); Context::hasHidden(/* ... */); From 02f36b0804da54b30f9c3e4e50ef006a28193ae2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 10 Apr 2024 13:34:15 -0500 Subject: [PATCH 1611/2609] wip --- billing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index efb3f6c8fc7..f29dafaf9f0 100644 --- a/billing.md +++ b/billing.md @@ -436,7 +436,7 @@ Next, let's define the route that initiates a Stripe Customer Billing Portal ses })->middleware(['auth'])->name('billing'); > [!NOTE] -> As long as you have configured Cashier's webhook handling, Cashier will automatically keep your application's Cashier-related database tables in sync by inspecting the incoming webhooks from Stripe. So, for example, when a user cancels their subscription via Stripe's Customer Billing Portal, Cashier will receive the corresponding webhook and mark the subscription as "cancelled" in your application's database. +> As long as you have configured Cashier's webhook handling, Cashier will automatically keep your application's Cashier-related database tables in sync by inspecting the incoming webhooks from Stripe. So, for example, when a user cancels their subscription via Stripe's Customer Billing Portal, Cashier will receive the corresponding webhook and mark the subscription as "canceled" in your application's database. ## Customers @@ -833,7 +833,7 @@ Instead of collecting a customer's recurring payments automatically, you may ins $user->newSubscription('default', 'price_monthly')->createAndSendInvoice(); -The amount of time a customer has to pay their invoice before their subscription is cancelled is determined by the `days_until_due` option. By default, this is 30 days; however, you may provide a specific value for this option if you wish: +The amount of time a customer has to pay their invoice before their subscription is canceled is determined by the `days_until_due` option. By default, this is 30 days; however, you may provide a specific value for this option if you wish: $user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [ 'days_until_due' => 30 From 7107cc986bbe5e5b5c70c4fa7091f41ab6e94ba2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 10 Apr 2024 13:34:27 -0500 Subject: [PATCH 1612/2609] wip --- cashier-paddle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 4298e39f0f2..e3e8201ce88 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -407,10 +407,10 @@ Besides swapping plans you'll also need to allow your customers to cancel their return redirect()->route('dashboard'); })->name('subscription.cancel'); -And now your subscription will get cancelled at the end of its billing period. +And now your subscription will get canceled at the end of its billing period. > [!NOTE] -> As long as you have configured Cashier's webhook handling, Cashier will automatically keep your application's Cashier-related database tables in sync by inspecting the incoming webhooks from Paddle. So, for example, when you cancel a customer's subscription via Paddle's dashboard, Cashier will receive the corresponding webhook and mark the subscription as "cancelled" in your application's database. +> As long as you have configured Cashier's webhook handling, Cashier will automatically keep your application's Cashier-related database tables in sync by inspecting the incoming webhooks from Paddle. So, for example, when you cancel a customer's subscription via Paddle's dashboard, Cashier will receive the corresponding webhook and mark the subscription as "canceled" in your application's database. ## Checkout Sessions From 661689450c2cea25c4634da0dcbd3be9e990744d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 11 Apr 2024 09:13:41 -0500 Subject: [PATCH 1613/2609] wip --- console-tests.md | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/console-tests.md b/console-tests.md index 8ae16b7ceef..b949302012a 100644 --- a/console-tests.md +++ b/console-tests.md @@ -58,7 +58,7 @@ Laravel allows you to easily "mock" user input for your console commands using t $this->line('Your name is '.$name.' and you prefer '.$language.'.'); }); -You may test this command with the following test which utilizes the `expectsQuestion`, `expectsOutput`, `doesntExpectOutput`, `expectsOutputToContain`, `doesntExpectOutputToContain`, and `assertExitCode` methods: +You may test this command with the following test: ```php tab=Pest test('console command', function () { @@ -67,8 +67,6 @@ test('console command', function () { ->expectsQuestion('Which language do you prefer?', 'PHP') ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') - ->expectsOutputToContain('Taylor Otwell') - ->doesntExpectOutputToContain('you prefer Ruby') ->assertExitCode(0); }); ``` @@ -84,8 +82,6 @@ public function test_console_command(): void ->expectsQuestion('Which language do you prefer?', 'PHP') ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') - ->expectsOutputToContain('Taylor Otwell') - ->doesntExpectOutputToContain('you prefer Ruby') ->assertExitCode(0); } ``` @@ -110,6 +106,28 @@ public function test_console_command(): void ->doesntExpectOutput() ->assertExitCode(0); } +``` + + The `expectsOutputToContain` and `doesntExpectOutputToContain` methods may be used to make assertions against a portion of the output: + +```php tab=Pest +test('console command', function () { + $this->artisan('example') + ->expectsOutputToContain('Taylor') + ->assertExitCode(0); +}); +``` + +```php tab=PHPUnit +/** + * Test a console command. + */ +public function test_console_command(): void +{ + $this->artisan('example') + ->expectsOutputToContain('Taylor') + ->assertExitCode(0); +} ``` From fbf431b3d30df9fbb3ac58194c752619b6c354a2 Mon Sep 17 00:00:00 2001 From: Jurian Arie <28654085+JurianArie@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:30:51 +0200 Subject: [PATCH 1614/2609] Update contributions.md (#9573) --- contributions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributions.md b/contributions.md index 712e4a87c45..9d2b7dd050a 100644 --- a/contributions.md +++ b/contributions.md @@ -81,7 +81,7 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest version that supports bug fixes (currently `11.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `10.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. **Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `11.x`). From 8aaa0c0c0f42fa01c485ad0e09fe4e5ac9ccc153 Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Fri, 12 Apr 2024 12:52:24 +0100 Subject: [PATCH 1615/2609] Add missing import (#9575) --- dusk.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dusk.md b/dusk.md index 84405b767b8..f097f163ada 100644 --- a/dusk.md +++ b/dusk.md @@ -1939,6 +1939,7 @@ In addition to the default methods defined on pages, you may define additional m namespace Tests\Browser\Pages; use Laravel\Dusk\Browser; + use Laravel\Dusk\Page; class Dashboard extends Page { From 41d62ff34e73340e29a0b050812af1edce78f9b9 Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Mon, 15 Apr 2024 16:09:35 +0100 Subject: [PATCH 1616/2609] Correct code (#9577) * Correct code * Spacing --- dusk.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/dusk.md b/dusk.md index f097f163ada..d9a96d4bf1a 100644 --- a/dusk.md +++ b/dusk.md @@ -64,7 +64,7 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome composer require laravel/dusk --dev ``` -> [!WARNING] +> [!WARNING] > If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operating system: @@ -75,7 +75,7 @@ php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. -> [!NOTE] +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -97,7 +97,7 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> [!WARNING] +> [!WARNING] > Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. @@ -181,7 +181,7 @@ class ExampleTest extends DuskTestCase } ``` -> [!WARNING] +> [!WARNING] > SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. @@ -287,7 +287,7 @@ The `dusk` command accepts any argument that is normally accepted by the Pest / php artisan dusk --group=foo ``` -> [!NOTE] +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -506,7 +506,7 @@ Often, you will be testing pages that require authentication. You can use Dusk's ->visit('/home'); }); -> [!WARNING] +> [!WARNING] > After using the `loginAs` method, the user session will be maintained for all tests within the file. @@ -707,7 +707,7 @@ The `attach` method may be used to attach a file to a `file` input element. Like $browser->attach('photo', __DIR__.'/photos/mountains.png'); -> [!WARNING] +> [!WARNING] > The attach function requires the `Zip` PHP extension to be installed and enabled on your server. @@ -738,7 +738,7 @@ You may use the `seeLink` method to determine if a link with the given display t // ... } -> [!WARNING] +> [!WARNING] > These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. @@ -752,7 +752,7 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" $browser->keys('.app', ['{command}', 'j']); -> [!NOTE] +> [!NOTE] > All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). @@ -914,9 +914,9 @@ If you need to interact with elements within an iframe, you may use the `withinF $browser->withinFrame('#credit-card-details', function ($browser) { $browser->type('input[name="cardnumber"]', '4242424242424242') - ->type('input[name="exp-date"]', '12/24') - ->type('input[name="cvc"]', '123'); - })->press('Pay'); + ->type('input[name="exp-date"]', '1224') + ->type('input[name="cvc"]', '123') + ->press('Pay'); }); @@ -2092,7 +2092,7 @@ class ExampleTest extends DuskTestCase ## Continuous Integration -> [!WARNING] +> [!WARNING] > Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. @@ -2222,7 +2222,7 @@ pipeline: cp -v .env.example .env composer install --no-interaction --prefer-dist --optimize-autoloader php artisan key:generate - + # Create a dusk env file, ensuring APP_URL uses BUILD_HOST cp -v .env .env.dusk.ci sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci From 978427348387522b17ab79145190264e8fa294a1 Mon Sep 17 00:00:00 2001 From: Kennith Leung Date: Mon, 15 Apr 2024 10:58:07 -0700 Subject: [PATCH 1617/2609] [11.x] add api middleware group for private broadcast channels (#9578) * add api middleware group for private broadcast channels * Update sanctum.md --------- Co-authored-by: Taylor Otwell --- sanctum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index 7f5e8677f91..b3080fad3d6 100644 --- a/sanctum.md +++ b/sanctum.md @@ -339,7 +339,7 @@ If your SPA needs to authenticate with [private / presence broadcast channels](/ ) ->withBroadcasting( __DIR__.'/../routes/channels.php', - ['prefix' => 'api', 'middleware' => ['auth:sanctum']], + ['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']], ) Next, in order for Pusher's authorization requests to succeed, you will need to provide a custom Pusher `authorizer` when initializing [Laravel Echo](/docs/{{version}}/broadcasting#client-side-installation). This allows your application to configure Pusher to use the `axios` instance that is [properly configured for cross-domain requests](#cors-and-cookies): From ae7f39aa92e062d833dbf971757eb3fa59c65f85 Mon Sep 17 00:00:00 2001 From: Luke Downing Date: Tue, 16 Apr 2024 16:31:55 +0100 Subject: [PATCH 1618/2609] [11.x] Adds documentation for Prompts form support (#9581) * [11.x] Adds documentation for Prompts form support. * formatting --------- Co-authored-by: Taylor Otwell --- prompts.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/prompts.md b/prompts.md index b5848aacca9..04ce5eeaeb7 100644 --- a/prompts.md +++ b/prompts.md @@ -13,6 +13,7 @@ - [Search](#search) - [Multi-search](#multisearch) - [Pause](#pause) +- [Forms](#forms) - [Informational Messages](#informational-messages) - [Tables](#tables) - [Spin](#spin) @@ -720,6 +721,61 @@ use function Laravel\Prompts\pause; pause('Press ENTER to continue.'); ``` + +## Forms + +Often, you will have multiple prompts that will be displayed in sequence to collect information before performing additional actions. You may use the `form` function to create a grouped set of prompts for the user to complete: + +```php +use function Laravel\Prompts\form; + +$responses = form() + ->text(label: 'What is your name?', required: true) + ->password('What is your password?', validate: ['password' => 'min:8']) + ->confirm('Do you accept the terms?') + ->submit(); +``` + +The `submit` method will return a numerically indexed array containing all of the responses from the form's prompts. However, you may provide a name for each prompt via the `name` argument. When a name is provided, the named prompt's response may be accessed via that name: + +```php +use App\Models\User; +use function Laravel\Prompts\form; + +$responses = form() + ->text(label: 'What is your name?', required: true, name: 'name') + ->password( + 'What is your password?', + validate: ['password' => 'min:8'], + name: 'password', + ) + ->confirm('Do you accept the terms?') + ->submit(); + +User::create([ + 'name' => $responses['name'], + 'password' => $responses['password'] +]); +``` + +The primary benefit of using the `form` function is the ability for the user to return to previous prompts in the form using either `CTRL + U` or `CMD + BACKSPACE`. This allows the user to fix mistakes or alter selections without needing to cancel and restart the entire form. + +If you need more granular control over a prompt in a form, you may invoke the `add` method instead of calling one of the prompt functions directly. The `add` method is passed all previous responses provided by the user: + +```php +use function Laravel\Prompts\form; +use function Laravel\Prompts\outro; + +$responses = form() + ->text(label: 'What is your name?', required: true, name: 'name') + ->add(function ($responses) { + return text("How old are you, {$responses['name']}?"); + }, name: 'age') + ->submit(); + +outro("Your name is {$responses['name']} and you are {$responses['age']} years old."); +``` + ## Informational Messages From 5117d931cabdcd6786839b98663b23c7ef5c1014 Mon Sep 17 00:00:00 2001 From: Timmy D'Hooghe <63366355+timmydhooghe@users.noreply.github.com> Date: Tue, 16 Apr 2024 17:32:13 +0200 Subject: [PATCH 1619/2609] Documenting the new RequiredIfDeclined validation rule (framework PR #51030) (#9579) --- validation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation.md b/validation.md index 09600ce4e2e..2154256744b 100644 --- a/validation.md +++ b/validation.md @@ -948,6 +948,7 @@ Below is a list of all available validation rules and their function: [Required](#rule-required) [Required If](#rule-required-if) [Required If Accepted](#rule-required-if-accepted) +[Required If Declined](#rule-required-if-declined) [Required Unless](#rule-required-unless) [Required With](#rule-required-with) [Required With All](#rule-required-with-all) @@ -1732,6 +1733,11 @@ If you would like to construct a more complex condition for the `required_if` ru The field under validation must be present and not empty if the _anotherfield_ field is equal to `"yes"`, `"on"`, `1`, `"1"`, `true`, or `"true"`. + +#### required_if_declined:_anotherfield_,... + +The field under validation must be present and not empty if the _anotherfield_ field is equal to `"no"`, `"off"`, `0`, `"0"`, `false`, or `"false"`. + #### required_unless:_anotherfield_,_value_,... From b5b7cd085edc9785b8190b2e2a172474f0d666ac Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 Apr 2024 10:37:02 -0500 Subject: [PATCH 1620/2609] wip --- collections.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/collections.md b/collections.md index 9b8fb4f28de..fc9c1cb5fb1 100644 --- a/collections.md +++ b/collections.md @@ -3665,6 +3665,22 @@ While the `each` method calls the given callback for each item in the collection // 2 // 3 + +#### `throttle()` {.collection-method} + +The `throttle` method will throttle the lazy collection such that each value is returned after the specified number of seconds. This method is especially useful for situations where you may be interacting with external APIs that rate limit incoming requests: + +```php +use App\Models\User; + +User::where('vip', true) + ->cursor() + ->throttle(seconds: 1) + ->each(function (User $user) { + // Call external API... + }); +``` + #### `remember()` {.collection-method} From 40ceee0b7209ed495607bf8761d19ace37e1cff9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 Apr 2024 10:39:32 -0500 Subject: [PATCH 1621/2609] wip --- cache.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cache.md b/cache.md index 701461a267b..169e6b63711 100644 --- a/cache.md +++ b/cache.md @@ -444,3 +444,13 @@ Event Name | `Illuminate\Cache\Events\CacheMissed` | `Illuminate\Cache\Events\KeyForgotten` | `Illuminate\Cache\Events\KeyWritten` | + +To increase performance, you may disable cache events by setting the `events` configuration option to `false` for a given cache store in your application's `config/cache.php` configuration file: + +```php +'database' => [ + 'driver' => 'database', + // ... + 'events' => false, +], +``` From 5c18f4c0e9ed990c4f34ab663b4192903cb7210e Mon Sep 17 00:00:00 2001 From: Mehmet Onur AKKAYA Date: Tue, 16 Apr 2024 18:40:27 +0300 Subject: [PATCH 1622/2609] removes string backed enum type we also support int (#9574) --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 900280948e2..eb8cd83a4b3 100644 --- a/routing.md +++ b/routing.md @@ -625,7 +625,7 @@ Typically, a 404 HTTP response will be generated if an implicitly bound model is ### Implicit Enum Binding -PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To complement this feature, Laravel allows you to type-hint a [string-backed Enum](https://www.php.net/manual/en/language.enumerations.backed.php) on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: +PHP 8.1 introduced support for [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To complement this feature, Laravel allows you to type-hint a [backed Enum](https://www.php.net/manual/en/language.enumerations.backed.php) on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum: ```php Date: Tue, 16 Apr 2024 19:42:16 +0200 Subject: [PATCH 1623/2609] Document Facebook Limited Login support in Socialite (#9583) * Update socialite docs * Update socialite.md * Update socialite.md --------- Co-authored-by: Taylor Otwell --- socialite.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/socialite.md b/socialite.md index f55cba04016..ae937cf24eb 100644 --- a/socialite.md +++ b/socialite.md @@ -197,6 +197,8 @@ If you already have a valid access token for a user, you can retrieve their user $user = Socialite::driver('github')->userFromToken($token); +If you are using Facebook Limited Login via an iOS application, Facebook will return an OIDC token instead of an access token. Like an access token, the OIDC token can be provided to the `userFromToken` method in order to retrieve user details. + #### Retrieving User Details From a Token and Secret (OAuth1) From e1735c4ec309b6bf571e22af2639f1122b6e9c9d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 Apr 2024 14:13:55 -0500 Subject: [PATCH 1624/2609] wip --- prompts.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prompts.md b/prompts.md index 04ce5eeaeb7..231f9152f4e 100644 --- a/prompts.md +++ b/prompts.md @@ -730,7 +730,7 @@ Often, you will have multiple prompts that will be displayed in sequence to coll use function Laravel\Prompts\form; $responses = form() - ->text(label: 'What is your name?', required: true) + ->text('What is your name?', required: true) ->password('What is your password?', validate: ['password' => 'min:8']) ->confirm('Do you accept the terms?') ->submit(); @@ -743,7 +743,7 @@ use App\Models\User; use function Laravel\Prompts\form; $responses = form() - ->text(label: 'What is your name?', required: true, name: 'name') + ->text('What is your name?', required: true, name: 'name') ->password( 'What is your password?', validate: ['password' => 'min:8'], @@ -767,7 +767,7 @@ use function Laravel\Prompts\form; use function Laravel\Prompts\outro; $responses = form() - ->text(label: 'What is your name?', required: true, name: 'name') + ->text('What is your name?', required: true, name: 'name') ->add(function ($responses) { return text("How old are you, {$responses['name']}?"); }, name: 'age') From 6a842f44954da48d85d4902a5f480261319974f0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 Apr 2024 15:12:37 -0500 Subject: [PATCH 1625/2609] wip --- http-tests.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/http-tests.md b/http-tests.md index a852528720a..b645e30b1e8 100644 --- a/http-tests.md +++ b/http-tests.md @@ -347,7 +347,69 @@ class ExampleTest extends TestCase ### Exception Handling -Sometimes you may want to test that your application is throwing a specific exception. To ensure that the exception does not get caught by Laravel's exception handler and returned as an HTTP response, you may invoke the `withoutExceptionHandling` method before making your request: +Sometimes you may need to test that your application is throwing a specific exception. To accomplish this, you may "fake" the exception handler via the `Exceptions` facade. Once the exception handler has been faked, you may utilize the `assertReported` and `assertNotReported` methods to make assertions against exceptions that were thrown during the request: + +```php tab=Pest +get('/order/1'); + + // Assert an exception was thrown... + Exceptions::assertReported(InvalidOrderException::class); + + // Assert against the exception... + Exceptions::assertReported(function (InvalidOrderException $e) { + return $e->getMessage() === 'The order was invalid.'; + }); +}); +``` + +```php tab=PHPUnit +get('/'); + + // Assert an exception was thrown... + Exceptions::assertReported(InvalidOrderException::class); + + // Assert against the exception... + Exceptions::assertReported(function (InvalidOrderException $e) { + return $e->getMessage() === 'The order was invalid.'; + }); + } +} +``` + +The `assertNotReported` and `assertNothingReported` methods may be used to assert that a given exception was not thrown during the request or that no exceptions were thrown: + +```php +Exceptions::assertNotReported(InvalidOrderException::class); + +Exceptions::assertNothingReported(); +``` + +You may totally disable exception handling for a given request by invoking the `withoutExceptionHandling` method before making your request: $response = $this->withoutExceptionHandling()->get('/'); @@ -816,6 +878,11 @@ You may use the `component` method to evaluate and render a [Blade component](/d $view->assertSee('Taylor'); + +## Testing Exceptions + +If you are testing a + ## Available Assertions From 6e15a1b81114b49ad72890f34cca271b82775746 Mon Sep 17 00:00:00 2001 From: Luke Downing Date: Tue, 16 Apr 2024 21:18:08 +0100 Subject: [PATCH 1626/2609] [11.x] Fixes a typo about forms in the Prompt docs (#9585) --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index 231f9152f4e..726cd69686d 100644 --- a/prompts.md +++ b/prompts.md @@ -758,7 +758,7 @@ User::create([ ]); ``` -The primary benefit of using the `form` function is the ability for the user to return to previous prompts in the form using either `CTRL + U` or `CMD + BACKSPACE`. This allows the user to fix mistakes or alter selections without needing to cancel and restart the entire form. +The primary benefit of using the `form` function is the ability for the user to return to previous prompts in the form using `CTRL + U`. This allows the user to fix mistakes or alter selections without needing to cancel and restart the entire form. If you need more granular control over a prompt in a form, you may invoke the `add` method instead of calling one of the prompt functions directly. The `add` method is passed all previous responses provided by the user: From 9910299e9f14d5910002cd7251d6390cea55f7f1 Mon Sep 17 00:00:00 2001 From: Ali ABOUSSEBABA Date: Wed, 17 Apr 2024 18:54:32 +0100 Subject: [PATCH 1627/2609] Use the blade @use directive instead of @php (#9586) --- vite.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vite.md b/vite.md index 01bf4600004..c14bbca2bef 100644 --- a/vite.md +++ b/vite.md @@ -239,9 +239,7 @@ If needed, you may also specify the build path of your compiled assets when invo Sometimes it may be necessary to include the raw content of assets rather than linking to the versioned URL of the asset. For example, you may need to include asset content directly into your page when passing HTML content to a PDF generator. You may output the content of Vite assets using the `content` method provided by the `Vite` facade: ```blade -@php -use Illuminate\Support\Facades\Vite; -@endphp +@use('Illuminate\Support\Facades\Vite') From ca2571cb8a6492eb796519415df99244e0ea9d1b Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Wed, 17 Apr 2024 20:43:58 +0100 Subject: [PATCH 1628/2609] Document `assertPathEndsWith()` and `assertPathContains()` (#9582) * Document `assertPathEndsWith()` * Document `assertPathContains()` --- dusk.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dusk.md b/dusk.md index d9a96d4bf1a..6987eb7d3c2 100644 --- a/dusk.md +++ b/dusk.md @@ -1184,6 +1184,8 @@ Dusk provides a variety of assertions that you may make against your application [assertPortIs](#assert-port-is) [assertPortIsNot](#assert-port-is-not) [assertPathBeginsWith](#assert-path-begins-with) +[assertPathEndsWith](#assert-path-ends-with) +[assertPathContains](#assert-path-contains) [assertPathIs](#assert-path-is) [assertPathIsNot](#assert-path-is-not) [assertRouteIs](#assert-route-is) @@ -1322,6 +1324,20 @@ Assert that the current URL path begins with the given path: $browser->assertPathBeginsWith('/home'); + +#### assertPathEndsWith + +Assert that the current URL path ends with the given path: + + $browser->assertPathEndsWith('/home'); + + +#### assertPathContains + +Assert that the current URL path contains the given path: + + $browser->assertPathContains('/home'); + #### assertPathIs From c538d82dccf463e7201ed35b1a6c5d64cca548ba Mon Sep 17 00:00:00 2001 From: mgralikowski Date: Thu, 18 Apr 2024 16:02:19 +0200 Subject: [PATCH 1629/2609] Update notifications.md (#9588) Replace the class type with the intended one. --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 47136414836..84dddd24c15 100644 --- a/notifications.md +++ b/notifications.md @@ -1492,7 +1492,7 @@ When a notification is sent, the `Illuminate\Notifications\Events\NotificationSe /** * Handle the given event. */ - public function handle(NotificationSending $event): void + public function handle(NotificationSent $event): void { // ... } From 39fd7084ffdaac21e58569525c78b87b6c43dc22 Mon Sep 17 00:00:00 2001 From: Svenlaa Date: Mon, 22 Apr 2024 17:12:37 +0200 Subject: [PATCH 1630/2609] Add missing Mailersend step (#9592) * Add missing Mailersend step * Update mail.md --------- Co-authored-by: Taylor Otwell --- mail.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mail.md b/mail.md index 7bd51b3741b..b29c720bfd4 100644 --- a/mail.md +++ b/mail.md @@ -188,6 +188,14 @@ 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 +'mailersend' => [ + 'transport' => 'mailersend', +], +``` + To learn more about MailerSend, including how to use hosted templates, consult the [MailerSend driver documentation](https://github.com/mailersend/mailersend-laravel-driver#usage). From 427074e82ea21d127793161f2942816e3bcbe425 Mon Sep 17 00:00:00 2001 From: Felipe Dalcin Date: Tue, 23 Apr 2024 10:29:26 -0400 Subject: [PATCH 1631/2609] [11.x] fixes typo on closure commands (#9595) --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index 201e20de4be..6009fdb38a5 100644 --- a/artisan.md +++ b/artisan.md @@ -163,7 +163,7 @@ Let's take a look at an example command. Note that we are able to request any de 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. -Even though the `routes/console.php` file 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 command's arguments and options: +Even though the `routes/console.php` 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 command's arguments and options: Artisan::command('mail:send {user}', function (string $user) { $this->info("Sending email to: {$user}!"); From 30b9d7c70c505a571ad16f294a36803ea5f64bdf Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Tue, 23 Apr 2024 17:55:03 +0100 Subject: [PATCH 1632/2609] [11.x] Documents Anonymous Events (#9590) * documents anonymous events * wip * formatting --------- Co-authored-by: Taylor Otwell --- broadcasting.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/broadcasting.md b/broadcasting.md index b1ee1284df3..0f3611264c1 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -24,6 +24,7 @@ - [Broadcasting Events](#broadcasting-events) - [Only to Others](#only-to-others) - [Customizing the Connection](#customizing-the-connection) + - [Anonymous Events](#anonymous-events) - [Receiving Broadcasts](#receiving-broadcasts) - [Listening for Events](#listening-for-events) - [Leaving a Channel](#leaving-a-channel) @@ -767,6 +768,65 @@ Alternatively, you may specify the event's broadcast connection by calling the ` } } + +### Anonymous Events + +Sometimes, you may want to broadcast a simple event to your application's frontend without creating a dedicated event class. To accommodate this, the `Broadcast` facade allows you to broadcast "anonymous events": + +```php +Broadcast::on('orders.'.$order->id)->send(); +``` + +The example above will broadcast the following event: + +```json +{ + "event": "AnonymousEvent", + "data": "[]", + "channel": "orders.1" +} +``` + +Using the `as` and `with` methods, you may customize the event's name and data: + +```php +Broadcast::on('orders.'.$order->id) + ->as('OrderPlaced') + ->with($order) + ->send(); +``` + +The example above will broadcast an event like the following: + +```json +{ + "event": "OrderPlaced", + "data": "{ id: 1, total: 100 }", + "channel": "orders.1" +} +``` + +If you would like to broadcast the anonymous event on a private or presence channel, you may utilize the `private` and `presence` methods: + +```php +Broadcast::private('orders.'.$order->id)->send(); +Broadcast::presence('channels.'.$channel->id)->send(); +``` + +Broadcasting an anonymous event using the `send` method dispatches the event to your application's [queue](/docs/{{version}}/queues) for processing. However, if you would like to broadcast the event immediately, you may use the `sendNow` method: + +```php +Broadcast::on('orders.'.$order->id)->sendNow(); +``` + +To broadcast the event to all channel subscribers except the currently authenticated user, you can invoke the `toOthers` method: + +```php +Broadcast::on('orders.'.$order->id) + ->toOthers() + ->send(); +``` + ## Receiving Broadcasts From 047eb0f671cc9aced99f76e670c07f343ceecc71 Mon Sep 17 00:00:00 2001 From: "S.a Mahmoudzadeh" <36761585+saMahmoudzadeh@users.noreply.github.com> Date: Wed, 24 Apr 2024 23:20:22 +0330 Subject: [PATCH 1633/2609] fix grammar (#9597) --- context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.md b/context.md index 18a6a4569ce..4f70e34ca6f 100644 --- a/context.md +++ b/context.md @@ -155,7 +155,7 @@ Context::when( ### Stacks -Context offers the ability to create "stacks", which are lists of data stored in the order that they where added. You can add information to a stack by invoking the `push` method: +Context offers the ability to create "stacks", which are lists of data stored in the order that they were added. You can add information to a stack by invoking the `push` method: ```php use Illuminate\Support\Facades\Context; From 303e0d608f86f80b3f9abdbd77d1e44654d2f4fa Mon Sep 17 00:00:00 2001 From: NRDev <144210397+nrdevau@users.noreply.github.com> Date: Thu, 25 Apr 2024 07:12:54 +1000 Subject: [PATCH 1634/2609] [11.x] Update authorization.md - include Vue JS approaches (#9596) * Update authorization.md - include Vue JS approaches Would be great to see the Inertia approach when using authorization in the frontend. I'm not super happy about the Warning copy, but figured this would be a good way to get the discussion happening around this * formatting --------- Co-authored-by: Taylor Otwell --- authorization.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/authorization.md b/authorization.md index 6c1c79b202b..a817390e487 100644 --- a/authorization.md +++ b/authorization.md @@ -22,6 +22,7 @@ - [Via Middleware](#via-middleware) - [Via Blade Templates](#via-blade-templates) - [Supplying Additional Context](#supplying-additional-context) +- [Authorization & Inertia](#authorization-and-inertia) ## Introduction @@ -728,3 +729,45 @@ When attempting to determine if the authenticated user can update a given post, return redirect('/posts'); } + + +## Authorization & Inertia + +Although authorization must always be handled on the server, it can often be convenient to provide your frontend application with authorization data in order to properly render your application's UI. Laravel does not define a required convention for exposing authorization information to an Inertia powered frontend. + +However, if you are using one of Laravel's Inertia-based [starter kits](/docs/{{version}}/starter-kits), your application already contains a `HandleInertiaRequests` middleware. Within this middleware's `share` method, you may return shared data that will provided to all Inertia pages in your application. This shared data can serve as a convenient location to define authorization information for the user: + +```php + + */ + public function share(Request $request) + { + return [ + ...parent::share($request), + 'auth' => [ + 'user' => $request->user(), + 'permissions' => [ + 'post' => [ + 'create' => $request->user()->can('create', Post::class), + ], + ], + ], + ]; + } +} +``` From 4fd723ac5fe53fdc58c4e64febb3683e1975d1f8 Mon Sep 17 00:00:00 2001 From: "S.a Mahmoudzadeh" <36761585+saMahmoudzadeh@users.noreply.github.com> Date: Thu, 25 Apr 2024 17:28:28 +0330 Subject: [PATCH 1635/2609] fixes grammar (#9603) --- scheduling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index ec35a5bc564..9afd97f8ca0 100644 --- a/scheduling.md +++ b/scheduling.md @@ -389,7 +389,7 @@ On most operating systems, cron jobs are limited to running a maximum of once pe When sub-minute tasks are defined within your application, the `schedule:run` command will continue running until the end of the current minute instead of exiting immediately. This allows the command to invoke all required sub-minute tasks throughout the minute. -Since sub-minute tasks that take longer than expected to run could delay the execution of later sub-minute tasks, it is recommend that all sub-minute tasks dispatch queued jobs or background commands to handle the actual task processing: +Since sub-minute tasks that take longer than expected to run could delay the execution of later sub-minute tasks, it is recommended that all sub-minute tasks dispatch queued jobs or background commands to handle the actual task processing: use App\Jobs\DeleteRecentUsers; From 50d6c959ca6d215772055239a2a763538bf4ccb3 Mon Sep 17 00:00:00 2001 From: Chris Loftus <68920+chrisloftus@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:17:36 +0100 Subject: [PATCH 1636/2609] Response `json()` method mixed return type (#9602) --- http-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-client.md b/http-client.md index c49aaa57d8f..ca74f4d5fa9 100644 --- a/http-client.md +++ b/http-client.md @@ -35,7 +35,7 @@ To make requests, you may use the `head`, `get`, `post`, `put`, `patch`, and `de The `get` method returns an instance of `Illuminate\Http\Client\Response`, which provides a variety of methods that may be used to inspect the response: $response->body() : string; - $response->json($key = null, $default = null) : array|mixed; + $response->json($key = null, $default = null) : mixed; $response->object() : object; $response->collect($key = null) : Illuminate\Support\Collection; $response->status() : int; From 387f6977a4ea13943cc5079fb178bdccf55ba74a Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Thu, 25 Apr 2024 10:23:43 -0400 Subject: [PATCH 1637/2609] Add `URL::query()` method documentation (#9598) * Add url()->query() method documentation * Fix typo * Update urls.md --------- Co-authored-by: Taylor Otwell --- urls.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/urls.md b/urls.md index 5d454d5e75a..ab71f573d3d 100644 --- a/urls.md +++ b/urls.md @@ -28,6 +28,32 @@ The `url` helper may be used to generate arbitrary URLs for your application. Th // http://example.com/posts/1 +To generate a URL with query string parameters, you may use the `query` method: + + echo url()->query('/posts', ['search' => 'Laravel']); + + // https://example.com/posts?search=Laravel + + echo url()->query('/posts?sort=latest', ['search' => 'Laravel']); + + // http://example.com/posts?sort=latest&search=Laravel + +Providing query string parameters that already exist in the path will overwrite their existing value: + + echo url()->query('/posts?sort=latest', ['sort' => 'oldest']); + + // http://example.com/posts?sort=oldest + +Arrays of values may also be passed as query parameters. These values will be properly keyed and encoded in the generated URL: + + echo $url = url()->query('/posts', ['columns' => ['title', 'body']]); + + // http://example.com/posts?columns%5B0%5D=title&columns%5B1%5D=body + + echo urldecode($url); + + // http://example.com/posts?columns[0]=title&columns[1]=body + ### Accessing the Current URL From 2c81d29aaa90791945a33c88c8e8608f173a68e1 Mon Sep 17 00:00:00 2001 From: Steve Thomas Date: Fri, 26 Apr 2024 00:45:13 +1000 Subject: [PATCH 1638/2609] Add a section on authentication throttling (#9600) * Add a section on authentication throttling * wording * note instead of warning * formatting --------- Co-authored-by: Taylor Otwell --- fortify.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fortify.md b/fortify.md index 4a0b0bf547c..4e6c83dbbf4 100644 --- a/fortify.md +++ b/fortify.md @@ -205,6 +205,15 @@ Fortify::authenticateThrough(function (Request $request) { }); ``` +#### Authentication Throttling + +By default, Fortify will throttle authentication attempts using the `EnsureLoginIsNotThrottled` middleware. This middleware throttles attempts that are unique to a username and IP address combination. + +Some applications may require a different approach to throttling authentication attempts, such as throttling by IP address alone. Therefore, Fortify allows you to specify your own [rate limiter](/docs/{{version}}/routing#rate-limiting) via the `fortify.limiters.login` configuration option. Of course, this configuration option is located in your application's `config/fortify.php` configuration file. + +> [!NOTE] +> Utilizing a mixture of throttling, [two factor authentication](/docs/{{version}}/fortify#two-factor-authentication), and an external web application firewall (WAF) will provide the most robust defense for your legitimate application users. + ### Customizing Redirects From 3f53d0aa4dbcc18f51c547f366a4acf8b1baac4b Mon Sep 17 00:00:00 2001 From: Aron Rotteveel Date: Thu, 25 Apr 2024 17:22:15 +0200 Subject: [PATCH 1639/2609] [11.x] Write documentation for notification middleware (#9594) * Write documentation for notification middleware * Make code block stylign align with rest of docs * More little markdown tweaks * Minor wording tweaks * formatting * Formatting --------- Co-authored-by: Taylor Otwell --- notifications.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/notifications.md b/notifications.md index 84dddd24c15..e6676d8d99b 100644 --- a/notifications.md +++ b/notifications.md @@ -169,9 +169,6 @@ If you would like to delay the delivery of the notification, you may chain the ` $user->notify((new InvoicePaid($invoice))->delay($delay)); - -#### Delaying Notifications per Channel - You may pass an array to the `delay` method to specify the delay amount for specific channels: $user->notify((new InvoicePaid($invoice))->delay([ @@ -253,6 +250,27 @@ If you would like to specify a specific queue that should be used for each notif ]; } + +#### Queued Notification Middleware + +Queued notifications may define middleware [just like queued jobs](/docs/{{version}}/queues#job-middleware). To get started, define a `middleware` method on your notification class. The `middleware` method will receive `$notifiable` and `$channel` variables, which allow you to customize the returned middleware based on the notification's destination: + + use Illuminate\Queue\Middleware\RateLimited; + + /** + * Get the middleware the notification job should pass through. + * + * @return array + */ + public function middleware(object $notifiable, string $channel) + { + return match ($channel) { + 'email' => [new RateLimited('postmark')], + 'slack' => [new RateLimited('slack')], + default => [], + }; + } + #### Queued Notifications and Database Transactions From d67ea35774a93da23f751c0b3059c747d3da7d0a Mon Sep 17 00:00:00 2001 From: Janos Horvath Date: Thu, 25 Apr 2024 20:35:45 +0200 Subject: [PATCH 1640/2609] Update instructions to install the beta release of the package without downgrading application's minimum-stability setting (#9604) --- pulse.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pulse.md b/pulse.md index 79f787a71a5..680a6435c0b 100644 --- a/pulse.md +++ b/pulse.md @@ -35,17 +35,10 @@ For in-depth debugging of individual events, check out [Laravel Telescope](/docs > [!WARNING] > Pulse's first-party storage implementation currently requires a MySQL, MariaDB, or PostgreSQL database. If you are using a different database engine, you will need a separate MySQL, MariaDB, or PostgreSQL database for your Pulse data. -Since Pulse is currently in beta, you may need to adjust your application's `composer.json` file to allow beta package releases to be installed: - -```json -"minimum-stability": "beta", -"prefer-stable": true -``` - -Then, you may use the Composer package manager to install Pulse into your Laravel project: +Since Pulse is currently in beta, you will need to explicitly install the beta release: ```sh -composer require laravel/pulse +composer require laravel/pulse:@beta ``` Next, you should publish the Pulse configuration and migration files using the `vendor:publish` Artisan command: From ed143722f3e0584b26d8b6bebbd2e3d3424994a3 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:55:10 +0330 Subject: [PATCH 1641/2609] [11.x] Add note for `make:provider` command (#9607) * Update providers.md * Update providers.md * Update providers.md --------- Co-authored-by: Taylor Otwell --- providers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers.md b/providers.md index e17d9c15635..4d6c2709e1d 100644 --- a/providers.md +++ b/providers.md @@ -26,7 +26,7 @@ All user-defined service providers are registered in the `bootstrap/providers.ph All service providers extend the `Illuminate\Support\ServiceProvider` class. Most service providers contain a `register` and a `boot` method. Within the `register` method, you should **only bind things into the [service container](/docs/{{version}}/container)**. You should never attempt to register any event listeners, routes, or any other piece of functionality within the `register` method. -The Artisan CLI can generate a new provider via the `make:provider` command: +The Artisan CLI can generate a new provider via the `make:provider` command. Laravel will automatically register your new provider in your application's `bootstrap/providers.php` file: ```shell php artisan make:provider RiakServiceProvider From 71c87a53e238d1d96dbcf970e07082da93bcb8cc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 29 Apr 2024 08:26:57 -0500 Subject: [PATCH 1642/2609] wip --- facades.md | 1 + 1 file changed, 1 insertion(+) diff --git a/facades.md b/facades.md index e8ecca5e55c..1d849e0658b 100644 --- a/facades.md +++ b/facades.md @@ -326,6 +326,7 @@ Request | [Illuminate\Http\Request](https://laravel.com/api/{{version}}/Illumi Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Routing/ResponseFactory.html) |   Response (Instance) | [Illuminate\Http\Response](https://laravel.com/api/{{version}}/Illuminate/Http/Response.html) |   Route | [Illuminate\Routing\Router](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) | `router` +Schedule | [Illuminate\Console\Scheduling\Schedule](https://laravel.com/api/{{version}}/Illuminate/Console/Scheduling/Schedule.html) |   Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/{{version}}/Illuminate/Database/Schema/Builder.html) |   Session | [Illuminate\Session\SessionManager](https://laravel.com/api/{{version}}/Illuminate/Session/SessionManager.html) | `session` Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/{{version}}/Illuminate/Session/Store.html) | `session.store` From 2c88575e6855e4a61e8f98ce8a0a7b693fd5800d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 29 Apr 2024 18:05:19 +0330 Subject: [PATCH 1643/2609] remove incomplete section (#9611) --- http-tests.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/http-tests.md b/http-tests.md index b645e30b1e8..13e2e095c06 100644 --- a/http-tests.md +++ b/http-tests.md @@ -878,11 +878,6 @@ You may use the `component` method to evaluate and render a [Blade component](/d $view->assertSee('Taylor'); - -## Testing Exceptions - -If you are testing a - ## Available Assertions From ec089da122a82f89f8035aea5c918522b139b783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 29 Apr 2024 22:46:33 +0200 Subject: [PATCH 1644/2609] [11.x] Update FrankenPHP docs (#9613) * [11.x] Update FrankenPHP docs * Update deployment.md * Update octane.md --------- Co-authored-by: Taylor Otwell --- deployment.md | 12 ++++++++++++ octane.md | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index 6bbc2c07f18..e6af9290a33 100644 --- a/deployment.md +++ b/deployment.md @@ -4,6 +4,7 @@ - [Server Requirements](#server-requirements) - [Server Configuration](#server-configuration) - [Nginx](#nginx) + - [FrankenPHP](#frankenphp) - [Optimization](#optimization) - [Caching Configuration](#optimizing-configuration-loading) - [Caching Events](#caching-events) @@ -87,6 +88,17 @@ server { } ``` + +### FrankenPHP + +[FrankenPHP](https://frankenphp.dev/) may also be used to serve your Laravel applications. FrankenPHP is a modern PHP application server written in Go. To serve a Laravel PHP application using FrankenPHP, you may simply invoke its `php-server` command: + +```shell +frankenphp php-server -r public/ +``` + +To take advantage of more powerful features supported by FrankenPHP, such as its [Laravel Octane](/docs/{{version}}/octane) integration, HTTP/3, modern compression, or the ability to package Laravel applications as standalone binaries, please consult FrankenPHP's [Laravel documentation](https://frankenphp.dev/docs/laravel/). + ## Optimization diff --git a/octane.md b/octane.md index be0154e289e..2aee14ceb1d 100644 --- a/octane.md +++ b/octane.md @@ -56,7 +56,7 @@ php artisan octane:install > [!WARNING] > FrankenPHP's Octane integration is in beta and should be used with caution in production. -[FrankenPHP](https://frankenphp.dev) is a PHP application server, written in Go, that supports modern web features like early hints and Zstandard compression. When you install Octane and choose FrankenPHP as your server, Octane will automatically download and install the FrankenPHP binary for you. +[FrankenPHP](https://frankenphp.dev) is a PHP application server, written in Go, that supports modern web features like early hints, Brotli, and Zstandard compression. When you install Octane and choose FrankenPHP as your server, Octane will automatically download and install the FrankenPHP binary for you. #### FrankenPHP via Laravel Sail From bf44ac6afe222bb2af94a13dda285718fd215ab6 Mon Sep 17 00:00:00 2001 From: Nico <3315078+nicolus@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:36:07 +0200 Subject: [PATCH 1645/2609] Add note about standard "Forwarded" header in Trusted proxies (#9615) * Add note about standard "Forwarded" header in Trusted proxies This configuration is required for applications behind a proxy that uses the standard Forward: "" header described in RFC 7239, like HAproxy with the "option forwarded" directive. * Update requests.md --------- Co-authored-by: Taylor Otwell --- requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests.md b/requests.md index e2dbc005f27..9f3f1f9482c 100644 --- a/requests.md +++ b/requests.md @@ -620,7 +620,7 @@ In addition to configuring the trusted proxies, you may also configure the proxy }) > [!NOTE] -> If you are using AWS Elastic Load Balancing, your `headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `headers` value, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/7.0/deployment/proxies.html). +> If you are using AWS Elastic Load Balancing, the `headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. If your load balancer uses the standard `Forwarded` header from [RFC 7239](https://www.rfc-editor.org/rfc/rfc7239#section-4), the `headers` value should be `Request::HEADER_FORWARDED`. For more information on the constants that may be used in the `headers` value, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/7.0/deployment/proxies.html). #### Trusting All Proxies From 32acc19e0f6476497faf63b864e4551e324760f8 Mon Sep 17 00:00:00 2001 From: "S.a Mahmoudzadeh" <36761585+saMahmoudzadeh@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:06:39 +0330 Subject: [PATCH 1646/2609] fixes cashier-paddle.md (#9614) --- cashier-paddle.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index e3e8201ce88..345dfaf1475 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -160,7 +160,7 @@ Paddle relies on its own JavaScript library to initiate the Paddle checkout widg ### Currency Configuration -You can specify a locale to be used when formatting money values for display on invoices. Internally, Cashier utilizes [PHP's `NumberFormatter` class](https://www.php.net/manual/en/class.numberformatter.php) class to set the currency locale: +You can specify a locale to be used when formatting money values for display on invoices. Internally, Cashier utilizes [PHP's `NumberFormatter` class](https://www.php.net/manual/en/class.numberformatter.php) to set the currency locale: ```ini CASHIER_CURRENCY_LOCALE=nl_BE @@ -703,7 +703,7 @@ To create a subscription, first retrieve an instance of your billable model from The first argument given to the `subscribe` method is the specific price the user is subscribing to. This value should correspond to the price's identifier in Paddle. The `returnTo` method accepts a URL that your user will be redirected to after they successfully complete the checkout. The second argument passed to the `subscribe` method should be the internal "type" of the subscription. If your application only offers a single subscription, you might call this `default` or `primary`. This subscription type is only for internal application usage and is not meant to be displayed to users. In addition, it should not contain spaces and it should never be changed after creating the subscription. -You may also provide an array of custom meta data regarding the subscription using the `customData` method: +You may also provide an array of custom metadata regarding the subscription using the `customData` method: $checkout = $request->user()->subscribe($premium = 12345, 'default') ->customData(['key' => 'value']) @@ -722,7 +722,7 @@ After the user has finished their checkout, a `subscription_created` webhook wil ### Checking Subscription Status -Once a user is subscribed to your application, you may check their subscription status using a variety of convenient methods. First, the `subscribed` method returns `true` if the user has an valid subscription, even if the subscription is currently within its trial period: +Once a user is subscribed to your application, you may check their subscription status using a variety of convenient methods. First, the `subscribed` method returns `true` if the user has a valid subscription, even if the subscription is currently within its trial period: if ($user->subscribed()) { // ... From f0acbe976cf851fae97a8f1af44451ac0bdf9537 Mon Sep 17 00:00:00 2001 From: Haider Ali <47752310+haider00125@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:37:14 +0500 Subject: [PATCH 1647/2609] Fixing method name and example code under Price Preview section (#9616) * Fixing method name. * Fixing example code. --- cashier-paddle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 345dfaf1475..dbf236467ce 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -579,7 +579,7 @@ The currency will be determined based on the IP address of the request; however, use Laravel\Paddle\Cashier; - $prices = Cashier::productPrices(['pri_123', 'pri_456'], ['address' => [ + $prices = Cashier::previewPrices(['pri_123', 'pri_456'], ['address' => [ 'country_code' => 'BE', 'postal_code' => '1234', ]]); @@ -599,7 +599,7 @@ You may also display the subtotal price and tax amount separately: ```blade
      @foreach ($prices as $price) -
    • {{ $price->product_title }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)
    • +
    • {{ $price->product['name'] }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)
    • @endforeach
    ``` From 660a5099feeafcd66c38555b274851e630074178 Mon Sep 17 00:00:00 2001 From: Frans Slabbekoorn Date: Tue, 30 Apr 2024 16:42:45 +0200 Subject: [PATCH 1648/2609] fix: consistent parameter typing across request methods (#9617) * fix: consistent parameter typing across request methods * Update requests.md --------- Co-authored-by: Taylor Otwell --- requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests.md b/requests.md index 9f3f1f9482c..9bc0797de15 100644 --- a/requests.md +++ b/requests.md @@ -431,7 +431,7 @@ To determine if a given key is absent from the request, you may use the `missing // ... } - $request->whenMissing('name', function (array $input) { + $request->whenMissing('name', function () { // The "name" value is missing... }, function () { // The "name" value is present... From ea461f727a1b206a43a79e6b2185fd49ad3fef6b Mon Sep 17 00:00:00 2001 From: Davey Shafik Date: Tue, 30 Apr 2024 07:44:52 -0700 Subject: [PATCH 1649/2609] [1.x] Adds `--repair` option documentation (#9606) * Add `with-exit-status` flag docs for Pint * Update pint.md --------- Co-authored-by: Taylor Otwell --- pint.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pint.md b/pint.md index 50d6719a97a..2156e920e85 100644 --- a/pint.md +++ b/pint.md @@ -47,7 +47,7 @@ Pint will display a thorough list of all of the files that it updates. You can v ./vendor/bin/pint -v ``` -If you would like Pint to simply inspect your code for style errors without actually changing the files, you may use the `--test` option: +If you would like Pint to simply inspect your code for style errors without actually changing the files, you may use the `--test` option. Pint will return a non-zero exit code if any code style errors are found: ```shell ./vendor/bin/pint --test @@ -59,6 +59,12 @@ If you would like Pint to only modify the files that have uncommitted changes ac ./vendor/bin/pint --dirty ``` +If you would like Pint to fix any files with code style errors but also exit with a non-zero exit code if any errors were fixed, you may use the `--repair` option: + +```shell +./vendor/bin/pint --repair +``` + ## Configuring Pint From 6b5618c420d04e88421e60fb6e42802d260e0a9c Mon Sep 17 00:00:00 2001 From: Julius Kiekbusch Date: Tue, 30 Apr 2024 19:16:33 +0200 Subject: [PATCH 1650/2609] [11.x] Pulse v1 (#9618) * Pulse v1 * Update pulse.md --------- Co-authored-by: Taylor Otwell --- pulse.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pulse.md b/pulse.md index 680a6435c0b..f4b7e990ee8 100644 --- a/pulse.md +++ b/pulse.md @@ -35,10 +35,10 @@ For in-depth debugging of individual events, check out [Laravel Telescope](/docs > [!WARNING] > Pulse's first-party storage implementation currently requires a MySQL, MariaDB, or PostgreSQL database. If you are using a different database engine, you will need a separate MySQL, MariaDB, or PostgreSQL database for your Pulse data. -Since Pulse is currently in beta, you will need to explicitly install the beta release: +You may install Pulse using the Composer package manager: ```sh -composer require laravel/pulse:@beta +composer require laravel/pulse ``` Next, you should publish the Pulse configuration and migration files using the `vendor:publish` Artisan command: From 654fcea669deeb0653085567856aa07dd6875340 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 30 Apr 2024 20:51:20 +0100 Subject: [PATCH 1651/2609] Puts frankenphp out of beta (#9620) --- octane.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/octane.md b/octane.md index 2aee14ceb1d..1c4b32deec5 100644 --- a/octane.md +++ b/octane.md @@ -53,9 +53,6 @@ php artisan octane:install ### FrankenPHP -> [!WARNING] -> FrankenPHP's Octane integration is in beta and should be used with caution in production. - [FrankenPHP](https://frankenphp.dev) is a PHP application server, written in Go, that supports modern web features like early hints, Brotli, and Zstandard compression. When you install Octane and choose FrankenPHP as your server, Octane will automatically download and install the FrankenPHP binary for you. From e8ca8ae7d0c4932510de8905684fcfb7197f1a4b Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 2 May 2024 01:31:34 +1000 Subject: [PATCH 1652/2609] [11.x] Document disable highlighting (#9623) * Document disable highlighting * Update pulse.md --------- Co-authored-by: Taylor Otwell --- pulse.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pulse.md b/pulse.md index f4b7e990ee8..3d33ef69c63 100644 --- a/pulse.md +++ b/pulse.md @@ -214,6 +214,12 @@ The `` card shows the database queries in your ap By default, slow queries are grouped based on the SQL query (without bindings) and the location where it occurred, but you may choose to not capture the location if you wish to group solely on the SQL query. +If you encounter rendering performance issues due to extremely large SQL queries receiving syntax highlighting, you may disable highlighting by adding the `disable-highlighting` prop: + +```blade + +``` + See the [slow queries recorder](#slow-queries-recorder) documentation for more information. From f1eccbe0a9714c79f5c9943ef444d4968189ff28 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Wed, 1 May 2024 19:02:13 +0330 Subject: [PATCH 1653/2609] Update facades.md (#9621) --- facades.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/facades.md b/facades.md index 1d849e0658b..9b72a8cf783 100644 --- a/facades.md +++ b/facades.md @@ -303,6 +303,8 @@ Date | [Illuminate\Support\DateFactory](https://laravel.com/api/{{version}}/Il DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/{{version}}/Illuminate/Database/DatabaseManager.html) | `db` DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/{{version}}/Illuminate/Database/Connection.html) | `db.connection` Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Events/Dispatcher.html) | `events` +Exceptions | [Illuminate\Foundation\Exceptions\Handler](https://laravel.com/api/{{version}}/Illuminate/Foundation/Exceptions/Handler.html) |   +Exceptions (Instance) | [Illuminate\Contracts\Debug\ExceptionHandler](https://laravel.com/api/{{version}}/Illuminate/Contracts/Debug/ExceptionHandler.html) |   File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Filesystem/Filesystem.html) | `files` Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Access/Gate.html) |   Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Hashing/Hasher.html) | `hash` From 3f3c6b6fda3ec3e6e223ff60b96b8f453982b6b9 Mon Sep 17 00:00:00 2001 From: Ellie Rider Date: Fri, 3 May 2024 13:20:58 -0400 Subject: [PATCH 1654/2609] Helpers - Fix context example (#9630) Looks like it was a typo, the example used `config` instead of `context` --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 23cd72599c4..5906469c047 100644 --- a/helpers.md +++ b/helpers.md @@ -1714,7 +1714,7 @@ The `context` function gets the value from the [current context](/docs/{{version $value = context('trace_id'); - $value = config('trace_id', $default); + $value = context('trace_id', $default); You may set context values by passing an array of key / value pairs: From 28d91301f943da492a88c686fa81bfe6082fcfb0 Mon Sep 17 00:00:00 2001 From: Raphael Cunha Date: Fri, 3 May 2024 13:23:46 -0400 Subject: [PATCH 1655/2609] Remove extra "to" word in Multi-select section (#9628) --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index 726cd69686d..d61dbb0c668 100644 --- a/prompts.md +++ b/prompts.md @@ -372,7 +372,7 @@ If the `options` argument is an associative array, then the closure will receive ### Multi-select -If you need to the user to be able to select multiple options, you may use the `multiselect` function: +If you need the user to be able to select multiple options, you may use the `multiselect` function: ```php use function Laravel\Prompts\multiselect; From c371a706b9cfecde2e4e7e99f1db792f1df41868 Mon Sep 17 00:00:00 2001 From: Nikolay Nikolaev Date: Fri, 3 May 2024 20:24:29 +0300 Subject: [PATCH 1656/2609] Fix typos in sail.md (#9626) * Fix typos in sail.md * Update sail.md --- sail.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sail.md b/sail.md index a2a87744b2b..3811046a3d6 100644 --- a/sail.md +++ b/sail.md @@ -249,14 +249,14 @@ To connect to your application's Redis database from your local machine, you may ### Meilisearch -If you chose to install the [Meilisearch](https://www.meilisearch.com) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this powerful search-engine that is [compatible](https://github.com/meilisearch/meilisearch-laravel-scout) with [Laravel Scout](/docs/{{version}}/scout). Once you have started your containers, you may connect to the Meilisearch instance within your application by setting your `MEILISEARCH_HOST` environment variable to `http://meilisearch:7700`. +If you chose to install the [Meilisearch](https://www.meilisearch.com) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this powerful search engine that is integrated with [Laravel Scout](/docs/{{version}}/scout). Once you have started your containers, you may connect to the Meilisearch instance within your application by setting your `MEILISEARCH_HOST` environment variable to `http://meilisearch:7700`. From your local machine, you may access Meilisearch's web based administration panel by navigating to `http://localhost:7700` in your web browser. ### Typesense -If you chose to install the [Typesense](https://typesense.org) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this lightning fast, open-source search-engine that is natively integrated with [Laravel Scout](/docs/{{version}}/scout#typesense). Once you have started your containers, you may connect to the Typesense instance within your application by setting the following environment variables: +If you chose to install the [Typesense](https://typesense.org) service when installing Sail, your application's `docker-compose.yml` file will contain an entry for this lightning fast, open-source search engine that is natively integrated with [Laravel Scout](/docs/{{version}}/scout#typesense). Once you have started your containers, you may connect to the Typesense instance within your application by setting the following environment variables: ```ini TYPESENSE_HOST=typesense @@ -381,7 +381,7 @@ When Sail is running, you may access the Mailpit web interface at: http://localh ## Container CLI -Sometimes you may wish to start a Bash session within your application's container. You may use the `shell` 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: +Sometimes you may wish to start a Bash session within your application's container. You may use the `shell` command to connect to your application's container, allowing you to inspect its files and installed services as well as execute arbitrary shell commands within the container: ```shell sail shell @@ -517,7 +517,7 @@ sail debug migrate To debug your application while interacting with the application via a web browser, follow the [instructions provided by Xdebug](https://xdebug.org/docs/step_debug#web-application) for initiating an Xdebug session from the web browser. -If you're using PhpStorm, please review JetBrain's documentation regarding [zero-configuration debugging](https://www.jetbrains.com/help/phpstorm/zero-configuration-debugging.html). +If you're using PhpStorm, please review JetBrains' documentation regarding [zero-configuration debugging](https://www.jetbrains.com/help/phpstorm/zero-configuration-debugging.html). > [!WARNING] > Laravel Sail relies on `artisan serve` to serve your application. The `artisan serve` command only accepts the `XDEBUG_CONFIG` and `XDEBUG_MODE` variables as of Laravel version 8.53.0. Older versions of Laravel (8.52.0 and below) do not support these variables and will not accept debug connections. From 8ae1833164edd0ca78825724d23ef1f6550b961a Mon Sep 17 00:00:00 2001 From: Nikolay Nikolaev Date: Fri, 3 May 2024 20:24:42 +0300 Subject: [PATCH 1657/2609] Fix typo in octane.md (#9625) --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index 1c4b32deec5..4cdae9776f4 100644 --- a/octane.md +++ b/octane.md @@ -104,7 +104,7 @@ Typically, you should access your FrankenPHP Sail application via `https://local #### FrankenPHP via Docker -Using FrankenPHP's official Docker images can offer improved performance and the use additional extensions not included with static installations of FrankenPHP. In addition, the official Docker images provide support for running FrankenPHP on platforms it doesn't natively support, such as Windows. FrankenPHP's official Docker images are suitable for both local development and production usage. +Using FrankenPHP's official Docker images can offer improved performance and the use of additional extensions not included with static installations of FrankenPHP. In addition, the official Docker images provide support for running FrankenPHP on platforms it doesn't natively support, such as Windows. FrankenPHP's official Docker images are suitable for both local development and production usage. You may use the following Dockerfile as a starting point for containerizing your FrankenPHP powered Laravel application: From eee7d1f93f20cc9e79265dd1e3a3277ad2392573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sun, 5 May 2024 17:35:04 +0200 Subject: [PATCH 1658/2609] Fix typo in horizon (#9633) --- horizon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/horizon.md b/horizon.md index 0d55a87780c..a8d86dd7332 100644 --- a/horizon.md +++ b/horizon.md @@ -89,7 +89,7 @@ You may add additional supervisors to a given environment if you would like to d #### Maintenance Mode -While your application is in [maintainance mode](/docs/{{version}}/configuration#maintenance-mode), queued jobs will not be processed by Horizon unless the supervisor's `force` option is defined as `true` within the Horizon configuration file: +While your application is in [maintenance mode](/docs/{{version}}/configuration#maintenance-mode), queued jobs will not be processed by Horizon unless the supervisor's `force` option is defined as `true` within the Horizon configuration file: 'environments' => [ 'production' => [ From 8d7fffe5ca35892bf72eaffd7af46718d1e1585a Mon Sep 17 00:00:00 2001 From: Md Eamin Hossain Date: Sun, 5 May 2024 21:35:30 +0600 Subject: [PATCH 1659/2609] Update variable type hint in OrderShipmentStatusUpdated event (#9632) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 0f3611264c1..385b1b01516 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -361,7 +361,7 @@ When a user is viewing one of their orders, we don't want them to have to refres /** * The order instance. * - * @var \App\Order + * @var \App\Models\Order */ public $order; } From de690fc41dba45345a4ae0a03cffd54d40dd43e6 Mon Sep 17 00:00:00 2001 From: Ryuta Hamasaki Date: Mon, 6 May 2024 00:45:34 +0900 Subject: [PATCH 1660/2609] remove the repetitive "return" (#9631) --- pulse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pulse.md b/pulse.md index 3d33ef69c63..cee4f18a671 100644 --- a/pulse.md +++ b/pulse.md @@ -665,7 +665,7 @@ class TopSellers extends Card } ``` -The `aggregate` method returns return a collection of PHP `stdClass` objects. Each object will contain the `key` property captured earlier, along with keys for each of the requested aggregates: +The `aggregate` method returns a collection of PHP `stdClass` objects. Each object will contain the `key` property captured earlier, along with keys for each of the requested aggregates: ``` @foreach ($topSellers as $seller) From 78e4cab11471130f2e66a405a1ed82d84f1ce7a7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 6 May 2024 13:11:26 -0400 Subject: [PATCH 1661/2609] wip --- pulse.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pulse.md b/pulse.md index cee4f18a671..546d30e264e 100644 --- a/pulse.md +++ b/pulse.md @@ -214,10 +214,10 @@ The `` card shows the database queries in your ap By default, slow queries are grouped based on the SQL query (without bindings) and the location where it occurred, but you may choose to not capture the location if you wish to group solely on the SQL query. -If you encounter rendering performance issues due to extremely large SQL queries receiving syntax highlighting, you may disable highlighting by adding the `disable-highlighting` prop: +If you encounter rendering performance issues due to extremely large SQL queries receiving syntax highlighting, you may disable highlighting by adding the `without-highlighting` prop: ```blade - + ``` See the [slow queries recorder](#slow-queries-recorder) documentation for more information. From 851b548090eec94055f1f82595b3f8ca429b280b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 6 May 2024 15:03:04 -0400 Subject: [PATCH 1662/2609] resend driver --- mail.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/mail.md b/mail.md index b29c720bfd4..c43884c9f68 100644 --- a/mail.md +++ b/mail.md @@ -36,7 +36,7 @@ ## Introduction -Sending email doesn't have to be complicated. Laravel provides a clean, simple email API powered by the popular [Symfony Mailer](https://symfony.com/doc/7.0/mailer.html) component. Laravel and Symfony Mailer provide drivers for sending email via SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. +Sending email doesn't have to be complicated. Laravel provides a clean, simple email API powered by the popular [Symfony Mailer](https://symfony.com/doc/7.0/mailer.html) component. Laravel and Symfony Mailer provide drivers for sending email via SMTP, Mailgun, Postmark, Resend, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. ### Configuration @@ -48,7 +48,7 @@ Within your `mail` configuration file, you will find a `mailers` configuration a ### Driver / Transport Prerequisites -The API based drivers such as Mailgun, Postmark, and MailerSend are often simpler and faster than sending mail via SMTP servers. Whenever possible, we recommend that you use one of these drivers. +The API based drivers such as Mailgun, Postmark, Resend, and MailerSend are often simpler and faster than sending mail via SMTP servers. Whenever possible, we recommend that you use one of these drivers. #### Mailgun Driver @@ -113,6 +113,21 @@ If you would like to specify the Postmark message stream that should be used by This way you are also able to set up multiple Postmark mailers with different message streams. + +#### Resend Driver + +To use the Resend driver, install Resend's PHP SDK via Composer: + +```shell +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'), + ], + #### SES Driver From 2a1a27a4d5f9614ab92770833db16c09bd4b8eca Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 6 May 2024 15:03:41 -0400 Subject: [PATCH 1663/2609] wip --- mail.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mail.md b/mail.md index c43884c9f68..bd7e3c36997 100644 --- a/mail.md +++ b/mail.md @@ -89,7 +89,7 @@ If you are not using the United States [Mailgun region](https://documentation.ma #### Postmark Driver -To use the Postmark 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: ```shell composer require symfony/postmark-mailer symfony/http-client @@ -116,7 +116,7 @@ This way you are also able to set up multiple Postmark mailers with different me #### Resend Driver -To use the Resend driver, install Resend's PHP SDK via Composer: +To use the [Resend](https://resend.com/) driver, install Resend's PHP SDK via Composer: ```shell composer require resend/resend-php From fb0794e067f28b0704ba9c366f543d23135db70b Mon Sep 17 00:00:00 2001 From: Haider Ali <47752310+haider00125@users.noreply.github.com> Date: Tue, 7 May 2024 05:18:03 +0500 Subject: [PATCH 1664/2609] Fix event handler example code. (#9638) --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index dbf236467ce..aea70492b43 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -1235,7 +1235,7 @@ Both events contain the full payload of the Paddle webhook. For example, if you */ public function handle(WebhookReceived $event): void { - if ($event->payload['alert_name'] === 'transaction_billed') { + if ($event->payload['event_type'] === 'transaction.billed') { // Handle the incoming event... } } From 52c2336e200427998407c2b7c123584374155c2a Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 7 May 2024 08:59:46 +0200 Subject: [PATCH 1665/2609] Update cashier-paddle.md --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index aea70492b43..c84f3445a27 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -1220,7 +1220,7 @@ Cashier automatically handles subscription cancelation on failed charges and oth - `Laravel\Paddle\Events\WebhookReceived` - `Laravel\Paddle\Events\WebhookHandled` -Both events contain the full payload of the Paddle webhook. For example, if you wish to handle the `transaction_billed` webhook, you may register a [listener](/docs/{{version}}/events#defining-listeners) that will handle the event: +Both events contain the full payload of the Paddle webhook. For example, if you wish to handle the `transaction.billed` webhook, you may register a [listener](/docs/{{version}}/events#defining-listeners) that will handle the event: Date: Thu, 9 May 2024 04:21:46 +1000 Subject: [PATCH 1666/2609] Rename component prop (#9635) From 86518f934aa146fac6947d63ba5f5efbd6f8837d Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 May 2024 04:26:58 +1000 Subject: [PATCH 1667/2609] [11.x] Pulse: Ignore offline servers (#9622) * Document ignore-after prop * Improve example * Update pulse.md --------- Co-authored-by: Taylor Otwell --- pulse.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pulse.md b/pulse.md index 546d30e264e..55ecf0e1a77 100644 --- a/pulse.md +++ b/pulse.md @@ -169,6 +169,12 @@ public function boot(): void The `` card displays system resource usage for all servers running the `pulse:check` command. Please refer to the documentation regarding the [servers recorder](#servers-recorder) for more information on system resource reporting. +If you replace a server in your infrastructure, you may wish to stop displaying the inactive server in the Pulse dashboard after a given duration. You may accomplish this using the `ignore-after` prop, which accepts the number of seconds after which inactive servers should be removed from the Pulse dashboard. Alternatively, you may provide a relative time formatted string, such as `1 hour` or `3 days and 1 hour`: + +```blade + +``` + #### Application Usage From 43a416d09aa91530e8d6f24532acd7509c67ce82 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 9 May 2024 04:27:13 +1000 Subject: [PATCH 1668/2609] Namespace folio make command (#9510) --- folio.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/folio.md b/folio.md index 13406521094..9efb7edc619 100644 --- a/folio.md +++ b/folio.md @@ -109,7 +109,7 @@ php artisan folio:list You may create a nested route by creating one or more directories within one of Folio's directories. For instance, to create a page that is accessible via `/user/profile`, create a `profile.blade.php` template within the `pages/user` directory: ```bash -php artisan make:folio user/profile +php artisan folio:page user/profile # pages/user/profile.blade.php → /user/profile ``` @@ -120,10 +120,10 @@ php artisan make:folio user/profile Sometimes, you may wish to make a given page the "index" of a directory. By placing an `index.blade.php` template within a Folio directory, any requests to the root of that directory will be routed to that page: ```bash -php artisan make:folio index +php artisan folio:page index # pages/index.blade.php → / -php artisan make:folio users/index +php artisan folio:page users/index # pages/users/index.blade.php → /users ``` @@ -133,7 +133,7 @@ php artisan make:folio users/index Often, you will need to have segments of the incoming request's URL injected into your page so that you can interact with them. For example, you may need to access the "ID" of the user whose profile is being displayed. To accomplish this, you may encapsulate a segment of the page's filename in square brackets: ```bash -php artisan make:folio "users/[id]" +php artisan folio:page "users/[id]" # pages/users/[id].blade.php → /users/1 ``` @@ -149,7 +149,7 @@ Captured segments can be accessed as variables within your Blade template: To capture multiple segments, you can prefix the encapsulated segment with three dots `...`: ```bash -php artisan make:folio "users/[...ids]" +php artisan folio:page "users/[...ids]" # pages/users/[...ids].blade.php → /users/1/2/3 ``` @@ -170,7 +170,7 @@ When capturing multiple segments, the captured segments will be injected into th If a wildcard segment of your page template's filename corresponds one of your application's Eloquent models, Folio will automatically take advantage of Laravel's route model binding capabilities and attempt to inject the resolved model instance into your page: ```bash -php artisan make:folio "users/[User]" +php artisan folio:page "users/[User]" # pages/users/[User].blade.php → /users/1 ``` @@ -194,7 +194,7 @@ On Windows, you should use `-` to separate the model name from the key: `[Post-s By default, Folio will search for your model within your application's `app/Models` directory. However, if needed, you may specify the fully-qualified model class name in your template's filename: ```bash -php artisan make:folio "users/[.App.Models.User]" +php artisan folio:page "users/[.App.Models.User]" # pages/users/[.App.Models.User].blade.php → /users/1 ``` From 6d89df95cecd6c23227a306bd32ca21ac92ff767 Mon Sep 17 00:00:00 2001 From: Maarten Paauw Date: Thu, 9 May 2024 19:17:34 +0200 Subject: [PATCH 1669/2609] Update socialite.md (#9643) Add Slack OpenID provider to Socialite documentation --- socialite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/socialite.md b/socialite.md index ae937cf24eb..54a27175346 100644 --- a/socialite.md +++ b/socialite.md @@ -39,7 +39,7 @@ When upgrading to a new major version of Socialite, it's important that you care Before using Socialite, you will need to add credentials for the OAuth providers your application utilizes. Typically, these credentials may be retrieved by creating a "developer application" within the dashboard of the service you will be authenticating with. -These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin-openid`, `google`, `github`, `gitlab`, `bitbucket`, or `slack`, depending on the providers your application requires: +These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin-openid`, `google`, `github`, `gitlab`, `bitbucket`, `slack`, or `slack-openid`, depending on the providers your application requires: 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), From 62db09bab113a7b2236e96e0f47ab2f44aede3b7 Mon Sep 17 00:00:00 2001 From: Haider Ali <47752310+haider00125@users.noreply.github.com> Date: Sat, 11 May 2024 00:23:10 +0500 Subject: [PATCH 1670/2609] Fixing typo in extend trial example code (#9646) --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index c84f3445a27..48acd5dbb92 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -1170,7 +1170,7 @@ You may use the `onGenericTrial` method if you wish to know specifically that th You can extend an existing trial period on a subscription by invoking the `extendTrial` method and specifying the moment in time that the trial should end: - $user->subsription()->extendTrial(now()->addDays(5)); + $user->subscription()->extendTrial(now()->addDays(5)); Or, you may immediately activate a subscription by ending its trial by calling the `activate` method on the subscription: From f1e6124543f53c2a425356f0ec469f092e51740d Mon Sep 17 00:00:00 2001 From: Ehsan Mahmoodi Date: Fri, 10 May 2024 22:54:10 +0330 Subject: [PATCH 1671/2609] Update horizon.md (#9645) --- horizon.md | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/horizon.md b/horizon.md index a8d86dd7332..8e90e2bed3b 100644 --- a/horizon.md +++ b/horizon.md @@ -185,23 +185,7 @@ Alternatively, the job you wish to silence can implement the `Laravel\Horizon\Co ## Upgrading Horizon -When upgrading to a new major version of Horizon, it's important that you carefully review [the upgrade guide](https://github.com/laravel/horizon/blob/master/UPGRADE.md). In addition, when upgrading to any new Horizon version, you should re-publish Horizon's assets: - -```shell -php artisan horizon:publish -``` - -To keep the assets up-to-date and avoid issues in future updates, you may add the `vendor:publish --tag=laravel-assets` command to the `post-update-cmd` scripts in your application's `composer.json` file: - -```json -{ - "scripts": { - "post-update-cmd": [ - "@php artisan vendor:publish --tag=laravel-assets --ansi --force" - ] - } -} -``` +When upgrading to a new major version of Horizon, it's important that you carefully review [the upgrade guide](https://github.com/laravel/horizon/blob/master/UPGRADE.md). ## Running Horizon From 54adbdad2c77a91bb259879125a8d833c5f36088 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 10 May 2024 14:36:38 -0500 Subject: [PATCH 1672/2609] [11.x] add documentation for new "contains" validation rule (#9644) * add documentation for new "contains" validation rule https://github.com/laravel/framework/pull/51348 * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation.md b/validation.md index 2154256744b..1f24309e2ea 100644 --- a/validation.md +++ b/validation.md @@ -879,6 +879,7 @@ Below is a list of all available validation rules and their function: [Between](#rule-between) [Boolean](#rule-boolean) [Confirmed](#rule-confirmed) +[Contains](#rule-contains) [Current Password](#rule-current-password) [Date](#rule-date) [Date Equals](#rule-date-equals) @@ -1097,6 +1098,11 @@ The field under validation must be able to be cast as a boolean. Accepted 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. + +#### contains:_foo_,_bar_,... + +The field under validation must be an array that contains all of the given parameter values. + #### current_password From 61b0dcfaefb563e820976bda299554c65441af30 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 10 May 2024 15:06:40 -0500 Subject: [PATCH 1673/2609] minor grammar (#9648) - add missing word in `authorization.md`. - consistently use "an SPA" vs "a SPA". there were of mix of uses in the docs, and "an" is more appropriate as "SPA" starts with a vowel sound. --- authorization.md | 2 +- csrf.md | 2 +- sanctum.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/authorization.md b/authorization.md index a817390e487..f894ce11a72 100644 --- a/authorization.md +++ b/authorization.md @@ -735,7 +735,7 @@ When attempting to determine if the authenticated user can update a given post, Although authorization must always be handled on the server, it can often be convenient to provide your frontend application with authorization data in order to properly render your application's UI. Laravel does not define a required convention for exposing authorization information to an Inertia powered frontend. -However, if you are using one of Laravel's Inertia-based [starter kits](/docs/{{version}}/starter-kits), your application already contains a `HandleInertiaRequests` middleware. Within this middleware's `share` method, you may return shared data that will provided to all Inertia pages in your application. This shared data can serve as a convenient location to define authorization information for the user: +However, if you are using one of Laravel's Inertia-based [starter kits](/docs/{{version}}/starter-kits), your application already contains a `HandleInertiaRequests` middleware. Within this middleware's `share` method, you may return shared data that will be provided to all Inertia pages in your application. This shared data can serve as a convenient location to define authorization information for the user: ```php ### CSRF Tokens & SPAs -If you are building a SPA that is utilizing Laravel as an API backend, you should consult the [Laravel Sanctum documentation](/docs/{{version}}/sanctum) for information on authenticating with your API and protecting against CSRF vulnerabilities. +If you are building an SPA that is utilizing Laravel as an API backend, you should consult the [Laravel Sanctum documentation](/docs/{{version}}/sanctum) for information on authenticating with your API and protecting against CSRF vulnerabilities. ### Excluding URIs From CSRF Protection diff --git a/sanctum.md b/sanctum.md index b3080fad3d6..8830c7d5b9f 100644 --- a/sanctum.md +++ b/sanctum.md @@ -42,7 +42,7 @@ Laravel Sanctum offers this feature by storing user API tokens in a single datab #### 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 or a Next.js application. +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 an 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. 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. @@ -60,7 +60,7 @@ You may install Laravel Sanctum via the `install:api` Artisan command: php artisan install:api ``` -Next, if you plan to utilize Sanctum to authenticate a SPA, please refer to the [SPA Authentication](#spa-authentication) section of this documentation. +Next, if you plan to utilize Sanctum to authenticate an SPA, please refer to the [SPA Authentication](#spa-authentication) section of this documentation. ## Configuration @@ -267,7 +267,7 @@ Next, you should instruct Laravel that incoming requests from your SPA can authe #### CORS and Cookies -If you are having trouble authenticating with your application from a SPA that executes on a separate subdomain, you have likely misconfigured your CORS (Cross-Origin Resource Sharing) or session cookie settings. +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. The `config/cors.php` configuration file is not published by default. If you need to customize Laravel's CORS options, you should publish the complete `cors` configuration file using the `config:publish` Artisan command: From 9ff2aaeef0449657bbcfbf2da09fcb0509b85078 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 14 May 2024 03:05:59 +1000 Subject: [PATCH 1674/2609] Pulse thresolds (#9652) * Document per-x thresholds * Fix class reference * wip --------- Co-authored-by: Taylor Otwell --- pulse.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/pulse.md b/pulse.md index 55ecf0e1a77..30c57ecdf8e 100644 --- a/pulse.md +++ b/pulse.md @@ -309,6 +309,20 @@ The `SlowJobs` recorder captures information about slow jobs occurring in your a You may optionally adjust the slow job threshold, [sample rate](#sampling), and ignored job patterns. +You may have some jobs that you expect to take longer than others. In those cases, you may configure per-job thresholds: + +```php +Recorders\SlowJobs::class => [ + // ... + 'threshold' => [ + '#^App\\Jobs\\GenerateYearlyReports$#' => 5000, + 'default' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000), + ], +], +``` + +If no regular expression patterns match the job's classname, then the `'default'` value will be used. + #### Slow Outgoing Requests @@ -316,10 +330,24 @@ The `SlowOutgoingRequests` recorder captures information about outgoing HTTP req You may optionally adjust the slow outgoing request threshold, [sample rate](#sampling), and ignored URL patterns. +You may have some outgoing requests that you expect to take longer than others. In those cases, you may configure per-request thresholds: + +```php +Recorders\SlowOutgoingRequests::class => [ + // ... + 'threshold' => [ + '#backup.zip$#' => 5000, + 'default' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000), + ], +], +``` + +If no regular expression patterns match the request's URL, then the `'default'` value will be used. + You may also configure URL grouping so that similar URLs are grouped as a single entry. For example, you may wish to remove unique IDs from URL paths or group by domain only. Groups are configured using a regular expression to "find and replace" parts of the URL. Some examples are included in the configuration file: ```php -Recorders\OutgoingRequests::class => [ +Recorders\SlowOutgoingRequests::class => [ // ... 'groups' => [ // '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*', @@ -338,6 +366,20 @@ The `SlowQueries` recorder captures any database queries in your application tha You may optionally adjust the slow query threshold, [sample rate](#sampling), and ignored query patterns. You may also configure whether to capture the query location. The captured location will be displayed on the Pulse dashboard which can help to track down the query origin; however, if the same query is made in multiple locations then it will appear multiple times for each unique location. +You may have some queries that you expect to take longer than others. In those cases, you may configure per-query thresholds: + +```php +Recorders\SlowQueries::class => [ + // ... + 'threshold' => [ + '#^insert into `yearly_reports`#' => 5000, + 'default' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000), + ], +], +``` + +If no regular expression patterns match the query's SQL, then the `'default'` value will be used. + #### Slow Requests @@ -345,6 +387,20 @@ The `Requests` recorder captures information about requests made to your applica You may optionally adjust the slow route threshold, [sample rate](#sampling), and ignored paths. +You may have some requests that you expect to take longer than others. In those cases, you may configure per-request thresholds: + +```php +Recorders\SlowRequests::class => [ + // ... + 'threshold' => [ + '#^/admin/#' => 5000, + 'default' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000), + ], +], +``` + +If no regular expression patterns match the request's URL, then the `'default'` value will be used. + #### Servers From b8bf6ca8a64bd05c27e4ef2b6eddbd961d02bf2b Mon Sep 17 00:00:00 2001 From: "Alex P. Gates" Date: Mon, 13 May 2024 12:08:43 -0500 Subject: [PATCH 1675/2609] Add details about Carbon 3 upgrade (#9653) Adds a bit more context about changes to Carbon 3's `diffIn*`changes. --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 064f853be10..eb3ffb22f4a 100644 --- a/upgrade.md +++ b/upgrade.md @@ -414,7 +414,7 @@ public function scalar($query, $bindings = [], $useReadPdo = true); **Likelihood Of Impact: Medium** -Laravel 11 supports both Carbon 2 and Carbon 3. Carbon is a date manipulation library utilized extensively by Laravel and packages throughout the ecosystem. If you install Carbon 3, you should review Carbon's [change log](https://github.com/briannesbitt/Carbon/releases/tag/3.0.0). +Laravel 11 supports both Carbon 2 and Carbon 3. Carbon is a date manipulation library utilized extensively by Laravel and packages throughout the ecosystem. If you upgrade to Carbon 3, be aware that `diffIn*` methods now return floating-point numbers and may return negative values to indicate time direction, which is a significant change from Carbon 2. Review Carbon's [change log](https://github.com/briannesbitt/Carbon/releases/tag/3.0.0) for detailed information on how to handle these and other changes. ### Mail From 98800e08b321f2e591cefb104c9ece3645b41ab9 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 13 May 2024 20:45:33 +0330 Subject: [PATCH 1676/2609] [11.x] Add `whereIn` example with enum class (#9649) * Update routing.md * Update routing.md --------- Co-authored-by: Taylor Otwell --- routing.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing.md b/routing.md index eb8cd83a4b3..091ff88ee05 100644 --- a/routing.md +++ b/routing.md @@ -323,6 +323,10 @@ For convenience, some commonly used regular expression patterns have helper meth Route::get('/category/{category}', function (string $category) { // ... })->whereIn('category', ['movie', 'song', 'painting']); + + Route::get('/category/{category}', function (string $category) { + // ... + })->whereIn('category', CategoryEnum::cases()); If the incoming request does not match the route pattern constraints, a 404 HTTP response will be returned. From 2a9cd61538f0b9d340d11f178129b090a183bcbd Mon Sep 17 00:00:00 2001 From: Ahmed shamim Date: Mon, 13 May 2024 23:15:58 +0600 Subject: [PATCH 1677/2609] Hide X-powered-by in nginx config (#9651) --- deployment.md | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment.md b/deployment.md index e6af9290a33..421ccaf8c5b 100644 --- a/deployment.md +++ b/deployment.md @@ -80,6 +80,7 @@ server { fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; + fastcgi_hide_header X-Powered-By; } location ~ /\.(?!well-known).* { From 0dd21627d08a2bc060714f86c308f1c6e284b0de Mon Sep 17 00:00:00 2001 From: janne-stuvia <161812605+janne-stuvia@users.noreply.github.com> Date: Mon, 13 May 2024 19:50:40 +0200 Subject: [PATCH 1678/2609] Adding ',' in the example (#9657) --- precognition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precognition.md b/precognition.md index 3877cd0993e..4dc62c6de60 100644 --- a/precognition.md +++ b/precognition.md @@ -630,7 +630,7 @@ protected function rules() 'avatar' => [ ...$this->isPrecognitive() ? [] : ['required'], 'image', - 'mimes:jpg,png' + 'mimes:jpg,png', 'dimensions:ratio=3/2', ], // ... From bdf7135378f88ea5d47e8a8306462c65232b7a34 Mon Sep 17 00:00:00 2001 From: Milwad <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 13 May 2024 21:20:50 +0330 Subject: [PATCH 1679/2609] Update routing.md (#9656) --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 091ff88ee05..94a27da6983 100644 --- a/routing.md +++ b/routing.md @@ -317,7 +317,7 @@ For convenience, some commonly used regular expression patterns have helper meth })->whereUuid('id'); Route::get('/user/{id}', function (string $id) { - // + // ... })->whereUlid('id'); Route::get('/category/{category}', function (string $category) { From 147284468a475e2b294d9679e4d970d3a8104782 Mon Sep 17 00:00:00 2001 From: Edgaras <55696268+Edgaraszs@users.noreply.github.com> Date: Sat, 18 May 2024 00:37:35 +0300 Subject: [PATCH 1680/2609] [11.x] Adds documentation on GitHub Actions with Laravel Pint (#9661) * docs: laravel pint usage with github actions * Fixes Pint documentation with GitHub Actions * Update pint.md --------- Co-authored-by: Nuno Maduro Co-authored-by: Taylor Otwell --- pint.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pint.md b/pint.md index 2156e920e85..dcf69c9c294 100644 --- a/pint.md +++ b/pint.md @@ -7,6 +7,8 @@ - [Presets](#presets) - [Rules](#rules) - [Excluding Files / Folders](#excluding-files-or-folders) +- [Continuous Integration](#continuous-integration) + - [GitHub Actions](#running-tests-on-github-actions) ## Introduction @@ -156,3 +158,47 @@ If you would like to exclude a file by providing an exact path to the file, you ] } ``` + + +## Continuous Integration + + +### GitHub Actions + +To automate linting your project with Laravel Pint, you can configure [GitHub Actions](https://github.com/features/actions) to run Pint whenever new code is pushed to GitHub. First, be sure to grant "Read and write permissions" to workflows within GitHub at **Settings > Actions > General > Workflow permissions**. Then, create a `.github/workflows/lint.yml` file with the following content: + +```yaml +name: Fix Code Style + +on: [push] + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: [8.3] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json, dom, curl, libxml, mbstring + coverage: none + + - name: Install Pint + run: composer global require laravel/pint + + - name: Run Pint + run: pint + + - name: Commit linted files + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Fixes coding style" +``` From 782f498fdbac5fedffc491f5e6f512d5603e87a7 Mon Sep 17 00:00:00 2001 From: Janos Horvath Date: Tue, 21 May 2024 19:39:10 +0200 Subject: [PATCH 1681/2609] Fix typo in passing named argument (#9666) previous code was still working but not for the right reasons --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index fc9c1cb5fb1..b4317bd22a1 100644 --- a/collections.md +++ b/collections.md @@ -2131,7 +2131,7 @@ The `search` method searches the collection for the given value and returns its The search is done using a "loose" comparison, meaning a string with an integer value will be considered equal to an integer of the same value. To use "strict" comparison, pass `true` as the second argument to the method: - collect([2, 4, 6, 8])->search('4', $strict = true); + collect([2, 4, 6, 8])->search('4', strict: true); // false From 57be710a7a19ef854ddcb658e1bdb89958ab8571 Mon Sep 17 00:00:00 2001 From: Len Woodward Date: Wed, 22 May 2024 08:09:17 -0700 Subject: [PATCH 1682/2609] [11.x] Adds documentation for Command::fail method (#9669) * [11.x] Adds documentation for Command::fail method * Update artisan.md --------- Co-authored-by: Taylor Otwell --- artisan.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/artisan.md b/artisan.md index 6009fdb38a5..6c6f92da589 100644 --- a/artisan.md +++ b/artisan.md @@ -158,6 +158,19 @@ Let's take a look at an example command. Note that we are able to request any de > [!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. + +#### Exit Codes + +If nothing is returned from the `handle` method and the command executes successfully, the command will exit with a `0` exit code, indicating success. However, the `handle` method may optionally return an integer to manually specify command's exit code: + + $this->error('Something went wrong.'); + + return 1; + +If you would like to "fail" the command from another method within the command, you may utilize the `fail` method. The `fail` method will immediately terminate execution of the command and return an exit code of `1`: + + $this->fail('Something went wrong.'); + ### Closure Commands From b4ad31972d5e8a4859b3fc860f85879ba17c0a12 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 22 May 2024 10:10:33 -0500 Subject: [PATCH 1683/2609] wip --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index 6c6f92da589..645163c9c81 100644 --- a/artisan.md +++ b/artisan.md @@ -167,7 +167,7 @@ If nothing is returned from the `handle` method and the command executes success return 1; -If you would like to "fail" the command from another method within the command, you may utilize the `fail` method. The `fail` method will immediately terminate execution of the command and return an exit code of `1`: +If you would like to "fail" the command from any method within the command, you may utilize the `fail` method. The `fail` method will immediately terminate execution of the command and return an exit code of `1`: $this->fail('Something went wrong.'); From 0638b0001a8f66274a2c0129d1ccda0f09dc056f Mon Sep 17 00:00:00 2001 From: Mahmudul Hasan Date: Mon, 27 May 2024 19:42:36 +0600 Subject: [PATCH 1684/2609] Fix invalid route declarations (#9677) - Fixed invalid view route declarations in quickstart-selling-products section --- billing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/billing.md b/billing.md index f29dafaf9f0..24bf203762b 100644 --- a/billing.md +++ b/billing.md @@ -266,8 +266,8 @@ To charge customers for non-recurring, single-charge products, we'll utilize Cas ]); })->name('checkout'); - Route::view('checkout.success')->name('checkout-success'); - Route::view('checkout.cancel')->name('checkout-cancel'); + Route::view('/checkout/success', 'checkout.success')->name('checkout-success'); + Route::view('/checkout/cancel', 'checkout.cancel')->name('checkout-cancel'); As you can see in the example above, we will utilize Cashier's provided `checkout` method to redirect the customer to Stripe Checkout for a given "price identifier". When using Stripe, "prices" refer to [defined prices for specific products](https://stripe.com/docs/products-prices/how-products-and-prices-work). From fddaff4f44b4094e73c61ae81234fe5139cc7425 Mon Sep 17 00:00:00 2001 From: Chris Arter Date: Mon, 27 May 2024 09:45:58 -0400 Subject: [PATCH 1685/2609] [11.x] Documents removeAllFromSearch method (#9676) * documents removeAllFromSearch method * Update scout.md --------- Co-authored-by: Taylor Otwell --- scout.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scout.md b/scout.md index 535fb5bb18b..2a2c28246bb 100644 --- a/scout.md +++ b/scout.md @@ -595,6 +595,10 @@ Or, if you already have a collection of Eloquent models in memory, you may call $orders->unsearchable(); +To remove all of the model records from their corresponding index, you may invoke the `removeAllFromSearch` method: + + Order::removeAllFromSearch(); + ### Pausing Indexing From cdcb337a5d0fca73777d3453dd263eea02e81095 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 28 May 2024 14:49:33 -0500 Subject: [PATCH 1686/2609] wip --- prompts.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/prompts.md b/prompts.md index d61dbb0c668..1c29bfdb2ee 100644 --- a/prompts.md +++ b/prompts.md @@ -421,6 +421,14 @@ $categories = multiselect( ); ``` +You may allow the user to easily select all options via the `canSelectAll` argument: + +$categories = multiselect( + label: 'What categories should be assigned?', + options: Category::pluck('name', 'id'), + canSelectAll: true +); + #### Requiring a Value From 83acdf1ce7b6ba0940b1bfbbab95ad5a609f88f4 Mon Sep 17 00:00:00 2001 From: Hany Mohamed Date: Thu, 30 May 2024 20:13:22 +0400 Subject: [PATCH 1687/2609] Fix typo in shouldQueue method description (#9682) --- events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.md b/events.md index 719b17954cf..2a8bbfc6f20 100644 --- a/events.md +++ b/events.md @@ -328,7 +328,7 @@ If you would like to define the listener's queue connection, queue name, or dela #### Conditionally Queueing Listeners -Sometimes, you may need to determine whether a listener should be queued based on some data that are only available at runtime. To accomplish this, a `shouldQueue` method may be added to a listener to determine whether the listener should be queued. If the `shouldQueue` method returns `false`, the listener will not be executed: +Sometimes, you may need to determine whether a listener should be queued based on some data that are only available at runtime. To accomplish this, a `shouldQueue` method may be added to a listener to determine whether the listener should be queued. If the `shouldQueue` method returns `false`, the listener will not be queued: Date: Fri, 31 May 2024 18:27:52 +0200 Subject: [PATCH 1688/2609] Update matchAll to include first only matching group (#9683) * Update matchAll to include first only matching group * Update strings.md --------- Co-authored-by: Taylor Otwell --- strings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings.md b/strings.md index 102f67d31f3..43551e42bb8 100644 --- a/strings.md +++ b/strings.md @@ -2031,7 +2031,7 @@ The `matchAll` method will return a collection containing the portions of a stri // collect(['bar', 'bar']) -If you specify a matching group within the expression, Laravel will return a collection of that group's matches: +If you specify a matching group within the expression, Laravel will return a collection of the first matching group's matches: use Illuminate\Support\Str; From 0dc4bc30eaa44c960dce4b4e0d4397b953167571 Mon Sep 17 00:00:00 2001 From: William David Edwards Date: Thu, 6 Jun 2024 16:16:22 +0200 Subject: [PATCH 1689/2609] Remove libevent and libev from Reverb docs. Deprecated from React's event loop in https://github.com/reactphp/event-loop/pull/273 (#9693) --- reverb.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/reverb.md b/reverb.md index 48065d6dba1..e1ecb5a0dee 100644 --- a/reverb.md +++ b/reverb.md @@ -225,13 +225,9 @@ forge hard nofile 10000 Under the hood, Reverb uses a ReactPHP event loop to manage WebSocket connections on the server. By default, this event loop is powered by `stream_select`, which doesn't require any additional extensions. However, `stream_select` is typically limited to 1,024 open files. As such, if you plan to handle more than 1,000 concurrent connections, you will need to use an alternative event loop not bound to the same restrictions. -Reverb will automatically switch to an `ext-event`, `ext-ev`, or `ext-uv` powered loop when available. All of these PHP extensions are available for install via PECL: +Reverb will automatically switch to an `ext-uv` powered loop when available. This PHP extension is available for install via PECL: ```sh -pecl install event -# or -pecl install ev -# or pecl install uv ``` From 45770c3785bddd5a79d0fe8f5a8bfa5cd81867e6 Mon Sep 17 00:00:00 2001 From: Arjay Angeles Date: Thu, 6 Jun 2024 22:37:56 +0800 Subject: [PATCH 1690/2609] docs(migrations): NoPendingMigrations - nothing to migrate event (#9692) * docs(migrations): NoPendingMigrations - nothing to migrate event Add ed`NoPendingMigrations` event when there is nothing to migrate * Update migrations.md --------- Co-authored-by: Taylor Otwell --- migrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/migrations.md b/migrations.md index 6df04a5d488..e5a31935952 100644 --- a/migrations.md +++ b/migrations.md @@ -1221,5 +1221,6 @@ For convenience, each migration operation will dispatch an [event](/docs/{{versi | `Illuminate\Database\Events\MigrationsEnded` | A batch of migrations has finished executing. | | `Illuminate\Database\Events\MigrationStarted` | A single migration is about to be executed. | | `Illuminate\Database\Events\MigrationEnded` | A single migration has finished executing. | +| `Illuminate\Database\Events\NoPendingMigrations` | A migration command found no pending migrations. | | `Illuminate\Database\Events\SchemaDumped` | A database schema dump has completed. | | `Illuminate\Database\Events\SchemaLoaded` | An existing database schema dump has loaded. | From ef9f016ecda27acfad32d402290c2ac9a48072a3 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:59:01 +0330 Subject: [PATCH 1691/2609] [11.x] Add example for `updateOrInsert` method in queries (#9695) * Update queries.md * Update queries.md * Update queries.md --------- Co-authored-by: Taylor Otwell --- queries.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/queries.md b/queries.md index 94ff47f6905..ca6417d44d3 100644 --- a/queries.md +++ b/queries.md @@ -1026,6 +1026,22 @@ The `updateOrInsert` method will attempt to locate a matching database record us ['votes' => '2'] ); +You may provide a closure to the `updateOrInsert` method to customize the attributes that are updated or inserted into the database based on the existence of a matching record: + +```php +DB::table('users')->updateOrInsert( + ['user_id' => $user_id], + fn ($exists) => $exists ? [ + 'name' => $data['name'], + 'email' => $data['email'], + ] : [ + 'name' => $data['name'], + 'email' => $data['email'], + 'marketable' => true, + ], +); +``` + ### Updating JSON Columns From 38e549f8ec44e64769390ffd965728055f3b443b Mon Sep 17 00:00:00 2001 From: David Lambauer Date: Mon, 10 Jun 2024 17:12:23 +0200 Subject: [PATCH 1692/2609] table() expects an array, collection given (#9698) --- prompts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prompts.md b/prompts.md index 1c29bfdb2ee..fb7d19eab18 100644 --- a/prompts.md +++ b/prompts.md @@ -805,7 +805,7 @@ use function Laravel\Prompts\table; table( ['Name', 'Email'], - User::all(['name', 'email']) + User::all(['name', 'email'])->toArray() ); ``` From ec9ff5137928e2e6932664514b112ae12aaf7901 Mon Sep 17 00:00:00 2001 From: maru0914 <56859729+maru0914@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:13:03 +0900 Subject: [PATCH 1693/2609] Remove unnecessary imports from the first example (#9697) --- container.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/container.md b/container.md index fbe5fa2c496..1fe0e8921cc 100644 --- a/container.md +++ b/container.md @@ -29,9 +29,7 @@ Let's look at a simple example: namespace App\Http\Controllers; - use App\Http\Controllers\Controller; use App\Repositories\UserRepository; - use App\Models\User; use Illuminate\View\View; class UserController extends Controller From 669bb1c9dbb19687837e6062fab81a0fbe255858 Mon Sep 17 00:00:00 2001 From: Cas Ebbers <617080+CasEbb@users.noreply.github.com> Date: Tue, 11 Jun 2024 20:55:33 +0200 Subject: [PATCH 1694/2609] Fix call to Model::increment() (#9699) --- eloquent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index 0a5cd0d4135..a4142ed29a1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -313,7 +313,7 @@ If you need to customize the names of the columns used to store the timestamps, If you would like to perform model operations without the model having its `updated_at` timestamp modified, you may operate on the model within a closure given to the `withoutTimestamps` method: - Model::withoutTimestamps(fn () => $post->increment(['reads'])); + Model::withoutTimestamps(fn () => $post->increment('reads')); ### Database Connections From 39d960362f89761836c2f59d6aa51173fb94bd18 Mon Sep 17 00:00:00 2001 From: Volodya Kurshudyan <70023120+xurshudyan@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:59:39 +0400 Subject: [PATCH 1695/2609] Add default value in `Cache::pull` method (#9702) * Add default value in Cache::pull method * Update cache.md --------- Co-authored-by: Xurshudyan Co-authored-by: Taylor Otwell --- cache.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cache.md b/cache.md index 169e6b63711..e2a6751eee7 100644 --- a/cache.md +++ b/cache.md @@ -209,6 +209,8 @@ If you need to retrieve an item from the cache and then delete the item, you may $value = Cache::pull('key'); + $value = Cache::pull('key', 'default'); + ### Storing Items in the Cache From 329323efb2a054fa1b6fcdd93cdd264622283d8f Mon Sep 17 00:00:00 2001 From: Wes Hooper Date: Wed, 12 Jun 2024 15:05:37 +0100 Subject: [PATCH 1696/2609] Clarify exclusions for fallback HTTP error pages (#9701) * Clarify exclusions for fallback HTTP error pages * Update errors.md --------- Co-authored-by: Taylor Otwell --- errors.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/errors.md b/errors.md index 038caec7b8e..c04a842ceea 100644 --- a/errors.md +++ b/errors.md @@ -410,4 +410,6 @@ php artisan vendor:publish --tag=laravel-errors #### Fallback HTTP Error Pages -You may also define a "fallback" error page for a given series of HTTP status codes. This page will be rendered if there is not a corresponding page for the specific HTTP status code that occurred. To accomplish this, define a `4xx.blade.php` template and a `5xx.blade.php` template in your application's `resources/views/errors` directory. +You may also define a "fallback" error page for a given series of HTTP status codes. This page will be rendered if there is not a corresponding page for the specific HTTP status code that occurred. To accomplish this, define a `4xx.blade.php` template and a `5xx.blade.php` template in your application's `resources/views/errors` directory. + +When defining fallback error pages, the fallback pages will not affect `404`, `500`, and `503` error responses since Laravel has internal, dedicated pages for these status codes. To customize the pages rendered for these status codes, you should define a custom error page for each of them individually. From 9555e96b0920afefda9c4f5440fce758f2180be4 Mon Sep 17 00:00:00 2001 From: Gustavo Karkow <14905932+karkowg@users.noreply.github.com> Date: Thu, 13 Jun 2024 21:42:44 -0400 Subject: [PATCH 1697/2609] Document using wildcards in Horizon environments (#9706) * document wildcard in horizon environments * Update horizon.md --------- Co-authored-by: Taylor Otwell --- horizon.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/horizon.md b/horizon.md index 8e90e2bed3b..66d44d8999a 100644 --- a/horizon.md +++ b/horizon.md @@ -74,6 +74,18 @@ After installation, the primary Horizon configuration option that you should fam ], ], +You may also define a wildcard environment (`*`) which will be used when no other matching environment is found: + + 'environments' => [ + // ... + + '*' => [ + 'supervisor-1' => [ + 'maxProcesses' => 3, + ], + ], + ], + When you start Horizon, it will use the worker process configuration options for the environment that your application is running on. Typically, the environment is determined by the value of the `APP_ENV` [environment variable](/docs/{{version}}/configuration#determining-the-current-environment). For example, the default `local` Horizon environment is configured to start three worker processes and automatically balance the number of worker processes assigned to each queue. The default `production` environment is configured to start a maximum of 10 worker processes and automatically balance the number of worker processes assigned to each queue. > [!WARNING] From 64eb35ea054ff657e27aba694e8c1a8fae8d2f80 Mon Sep 17 00:00:00 2001 From: Anthony Cooper Date: Tue, 18 Jun 2024 18:22:04 +0100 Subject: [PATCH 1698/2609] Fix Lottery helper method name (#9711) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 5906469c047..d1db1245855 100644 --- a/helpers.md +++ b/helpers.md @@ -2360,7 +2360,7 @@ Laravel provides some simple methods to allow you to easily test your applicatio Lottery::fix([true, false]); // Lottery will return to normal behavior... - Lottery::determineResultsNormally(); + Lottery::determineResultNormally(); ### Pipeline From 92826a1f2422358b0d17bea19e13ecfa1e1aff6f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 18 Jun 2024 12:23:15 -0500 Subject: [PATCH 1699/2609] Revert "Fix Lottery helper method name (#9711)" (#9712) This reverts commit 64eb35ea054ff657e27aba694e8c1a8fae8d2f80. --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index d1db1245855..5906469c047 100644 --- a/helpers.md +++ b/helpers.md @@ -2360,7 +2360,7 @@ Laravel provides some simple methods to allow you to easily test your applicatio Lottery::fix([true, false]); // Lottery will return to normal behavior... - Lottery::determineResultNormally(); + Lottery::determineResultsNormally(); ### Pipeline From c6cffbab1697acf7e53bd52646ec72b432635113 Mon Sep 17 00:00:00 2001 From: Ryuta Hamasaki Date: Wed, 19 Jun 2024 02:47:08 +0900 Subject: [PATCH 1700/2609] [11.x] Add `before` and `after` methods to Collection (#9700) * add documentation for "before" method * add documentation for "after" method * mark "after" method as the first collection method * formatting --------- Co-authored-by: Taylor Otwell --- collections.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/collections.md b/collections.md index b4317bd22a1..38124cf783f 100644 --- a/collections.md +++ b/collections.md @@ -94,9 +94,11 @@ For the majority of the remaining collection documentation, we'll discuss each m
    +[after](#method-after) [all](#method-all) [average](#method-average) [avg](#method-avg) +[before](#method-before) [chunk](#method-chunk) [chunkWhile](#method-chunkwhile) [collapse](#method-collapse) @@ -255,8 +257,37 @@ For the majority of the remaining collection documentation, we'll discuss each m } + +#### `after()` {.collection-method .first-collection-method} + +The `after` method returns the item after the given item. `null` is returned if the given item is not found or is the last item: + + $collection = collect([1, 2, 3, 4, 5]); + + $collection->after(3); + + // 4 + + $collection->after(5); + + // null + +This method searches for the given item using "loose" comparison, meaning a string containing an integer value will be considered equal to an integer of the same value. To use "strict" comparison, you may provide the `strict` argument to the method: + + collect([2, 4, 6, 8])->after('4', strict: true); + + // null + +Alternatively, you may provide your own closure to search for the first item that passes a given truth test: + + collect([2, 4, 6, 8])->after(function (int $item, int $key) { + return $item > 5; + }); + + // 8 + -#### `all()` {.collection-method .first-collection-method} +#### `all()` {.collection-method} The `all` method returns the underlying array represented by the collection: @@ -287,6 +318,31 @@ The `avg` method returns the [average value](https://en.wikipedia.org/wiki/Avera // 2 + +#### `before()` {.collection-method} + +The `before` method is the opposite of the [`after`](#method-after) method. It returns the item before the given item. `null` is returned if the given item is not found or is the first item: + + $collection = collect([1, 2, 3, 4, 5]); + + $collection->before(3); + + // 2 + + $collection->before(1); + + // null + + collect([2, 4, 6, 8])->before('4', strict: true); + + // null + + collect([2, 4, 6, 8])->before(function (int $item, int $key) { + return $item > 5; + }); + + // 4 + #### `chunk()` {.collection-method} From c802e4ce57c4e56b43e0b59a8a588b68bbecf26b Mon Sep 17 00:00:00 2001 From: HarryLee186 <34167106+HarryLee186@users.noreply.github.com> Date: Thu, 20 Jun 2024 08:29:55 +0100 Subject: [PATCH 1701/2609] Update upgrade.md to include Scout dependency (#9714) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index eb3ffb22f4a..f1c49f053ca 100644 --- a/upgrade.md +++ b/upgrade.md @@ -79,6 +79,7 @@ You should update the following dependencies in your application's `composer.jso - `laravel/octane` to `^2.3` (If installed) - `laravel/passport` to `^12.0` (If installed) - `laravel/sanctum` to `^4.0` (If installed) +- `laravel/scout` to `^10.0` (If installed) - `laravel/spark-stripe` to `^5.0` (If installed) - `laravel/telescope` to `^5.0` (If installed) - `inertiajs/inertia-laravel` to `^1.0` (If installed) From 910898f77947c560ed9e1017b15314229a4bd1b6 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Jun 2024 16:37:04 +0200 Subject: [PATCH 1702/2609] Remove notes from bootstrap/providers.php (#9715) Follows the default from `laravel/laravel`, see https://github.com/laravel/laravel/commit/dd6777099d757eb714b2dd8b12709ed440e9a1d2. --- providers.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/providers.md b/providers.md index 4d6c2709e1d..f68718dfb67 100644 --- a/providers.md +++ b/providers.md @@ -149,8 +149,6 @@ All service providers are registered in the `bootstrap/providers.php` configurat Date: Fri, 21 Jun 2024 00:07:41 +0200 Subject: [PATCH 1703/2609] Add Herd installation guide (#9717) * add Herd setup instructions * wip * use official lowercase letters for nginx and dnsmasq * wip * wip --------- Co-authored-by: Marcel Pociot Co-authored-by: Taylor Otwell --- installation.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/installation.md b/installation.md index 56c43e782a9..beeff46dcfd 100644 --- a/installation.md +++ b/installation.md @@ -7,6 +7,9 @@ - [Environment Based Configuration](#environment-based-configuration) - [Databases and Migrations](#databases-and-migrations) - [Directory Configuration](#directory-configuration) +- [Local Installation Using Herd](#local-installation-using-herd) + - [Herd on macOS](#herd-on-macos) + - [Herd on Windows](#herd-on-windows) - [Docker Installation Using Sail](#docker-installation-using-sail) - [Sail on macOS](#sail-on-macos) - [Sail on Windows](#sail-on-windows) @@ -53,7 +56,7 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ## Creating a Laravel Project -Before creating your first Laravel project, make sure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS or Windows, PHP and Composer can be installed in minutes via [Laravel Herd](https://herd.laravel.com). In addition, we recommend [installing Node and NPM](https://nodejs.org). +Before creating your first Laravel project, make sure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS or Windows, PHP, Composer, Node and NPM can be installed in minutes via [Laravel Herd](#local-installation-using-herd). After you have installed PHP and Composer, you may create a new Laravel project via Composer's `create-project` command: @@ -124,13 +127,63 @@ php artisan migrate ``` > [!NOTE] -> If you are developing on macOS and need to install MySQL, PostgreSQL, or Redis locally, consider using [DBngin](https://dbngin.com/). +> If you are developing on macOS or Windows and need to install MySQL, PostgreSQL, or Redis locally, consider using [Herd Pro](https://herd.laravel.com/#plans). ### Directory Configuration Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files present within your application. + +## Local Installation Using Herd + +[Laravel Herd](https://herd.laravel.com) is a blazing fast, native Laravel and PHP development environment for macOS and Windows. Herd includes everything you need to get started with Laravel development, including PHP and Nginx. + +Once you install Herd, you're ready to start developing with Laravel. Herd includes command line tools for `php`, `composer`, `laravel`, `expose`, `node`, `npm`, and `nvm`. + +> [!NOTE] +> [Herd Pro](https://herd.laravel.com/#plans) augments Herd with additional powerful features, such as the ability to create and manage local MySQL, Postgres, and Redis databases, as well as local mail viewing and log monitoring. + + +### Herd on macOS + +If you develop on macOS, you can download the Herd installer from the [Herd website](https://herd.laravel.com). The installer automatically downloads the latest version of PHP and configures your Mac to always run [Nginx](https://www.nginx.com/) in the background. + +Herd for macOS uses [dnsmasq](https://en.wikipedia.org/wiki/Dnsmasq) to support "parked" directories. Any Laravel application in a parked directory will automatically be served by Herd. By default, Herd creates a parked directory at `~/Herd` and you can access any Laravel application in this directory on the `.test` domain using its directory name. + +After installing Herd, the fastest way to create a new Laravel project is using the Laravel CLI, which is bundled with Herd: + +```nothing +cd ~/Herd +laravel new my-app +cd my-app +herd open +``` + +Of course, you can always manage your parked directories and other PHP settings via Herd's UI, which can be opened from the Herd menu in your system tray. + +You can learn more about Herd by checking out the [Herd documentation](https://herd.laravel.com/docs). + + +### Herd on Windows + +You can download the Windows installer for Herd on the [Herd website](https://herd.laravel.com/windows). After the installation finishes, you can start Herd to complete the onboarding process and access the Herd UI for the first time. + +The Herd UI is accessible by left-clicking on Herd's system tray icon. A right-click opens the quick menu with access to all tools that you need on a daily basis. + +During installation, Herd creates a "parked" directory in your home directory at `%USERPROFILE%\Herd`. Any Laravel application in a parked directory will automatically be served by Herd, and you can access any Laravel application in this directory on the `.test` domain using its directory name. + +After installing Herd, the fastest way to create a new Laravel project is using the Laravel CLI, which is bundled with Herd. To get started, open Powershell and run the following commands: + +```nothing +cd ~\Herd +laravel new my-app +cd my-app +herd open +``` + +You can learn more about Herd by checking out the [Herd documentation for Windows](https://herd.laravel.com/docs/windows). + ## Docker Installation Using Sail From e7889864abbe68b9591dbca38809fd03599b8adf Mon Sep 17 00:00:00 2001 From: Spencer Williams Date: Fri, 21 Jun 2024 08:55:55 -0700 Subject: [PATCH 1704/2609] Add crucial note about folder permissions to deployment.md (#9718) * Add crucial note about folder permissions * This will help old and new devs understand which folders the framework needs to write in so that folder permissions are not too open. * Add new folder-permissions anchor to top list of links * Update deployment.md --------- Co-authored-by: Taylor Otwell --- deployment.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deployment.md b/deployment.md index 421ccaf8c5b..1cc4a57297f 100644 --- a/deployment.md +++ b/deployment.md @@ -5,6 +5,7 @@ - [Server Configuration](#server-configuration) - [Nginx](#nginx) - [FrankenPHP](#frankenphp) + - [Directory Permissions](#directory-permissions) - [Optimization](#optimization) - [Caching Configuration](#optimizing-configuration-loading) - [Caching Events](#caching-events) @@ -100,6 +101,11 @@ frankenphp php-server -r public/ To take advantage of more powerful features supported by FrankenPHP, such as its [Laravel Octane](/docs/{{version}}/octane) integration, HTTP/3, modern compression, or the ability to package Laravel applications as standalone binaries, please consult FrankenPHP's [Laravel documentation](https://frankenphp.dev/docs/laravel/). + +### Directory Permissions + +Laravel will need to write to the `bootstrap/cache` and `storage` directories, so you should ensure the web server process owner has permission to write to these directories. + ## Optimization From 33d43e187d466be64edb49b63f135a268fd7fb2c Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Mon, 24 Jun 2024 17:09:13 +0200 Subject: [PATCH 1705/2609] Update Homestead entry in package list (#9721) --- contributions.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contributions.md b/contributions.md index 9d2b7dd050a..4e25e01ea6f 100644 --- a/contributions.md +++ b/contributions.md @@ -36,8 +36,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Envoy](https://github.com/laravel/envoy) - [Laravel Folio](https://github.com/laravel/folio) - [Laravel Framework](https://github.com/laravel/framework) -- [Laravel Homestead](https://github.com/laravel/homestead) -- [Laravel Homestead Build Scripts](https://github.com/laravel/settler) +- [Laravel Homestead](https://github.com/laravel/homestead) ([Build Scripts](https://github.com/laravel/settler)) - [Laravel Horizon](https://github.com/laravel/horizon) - [Laravel Jetstream](https://github.com/laravel/jetstream) - [Laravel Passport](https://github.com/laravel/passport) From e6443d6177b0de2d6a620fd7a9be8569f74fd839 Mon Sep 17 00:00:00 2001 From: maru0914 <56859729+maru0914@users.noreply.github.com> Date: Fri, 28 Jun 2024 01:09:35 +0900 Subject: [PATCH 1706/2609] Add concrete domain name directory to example code (#9725) --- events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.md b/events.md index 2a8bbfc6f20..2c84b318632 100644 --- a/events.md +++ b/events.md @@ -71,7 +71,7 @@ By default, Laravel will automatically find and register your event listeners by If you plan to store your listeners in a different directory or within multiple directories, you may instruct Laravel to scan those directories using the `withEvents` method in your application's `bootstrap/app.php` file: ->withEvents(discover: [ - __DIR__.'/../app/Domain/Listeners', + __DIR__.'/../app/Domain/Orders/Listeners', ]) The `event:list` command may be used to list all of the listeners registered within your application: From 5f9c66744b1ba6cc2de8c533a061a9a953baa83b Mon Sep 17 00:00:00 2001 From: Gabi Suciu Date: Fri, 28 Jun 2024 23:05:33 +0300 Subject: [PATCH 1707/2609] Added missing dependency upgrade `livewire/livewire` (#9727) --- upgrade.md | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrade.md b/upgrade.md index f1c49f053ca..a5d380c972e 100644 --- a/upgrade.md +++ b/upgrade.md @@ -82,6 +82,7 @@ You should update the following dependencies in your application's `composer.jso - `laravel/scout` to `^10.0` (If installed) - `laravel/spark-stripe` to `^5.0` (If installed) - `laravel/telescope` to `^5.0` (If installed) +- `livewire/livewire` to `^3.4` (If installed) - `inertiajs/inertia-laravel` to `^1.0` (If installed)
    From 0117812a0237bfcb1989336b146d6bfbe8b8b696 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Mon, 1 Jul 2024 21:49:35 +0100 Subject: [PATCH 1708/2609] [11.x] Adds warning for Reverb URI (#9730) * add warning for reverb uri * Update reverb.md * Update reverb.md --------- Co-authored-by: Taylor Otwell --- reverb.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reverb.md b/reverb.md index e1ecb5a0dee..627068d2965 100644 --- a/reverb.md +++ b/reverb.md @@ -257,6 +257,9 @@ server { } ``` +> [!WARNING] +> Reverb listens for WebSocket connections at `/app` and handles API requests at `/apps`. You should ensure the web server handling Reverb requests can serve both of these URIs. If you are using [Laravel Forge](https://forge.laravel.com) to manage your servers, your Reverb server will be correctly configured by default. + Typically, web servers are configured to limit the number of allowed connections in order to prevent overloading the server. To increase the number of allowed connections on an Nginx web server to 10,000, the `worker_rlimit_nofile` and `worker_connections` values of the `nginx.conf` file should be updated: ```nginx From 2256340f468601a95a63fc1c1b61e3762e9b97e1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 3 Jul 2024 04:04:37 +1000 Subject: [PATCH 1709/2609] [11.x] Document Str::chop* methods (#9726) * Document Str::chop* methods * formatting --------- Co-authored-by: Taylor Otwell --- strings.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/strings.md b/strings.md index 43551e42bb8..ff3566a28ce 100644 --- a/strings.md +++ b/strings.md @@ -43,6 +43,8 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::betweenFirst](#method-str-between-first) [Str::camel](#method-camel-case) [Str::charAt](#method-char-at) +[Str::chopStart](#method-str-chop-start) +[Str::chopEnd](#method-str-chop-end) [Str::contains](#method-str-contains) [Str::containsAll](#method-str-contains-all) [Str::endsWith](#method-ends-with) @@ -134,6 +136,8 @@ Laravel includes a variety of functions for manipulating string values. Many of [camel](#method-fluent-str-camel) [charAt](#method-fluent-str-char-at) [classBasename](#method-fluent-str-class-basename) +[chopStart](#method-fluent-str-chop-start) +[chopEnd](#method-fluent-str-chop-end) [contains](#method-fluent-str-contains) [containsAll](#method-fluent-str-contains-all) [dirname](#method-fluent-str-dirname) @@ -377,6 +381,44 @@ The `Str::charAt` method returns the character at the specified index. If the in // 's' + +#### `Str::chopStart()` {.collection-method} + +The `Str::chopStart` method removes the first occurrence of the given value only if the value appears at the start of the string: + + use Illuminate\Support\Str; + + $url = Str::chopStart('https://laravel.com/', 'https://'); + + // 'laravel.com' + +You may also pass an array as the second argument. If the string starts with any of the values in the array then that value will be removed from string: + + use Illuminate\Support\Str; + + $url = Str::chopStart('http://laravel.com/', ['https://', 'http://']); + + // 'laravel.com' + + +#### `Str::chopEnd()` {.collection-method} + +The `Str::chopEnd` method removes the last occurrence of the given value only if the value appears at the end of the string: + + use Illuminate\Support\Str; + + $url = Str::chopEnd('app/Models/Photograph.php', '.php'); + + // 'app/Models/Photograph' + +You may also pass an array as the second argument. If the string ends with any of the values in the array then that value will be removed from string: + + use Illuminate\Support\Str; + + $url = Str::chopEnd('laravel.com/index.php', ['/index.html', '/index.php']); + + // 'laravel.com' + #### `Str::contains()` {.collection-method} @@ -1587,6 +1629,44 @@ The `classBasename` method returns the class name of the given class with the cl // 'Baz' + +#### `chopStart` {.collection-method} + +The `chopStart` method removes the first occurrence of the given value only if the value appears at the start of the string: + + use Illuminate\Support\Str; + + $url = Str::of('https://laravel.com/')->chopStart('https://'); + + // 'laravel.com' + +You may also pass an array. If the string starts with any of the values in the array then that value will be removed from string: + + use Illuminate\Support\Str; + + $url = Str::of('http://laravel.com/')->chopStart(['https://', 'http://']); + + // 'laravel.com' + + +#### `chopEnd` {.collection-method} + +The `chopEnd` method removes the last occurrence of the given value only if the value appears at the end of the string: + + use Illuminate\Support\Str; + + $url = Str::of('https://laravel.com/')->chopEnd('https://'); + + // 'laravel.com' + +You may also pass an array. If the string ends with any of the values in the array then that value will be removed from string: + + use Illuminate\Support\Str; + + $url = Str::of('http://laravel.com/')->chopEnd(['https://', 'http://']); + + // 'laravel.com' + #### `contains` {.collection-method} From 0599e7c4b9509b7e7dd311069debaca1c27b08b1 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Thu, 4 Jul 2024 00:30:14 +0330 Subject: [PATCH 1710/2609] [1.x] Add `multiply` method to collection (#9731) * add `multiply` method to collection * Update collections.md * Update collections.md --------- Co-authored-by: Taylor Otwell --- collections.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/collections.md b/collections.md index 38124cf783f..c4cfff89371 100644 --- a/collections.md +++ b/collections.md @@ -163,6 +163,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [mergeRecursive](#method-mergerecursive) [min](#method-min) [mode](#method-mode) +[multiply](#method-multiply) [nth](#method-nth) [only](#method-only) [pad](#method-pad) @@ -1689,6 +1690,29 @@ The `mode` method returns the [mode value](https://en.wikipedia.org/wiki/Mode_(s // [1, 2] + +#### `multiply()` {.collection-method} + +The `multiply` method creates the specified number of copies of all items in the collection: + +```php +$users = collect([ + ['name' => 'User #1', 'email' => 'user1@example.com'], + ['name' => 'User #2', 'email' => 'user2@example.com'], +])->multiply(3); + +/* + [ + ['name' => 'User #1', 'email' => 'user1@example.com'], + ['name' => 'User #2', 'email' => 'user2@example.com'], + ['name' => 'User #1', 'email' => 'user1@example.com'], + ['name' => 'User #2', 'email' => 'user2@example.com'], + ['name' => 'User #1', 'email' => 'user1@example.com'], + ['name' => 'User #2', 'email' => 'user2@example.com'], + ] +*/ +``` + #### `nth()` {.collection-method} From ae9631f61bd97b40e61aa0e81375d115414bc8d1 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 4 Jul 2024 16:13:43 +0200 Subject: [PATCH 1711/2609] Remove tax warning for single charges (#9733) --- billing.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/billing.md b/billing.md index 24bf203762b..dbc9b1e58a6 100644 --- a/billing.md +++ b/billing.md @@ -200,9 +200,6 @@ Once tax calculation has been enabled, any new subscriptions and any one-off inv For this feature to work properly, your customer's billing details, such as the customer's name, address, and tax ID, need to be synced to Stripe. You may use the [customer data synchronization](#syncing-customer-data-with-stripe) and [Tax ID](#tax-ids) methods offered by Cashier to accomplish this. -> [!WARNING] -> No tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). - ### Logging From 3ab73d96f69f08d996ec613d2382855dd79710ef Mon Sep 17 00:00:00 2001 From: Philip Downer Date: Thu, 4 Jul 2024 08:14:37 -0600 Subject: [PATCH 1712/2609] Fixes grammar for queue/connection customization heading (#9732) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 57c3f6fecde..723b65bba01 100644 --- a/queues.md +++ b/queues.md @@ -933,7 +933,7 @@ When chaining jobs, you may use the `catch` method to specify a closure that sho > Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. -### Customizing The Queue a Connection +### Customizing the Queue and Connection #### Dispatching to a Particular Queue From eb257374f1b48862572c496e1e500ba550dd807a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20H?= <36476318+Tamas-hi@users.noreply.github.com> Date: Fri, 5 Jul 2024 17:59:44 +0200 Subject: [PATCH 1713/2609] Update broadcasting.md (#9741) Since Laravel Reverb is no longer in beta, we can remove the info message from the documentation. --- broadcasting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 385b1b01516..6fb7ebad010 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -91,10 +91,10 @@ Before broadcasting any events, you should first configure and run a [queue work ### Reverb -When running the `install:broadcasting` command, you will be prompted to install [Laravel Reverb](/docs/{{version}}/reverb). Of course, you may also install Reverb manually using the Composer package manager. Since Reverb is currently in beta, you will need to explicitly install the beta release: +When running the `install:broadcasting` command, you will be prompted to install [Laravel Reverb](/docs/{{version}}/reverb). Of course, you may also install Reverb manually using the Composer package manager. ```sh -composer require laravel/reverb:@beta +composer require laravel/reverb ``` Once the package is installed, you may run Reverb's installation command to publish the configuration, add Reverb's required environment variables, and enable event broadcasting in your application: From c1e2f0e094846ec98e618c36a3d09966fca8ce61 Mon Sep 17 00:00:00 2001 From: Maru <56859729+maru0914@users.noreply.github.com> Date: Sat, 6 Jul 2024 00:59:58 +0900 Subject: [PATCH 1714/2609] Remove an unnecessary double quote (#9739) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 5906469c047..aaac1146fa0 100644 --- a/helpers.md +++ b/helpers.md @@ -1867,7 +1867,7 @@ An array of contextual data may also be passed to the function: #### `literal()` {.collection-method} -"The `literal` function creates a new [stdClass](https://www.php.net/manual/en/class.stdclass.php) instance with the given named arguments as properties: +The `literal` function creates a new [stdClass](https://www.php.net/manual/en/class.stdclass.php) instance with the given named arguments as properties: $obj = literal( name: 'Joe', From fd2151dcac919bba2935dc325b9490c2185fe594 Mon Sep 17 00:00:00 2001 From: Maru <56859729+maru0914@users.noreply.github.com> Date: Sat, 6 Jul 2024 01:00:32 +0900 Subject: [PATCH 1715/2609] Fix results of Number::currency() example (#9738) --- helpers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helpers.md b/helpers.md index aaac1146fa0..7f2ecbba42e 100644 --- a/helpers.md +++ b/helpers.md @@ -1218,15 +1218,15 @@ The `Number::currency` method returns the currency representation of the given v $currency = Number::currency(1000); - // $1,000 + // $1,000.00 $currency = Number::currency(1000, in: 'EUR'); - // €1,000 + // €1,000.00 $currency = Number::currency(1000, in: 'EUR', locale: 'de'); - // 1.000 € + // 1.000,00 € #### `Number::fileSize()` {.collection-method} From 8fbc1e3a96523dbf21116b58e213e69d110350a6 Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Fri, 5 Jul 2024 17:03:07 +0100 Subject: [PATCH 1716/2609] Align with `waitForRoute()` making it more obvious how to pass parameters (#9740) --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index 6987eb7d3c2..22acb86dccc 100644 --- a/dusk.md +++ b/dusk.md @@ -423,7 +423,7 @@ The `visit` method may be used to navigate to a given URI within your applicatio You may use the `visitRoute` method to navigate to a [named route](/docs/{{version}}/routing#named-routes): - $browser->visitRoute('login'); + $browser->visitRoute($routeName, $parameters); You may navigate "back" and "forward" using the `back` and `forward` methods: From a39ad9cb9e0512df3cba8163a405616683373459 Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Mon, 8 Jul 2024 16:42:43 +0200 Subject: [PATCH 1717/2609] Improve database support for JSON queries (#9744) --- queries.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/queries.md b/queries.md index ca6417d44d3..8646b135512 100644 --- a/queries.md +++ b/queries.md @@ -552,7 +552,7 @@ WHERE published = true AND ( ### JSON Where Clauses -Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 8.0+, PostgreSQL 12.0+, SQL Server 2017+, and SQLite 3.39.0+ (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator: +Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MariaDB 10.3+, MySQL 8.0+, PostgreSQL 12.0+, SQL Server 2017+, and SQLite 3.39.0+. To query a JSON column, use the `->` operator: $users = DB::table('users') ->where('preferences->dining->meal', 'salad') @@ -564,7 +564,7 @@ You may use `whereJsonContains` to query JSON arrays: ->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: +If your application uses the MariaDB, MySQL, or PostgreSQL databases, you may pass an array of values to the `whereJsonContains` method: $users = DB::table('users') ->whereJsonContains('options->languages', ['en', 'de']) @@ -1045,7 +1045,7 @@ DB::table('users')->updateOrInsert( ### Updating JSON Columns -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+: +When updating a JSON column, you should use `->` syntax to update the appropriate key in the JSON object. This operation is supported on MariaDB 10.3+, MySQL 5.7+, and PostgreSQL 9.5+: $affected = DB::table('users') ->where('id', 1) From 714d793e2cdb215897ca5f3618a54ef9505ba802 Mon Sep 17 00:00:00 2001 From: Anne Douwe Bouma Date: Mon, 8 Jul 2024 16:43:52 +0200 Subject: [PATCH 1718/2609] Precognition: Replace attribute `for` with `htmlFor` in React example (#9743) The `for` attribute is not allowed in JSX syntax and should be replaced with `htmlFor` --- precognition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/precognition.md b/precognition.md index 4dc62c6de60..600c96ae9b2 100644 --- a/precognition.md +++ b/precognition.md @@ -247,7 +247,7 @@ export default function Form() { return (
    - + {form.invalid('name') &&
    {form.errors.name}
    } - + Date: Tue, 9 Jul 2024 02:27:24 +0300 Subject: [PATCH 1719/2609] [11.x] Update cache.md (#9745) Updated cache lock --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index e2a6751eee7..dc896d2a3db 100644 --- a/cache.md +++ b/cache.md @@ -325,7 +325,7 @@ If the lock is not available at the moment you request it, you may instruct Lara } catch (LockTimeoutException $e) { // Unable to acquire lock... } finally { - $lock?->release(); + $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: From fe305fbe9abdd1e8e1c8b2af182407f375b8de83 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Tue, 9 Jul 2024 03:30:26 +0330 Subject: [PATCH 1720/2609] [11.x] Add `pairs` method to Number (#9742) * Update helpers.md * Update helpers.md * Improve database support for JSON queries (#9744) * Precognition: Replace attribute `for` with `htmlFor` in React example (#9743) The `for` attribute is not allowed in JSX syntax and should be replaced with `htmlFor` * Update helpers.md * Update helpers.md * Update helpers.md --------- Co-authored-by: Jonas Staudenmeir Co-authored-by: Anne Douwe Bouma Co-authored-by: Taylor Otwell --- helpers.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/helpers.md b/helpers.md index 7f2ecbba42e..3baf5b3fa19 100644 --- a/helpers.md +++ b/helpers.md @@ -97,6 +97,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Number::forHumans](#method-number-for-humans) [Number::format](#method-number-format) [Number::ordinal](#method-number-ordinal) +[Number::pairs](#method-number-pairs) [Number::percentage](#method-number-percentage) [Number::spell](#method-number-spell) [Number::useLocale](#method-number-use-locale) @@ -1308,6 +1309,23 @@ The `Number::ordinal` method returns a number's ordinal representation: // 21st + +#### `Number::pairs()` {.collection-method} + +The `Number::pairs` method generates an array of number pairs (sub-ranges) based on a specified range and step value. This method can be useful for dividing a larger range of numbers into smaller, manageable sub-ranges for things like pagination or batching tasks. The `pairs` method returns an array of arrays, where each inner array represents a pair (sub-range) of numbers: + +```php +use Illuminate\Support\Number; + +$result = Number::pairs(25, 10); + +// [[1, 10], [11, 20], [21, 25]] + +$result = Number::pairs(25, 10, offset: 0); + +// [[0, 10], [10, 20], [20, 25]] +``` + #### `Number::percentage()` {.collection-method} From a70cb211a1998c65f1457e0a88bfcfedbd523a3a Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 10 Jul 2024 01:03:18 +1000 Subject: [PATCH 1721/2609] [11.x] Document Pennant's `before` hook (#9746) * Document Pennant's `before` hook * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pennant.md b/pennant.md index 1669713113b..3970e9bcdeb 100644 --- a/pennant.md +++ b/pennant.md @@ -10,6 +10,7 @@ - [The `HasFeatures` Trait](#the-has-features-trait) - [Blade Directive](#blade-directive) - [Middleware](#middleware) + - [Intercepting Feature Checks](#intercepting-feature-checks) - [In-Memory Cache](#in-memory-cache) - [Scope](#scope) - [Specifying the Scope](#specifying-the-scope) @@ -123,6 +124,7 @@ When writing a feature class, you only need to define a `resolve` method, which namespace App\Features; +use App\Models\User; use Illuminate\Support\Lottery; class NewApi @@ -402,6 +404,78 @@ public function boot(): void } ``` + +### Intercepting Feature Checks + +Sometimes it can be useful to perform some in-memory checks before retrieving the stored value of a given feature. Imagine you are developing a new API behind a feature flag and want the ability to disable the new API without losing any of the resolved feature values in storage. If you notice a bug in the new API, you could easily disable it for everyone except internal team members, fix the bug, and then re-enable the new API for the users that previously had access to the feature. + +You can achieve this with a [class-based feature's](#class-based-features) `before` method. When present, the `before` method is always run in-memory before retrieving the value from storage. If a non-`null` value is returned from the method, it will be used in place of the feature's stored value for the duration of the request: + +```php +isInternalTeamMember(); + } + } + + /** + * Resolve the feature's initial value. + */ + public function resolve(User $user): mixed + { + return match (true) { + $user->isInternalTeamMember() => true, + $user->isHighTrafficCustomer() => false, + default => Lottery::odds(1 / 100), + }; + } +} +``` + +You could also use this feature to schedule the global rollout of a feature that was previously behind a feature flag: + +```php +isInternalTeamMember(); + } + + if (Carbon::parse(Config::get('features.new-api.rollout-date'))->isPast()) { + return true; + } + } + + // ... +} +``` + ### In-Memory Cache From c87c7d16189dec8535f25239c117c300a15a75d9 Mon Sep 17 00:00:00 2001 From: Iman Date: Wed, 10 Jul 2024 22:48:40 +0330 Subject: [PATCH 1722/2609] Add docs for the `empty` preset (#9747) * Add docs for the `empty` preset * Update pint.md --------- Co-authored-by: Taylor Otwell --- pint.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pint.md b/pint.md index dcf69c9c294..f7da25d53d4 100644 --- a/pint.md +++ b/pint.md @@ -87,7 +87,7 @@ pint --config vendor/my-company/coding-style/pint.json ### Presets -Presets defines a set of rules that can be used to fix code style issues in your code. By default, Pint uses the `laravel` preset, which fixes issues by following the opinionated coding style of Laravel. However, you may specify a different preset by providing the `--preset` option to Pint: +Presets define a set of rules that can be used to fix code style issues in your code. By default, Pint uses the `laravel` preset, which fixes issues by following the opinionated coding style of Laravel. However, you may specify a different preset by providing the `--preset` option to Pint: ```shell pint --preset psr12 @@ -101,14 +101,14 @@ If you wish, you may also set the preset in your project's `pint.json` file: } ``` -Pint's currently supported presets are: `laravel`, `per`, `psr12`, and `symfony`. +Pint's currently supported presets are: `laravel`, `per`, `psr12`, `symfony`, and `empty`. ### Rules Rules are style guidelines that Pint will use to fix code style issues in your code. As mentioned above, presets are predefined groups of rules that should be perfect for most PHP projects, so you typically will not need to worry about the individual rules they contain. -However, if you wish, you may enable or disable specific rules in your `pint.json` file: +However, if you wish, you may enable or disable specific rules in your `pint.json` file or use the `empty` preset and define the rules from scratch: ```json { From 111cc1d62d3e4123c73cd7ab5082cc0c9b7416d4 Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Fri, 12 Jul 2024 16:07:06 +0200 Subject: [PATCH 1723/2609] [11.x] Explicitly document MariaDB support (#9751) * Explicitly document MariaDB support * Explicitly document MariaDB support --- eloquent.md | 2 +- migrations.md | 32 ++++++++++++++++---------------- queries.md | 6 +++--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/eloquent.md b/eloquent.md index a4142ed29a1..c31d51832bd 100644 --- a/eloquent.md +++ b/eloquent.md @@ -897,7 +897,7 @@ Eloquent's `upsert` method may be used to update or create records in a single, ], uniqueBy: ['departure', 'destination'], update: ['price']); > [!WARNING] -> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. +> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MariaDB and MySQL database drivers ignore the second argument of the `upsert` method and always use the "primary" and "unique" indexes of the table to detect existing records. ## Deleting Models diff --git a/migrations.md b/migrations.md index e5a31935952..58f869b185f 100644 --- a/migrations.md +++ b/migrations.md @@ -71,7 +71,7 @@ php artisan schema:dump --database=testing --prune You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure. > [!WARNING] -> Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. +> Migration squashing is only available for the MariaDB, MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. ## Migration Structure @@ -290,7 +290,7 @@ If you want to perform a schema operation on a database connection that is not y $table->id(); }); -In addition, a few other properties and methods may be used to define other aspects of the table's creation. The `engine` property may be used to specify the table's storage engine when using MySQL: +In addition, a few other properties and methods may be used to define other aspects of the table's creation. The `engine` property may be used to specify the table's storage engine when using MariaDB or MySQL: Schema::create('users', function (Blueprint $table) { $table->engine('InnoDB'); @@ -298,7 +298,7 @@ In addition, a few other properties and methods may be used to define other aspe // ... }); -The `charset` and `collation` properties may be used to specify the character set and collation for the created table when using MySQL: +The `charset` and `collation` properties may be used to specify the character set and collation for the created table when using MariaDB or MySQL: Schema::create('users', function (Blueprint $table) { $table->charset('utf8mb4'); @@ -315,7 +315,7 @@ The `temporary` method may be used to indicate that the table should be "tempora // ... }); -If you would like to add a "comment" to a database table, you may invoke the `comment` method on the table instance. Table comments are currently only supported by MySQL and PostgreSQL: +If you would like to add a "comment" to a database table, you may invoke the `comment` method on the table instance. Table comments are currently only supported by MariaDB, MySQL, and PostgreSQL: Schema::create('calculations', function (Blueprint $table) { $table->comment('Business calculations'); @@ -941,21 +941,21 @@ The following table contains all of the available column modifiers. This list do Modifier | Description -------- | ----------- -`->after('column')` | Place the column "after" another column (MySQL). +`->after('column')` | Place the column "after" another column (MariaDB / MySQL). `->autoIncrement()` | Set INTEGER columns as auto-incrementing (primary key). -`->charset('utf8mb4')` | Specify a character set for the column (MySQL). +`->charset('utf8mb4')` | Specify a character set for the column (MariaDB / MySQL). `->collation('utf8mb4_unicode_ci')` | Specify a collation for the column. -`->comment('my comment')` | Add a comment to a column (MySQL / PostgreSQL). +`->comment('my comment')` | Add a comment to a column (MariaDB / 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). -`->invisible()` | Make the column "invisible" to `SELECT *` queries (MySQL). +`->first()` | Place the column "first" in the table (MariaDB / MySQL). +`->from($integer)` | Set the starting value of an auto-incrementing field (MariaDB / MySQL / PostgreSQL). +`->invisible()` | Make the column "invisible" to `SELECT *` queries (MariaDB / MySQL). `->nullable($value = true)` | Allow NULL values to be inserted into the column. -`->storedAs($expression)` | Create a stored generated column (MySQL / PostgreSQL / SQLite). -`->unsigned()` | Set INTEGER columns as UNSIGNED (MySQL). +`->storedAs($expression)` | Create a stored generated column (MariaDB / MySQL / PostgreSQL / SQLite). +`->unsigned()` | Set INTEGER columns as UNSIGNED (MariaDB / 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 (MySQL). -`->virtualAs($expression)` | Create a virtual generated column (MySQL / SQLite). +`->useCurrentOnUpdate()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated (MariaDB / MySQL). +`->virtualAs($expression)` | Create a virtual generated column (MariaDB / MySQL / SQLite). `->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). @@ -992,7 +992,7 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi #### Column Order -When using the MySQL database, the `after` method may be used to add columns after an existing column in the schema: +When using the MariaDB or MySQL database, the `after` method may be used to add columns after an existing column in the schema: $table->after('password', function (Blueprint $table) { $table->string('address_line1'); @@ -1101,7 +1101,7 @@ Command | Description `$table->primary(['id', 'parent_id']);` | Adds composite keys. `$table->unique('email');` | Adds a unique index. `$table->index('state');` | Adds an index. -`$table->fullText('body');` | Adds a full text index (MySQL / PostgreSQL). +`$table->fullText('body');` | Adds a full text index (MariaDB / MySQL / PostgreSQL). `$table->fullText('body')->language('english');` | Adds a full text index of the specified language (PostgreSQL). `$table->spatialIndex('location');` | Adds a spatial index (except SQLite). diff --git a/queries.md b/queries.md index 8646b135512..e873bd8fadb 100644 --- a/queries.md +++ b/queries.md @@ -804,9 +804,9 @@ Or, you may need to construct a "where" clause that compares a column to the res ### Full Text Where Clauses > [!WARNING] -> Full text where clauses are currently supported by MySQL and PostgreSQL. +> Full text where clauses are currently supported by MariaDB, MySQL, and PostgreSQL. -The `whereFullText` and `orWhereFullText` methods may be used to add full text "where" clauses to a query for columns that have [full text indexes](/docs/{{version}}/migrations#available-index-types). These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a `MATCH AGAINST` clause will be generated for applications utilizing MySQL: +The `whereFullText` and `orWhereFullText` methods may be used to add full text "where" clauses to a query for columns that have [full text indexes](/docs/{{version}}/migrations#available-index-types). These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a `MATCH AGAINST` clause will be generated for applications utilizing MariaDB or MySQL: $users = DB::table('users') ->whereFullText('bio', 'web developer') @@ -1002,7 +1002,7 @@ The `upsert` method will insert records that do not exist and update the records 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. > [!WARNING] -> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. +> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MariaDB and MySQL database drivers ignore the second argument of the `upsert` method and always use the "primary" and "unique" indexes of the table to detect existing records. ## Update Statements From ded3003a722af87b42cece21cda026273a22f30a Mon Sep 17 00:00:00 2001 From: Tyler Smith Date: Fri, 12 Jul 2024 07:12:08 -0700 Subject: [PATCH 1724/2609] Elaborate on Laravel installer's features (#9750) * Elaborate on Laravel installer's features * Update installation.md --------- Co-authored-by: Taylor Otwell --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index beeff46dcfd..43c643a3038 100644 --- a/installation.md +++ b/installation.md @@ -64,7 +64,7 @@ After you have installed PHP and Composer, you may create a new Laravel project composer create-project laravel/laravel example-app ``` -Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer: +Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer. The Laravel installer allows you to select your preferred testing framework, database, and starter kit when creating new applications: ```nothing composer global require laravel/installer From bc259b0c5291bd5c5fb0390c1a838656a09271bc Mon Sep 17 00:00:00 2001 From: Mike Scott Date: Fri, 12 Jul 2024 16:09:33 +0100 Subject: [PATCH 1725/2609] Corrected typo in `assetReleased` to `assertReleased` (#9752) Trivial typo change, added missing 'r'. --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 723b65bba01..290edd6b062 100644 --- a/queues.md +++ b/queues.md @@ -2411,7 +2411,7 @@ In addition, you may occasionally need to test an individual job's interaction w Sometimes, you may need to test that a queued job [releases itself back onto the queue](#manually-releasing-a-job). Or, you may need to test that the job deleted itself. You may test these queue interactions by instantiating the job and invoking the `withFakeQueueInteractions` method. -Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assetReleased`, `assertDeleted`, and `assertFailed` methods may be used to make assertions against the job's queue interactions: +Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assertReleased`, `assertDeleted`, and `assertFailed` methods may be used to make assertions against the job's queue interactions: ```php use App\Jobs\ProcessPodcast; From 98c439491fca8234a43a8edd00d144cbcafadabe Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 12 Jul 2024 13:04:24 -0500 Subject: [PATCH 1726/2609] update docs for foundation queueable --- context.md | 2 +- horizon.md | 9 +++------ queues.md | 36 +++++++++++------------------------- 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/context.md b/context.md index 4f70e34ca6f..1ef39c20f83 100644 --- a/context.md +++ b/context.md @@ -76,7 +76,7 @@ When the job is dispatched, any information currently stored in the context is c ```php class ProcessPodcast implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Queueable; // ... diff --git a/horizon.md b/horizon.md index 66d44d8999a..3e34b47cf28 100644 --- a/horizon.md +++ b/horizon.md @@ -189,7 +189,7 @@ Alternatively, the job you wish to silence can implement the `Laravel\Horizon\Co class ProcessPodcast implements ShouldQueue, Silenced { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Queueable; // ... } @@ -307,15 +307,12 @@ Horizon allows you to assign “tags” to jobs, including mailables, broadcast namespace App\Jobs; use App\Models\Video; - use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; - use Illuminate\Foundation\Bus\Dispatchable; - use Illuminate\Queue\InteractsWithQueue; - use Illuminate\Queue\SerializesModels; + use Illuminate\Foundation\Queue\Queueable; class RenderVideo implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Queueable; /** * Create a new job instance. diff --git a/queues.md b/queues.md index 57c3f6fecde..1554454412f 100644 --- a/queues.md +++ b/queues.md @@ -181,15 +181,12 @@ Job classes are very simple, normally containing only a `handle` method that is use App\Models\Podcast; use App\Services\AudioProcessor; - use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; - use Illuminate\Foundation\Bus\Dispatchable; - use Illuminate\Queue\InteractsWithQueue; - use Illuminate\Queue\SerializesModels; + use Illuminate\Foundation\Queue\Queueable; class ProcessPodcast implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Queueable; /** * Create a new job instance. @@ -207,7 +204,7 @@ Job classes are very simple, normally containing only a `handle` method that is } } -In this example, note that we were able to pass an [Eloquent model](/docs/{{version}}/eloquent) directly into the queued job's constructor. Because of the `SerializesModels` trait that the job is using, Eloquent models and their loaded relationships will be gracefully serialized and unserialized when the job is processing. +In this example, note that we were able to pass an [Eloquent model](/docs/{{version}}/eloquent) directly into the queued job's constructor. Because of the `Queueable` trait that the job is using, Eloquent models and their loaded relationships will be gracefully serialized and unserialized when the job is processing. If your queued job accepts an Eloquent model in its constructor, only the identifier for the model will be serialized onto the queue. When the job is actually handled, the queue system will automatically re-retrieve the full model instance and its loaded relationships from the database. This approach to model serialization allows for much smaller job payloads to be sent to your queue driver. @@ -973,15 +970,12 @@ Alternatively, you may specify the job's queue by calling the `onQueue` method w namespace App\Jobs; - use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; - use Illuminate\Foundation\Bus\Dispatchable; - use Illuminate\Queue\InteractsWithQueue; - use Illuminate\Queue\SerializesModels; + use Illuminate\Foundation\Queue\Queueable; class ProcessPodcast implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Queueable; /** * Create a new job instance. @@ -1036,15 +1030,12 @@ Alternatively, you may specify the job's connection by calling the `onConnection namespace App\Jobs; - use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; - use Illuminate\Foundation\Bus\Dispatchable; - use Illuminate\Queue\InteractsWithQueue; - use Illuminate\Queue\SerializesModels; + use Illuminate\Foundation\Queue\Queueable; class ProcessPodcast implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Queueable; /** * Create a new job instance. @@ -1277,15 +1268,12 @@ To define a batchable job, you should [create a queueable job](#creating-jobs) a namespace App\Jobs; use Illuminate\Bus\Batchable; - use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; - use Illuminate\Foundation\Bus\Dispatchable; - use Illuminate\Queue\InteractsWithQueue; - use Illuminate\Queue\SerializesModels; + use Illuminate\Foundation\Queue\Queueable; class ImportCsv implements ShouldQueue { - use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Batchable, Queueable; /** * Execute the job. @@ -1932,15 +1920,13 @@ When a particular job fails, you may want to send an alert to your users or reve use App\Models\Podcast; use App\Services\AudioProcessor; - use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; - use Illuminate\Queue\InteractsWithQueue; - use Illuminate\Queue\SerializesModels; + use Illuminate\Foundation\Queue\Queueable; use Throwable; class ProcessPodcast implements ShouldQueue { - use InteractsWithQueue, Queueable, SerializesModels; + use Queueable; /** * Create a new job instance. From df370906ae04c91bad1acf44487e7471a8290510 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:58:17 +0700 Subject: [PATCH 1727/2609] Fix typos in Authorization (#9761) --- authorization.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authorization.md b/authorization.md index f894ce11a72..d795ac76c09 100644 --- a/authorization.md +++ b/authorization.md @@ -188,7 +188,7 @@ When using the `Gate::authorize` method, which throws an `AuthorizationException // The action is authorized... - + #### Customizing The HTTP Response Status When an action is denied via a Gate, a `403` HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the `denyWithStatus` static constructor on the `Illuminate\Auth\Access\Response` class: @@ -384,7 +384,7 @@ When using the `Gate::authorize` method, which throws an `AuthorizationException // The action is authorized... - + #### Customizing the HTTP Response Status When an action is denied via a policy method, a `403` HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the `denyWithStatus` static constructor on the `Illuminate\Auth\Access\Response` class: From 89fca51366bd2495622ebf3c2f413793bc18e256 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:58:47 +0700 Subject: [PATCH 1728/2609] Fix errors directory in URL Generation (#9759) * Update errors directory in url generation docs * Use named arguments in `view` method --- errors.md | 2 +- urls.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/errors.md b/errors.md index c04a842ceea..1562d825a85 100644 --- a/errors.md +++ b/errors.md @@ -187,7 +187,7 @@ The closure passed to the `render` method should return an instance of `Illumina ->withExceptions(function (Exceptions $exceptions) { $exceptions->render(function (InvalidOrderException $e, Request $request) { - return response()->view('errors.invalid-order', [], 500); + return response()->view('errors.invalid-order', status: 500); }); }) diff --git a/urls.md b/urls.md index ab71f573d3d..35a730ae271 100644 --- a/urls.md +++ b/urls.md @@ -177,7 +177,7 @@ When someone visits a signed URL that has expired, they will receive a generic e ->withExceptions(function (Exceptions $exceptions) { $exceptions->render(function (InvalidSignatureException $e) { - return response()->view('error.link-expired', [], 403); + return response()->view('errors.link-expired', status: 403); }); }) From 7e9db237fa22681070c7dc42553fc62205443eb7 Mon Sep 17 00:00:00 2001 From: Gin Date: Mon, 15 Jul 2024 22:00:23 +0700 Subject: [PATCH 1729/2609] Update upgrade.md: Move Spatie Once Package notice to Medium impact list (#9758) Move "Spatie Once Package" item to Medium Impact Changes according to the detail part: "Likelihood Of Impact: Medium" --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index a5d380c972e..c8eea49b09b 100644 --- a/upgrade.md +++ b/upgrade.md @@ -24,6 +24,7 @@ - [Carbon 3](#carbon-3) - [Password Rehashing](#password-rehashing) - [Per-Second Rate Limiting](#per-second-rate-limiting) +- [Spatie Once Package](#spatie-once-package)
    @@ -35,7 +36,6 @@ - [Doctrine DBAL Removal](#doctrine-dbal-removal) - [Eloquent Model `casts` Method](#eloquent-model-casts-method) - [Spatial Types](#spatial-types) -- [Spatie Once Package](#spatie-once-package) - [The `Enumerable` Contract](#the-enumerable-contract) - [The `UserProvider` Contract](#the-user-provider-contract) - [The `Authenticatable` Contract](#the-authenticatable-contract) From cfc8e5cce55b283a18c463a7026ee2c0a9b74de6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 15 Jul 2024 15:00:20 -0500 Subject: [PATCH 1730/2609] wip --- container.md | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/container.md b/container.md index 1fe0e8921cc..9030788ac7b 100644 --- a/container.md +++ b/container.md @@ -29,30 +29,30 @@ Let's look at a simple example: namespace App\Http\Controllers; - use App\Repositories\UserRepository; + use App\Services\AppleMusic; use Illuminate\View\View; - class UserController extends Controller + class PodcastController extends Controller { /** * Create a new controller instance. */ public function __construct( - protected UserRepository $users, + protected AppleMusic $apple, ) {} /** - * Show the profile for the given user. + * Show information about the given podcast. */ public function show(string $id): View { - $user = $this->users->find($id); - - return view('user.profile', ['user' => $user]); + return view('podcasts.show', [ + 'podcast' => $this->apple->findPodcast($id) + ]); } } -In this example, the `UserController` needs to retrieve users from a data source. So, we will **inject** a service that is able to retrieve users. In this context, our `UserRepository` most likely uses [Eloquent](/docs/{{version}}/eloquent) to retrieve user information from the database. However, since the repository is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the `UserRepository` when testing our application. +In this example, the `PodcastController` needs to retrieve podcasts from a data source such as Apple Music. So, we will **inject** a service that is able to retrieve podcasts. Since the service is injected, we are able to easily "mock", or create a dummy implementation of the `AppleMusic` service when testing our application. A deep understanding of the Laravel service container is essential to building a powerful, large application, as well as for contributing to the Laravel core itself. @@ -394,26 +394,23 @@ For example, you may type-hint a repository defined by your application in a con namespace App\Http\Controllers; - use App\Repositories\UserRepository; - use App\Models\User; + use App\Services\AppleMusic; - class UserController extends Controller + class PodcastController extends Controller { /** * Create a new controller instance. */ public function __construct( - protected UserRepository $users, + protected AppleMusic $apple, ) {} /** - * Show the user with the given ID. + * Show information about the given podcast. */ - public function show(string $id): User + public function show(string $id): Podcast { - $user = $this->users->findOrFail($id); - - return $user; + return $this->apple->findPodcast($id); } } @@ -426,14 +423,14 @@ Sometimes you may wish to invoke a method on an object instance while allowing t namespace App; - use App\Repositories\UserRepository; + use App\Services\AppleMusic; - class UserReport + class PodcastStats { /** - * Generate a new user report. + * Generate a new podcast stats report. */ - public function generate(UserRepository $repository): array + public function generate(AppleMusic $apple): array { return [ // ... @@ -443,17 +440,17 @@ Sometimes you may wish to invoke a method on an object instance while allowing t You may invoke the `generate` method via the container like so: - use App\UserReport; + use App\PodcastStats; use Illuminate\Support\Facades\App; - $report = App::call([new UserReport, 'generate']); + $stats = App::call([new PodcastStats, 'generate']); The `call` method accepts any PHP callable. The container's `call` method may even be used to invoke a closure while automatically injecting its dependencies: - use App\Repositories\UserRepository; + use App\Services\AppleMusic; use Illuminate\Support\Facades\App; - $result = App::call(function (UserRepository $repository) { + $result = App::call(function (AppleMusic $apple) { // ... }); From 215a683e25f17c77925f8cadac7cedab3c395154 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Tue, 16 Jul 2024 04:42:46 +0700 Subject: [PATCH 1731/2609] Use consistent uppercase for DOCTYPE (#9762) This is in line with the Google HTML guide and with other files in the framework. https://google.github.io/styleguide/htmlcssguide.html#HTML_Validity --- vite.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vite.md b/vite.md index c14bbca2bef..0b8d56c4489 100644 --- a/vite.md +++ b/vite.md @@ -201,7 +201,7 @@ If your file changes are not being reflected in the browser while the developmen With your Vite entry points configured, you may now reference them in a `@vite()` Blade directive that you add to the `` of your application's root template: ```blade - + {{-- ... --}} @@ -212,7 +212,7 @@ With your Vite entry points configured, you may now reference them in a `@vite() If you're importing your CSS via JavaScript, you only need to include the JavaScript entry point: ```blade - + {{-- ... --}} From 0a0ab0c44caedef6902a2b03425c0b803d83e688 Mon Sep 17 00:00:00 2001 From: PovilasKorop Date: Tue, 16 Jul 2024 16:50:55 +0300 Subject: [PATCH 1732/2609] Container: replace word "repository" with "service" (#9763) --- container.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.md b/container.md index 9030788ac7b..dc804e9ba04 100644 --- a/container.md +++ b/container.md @@ -388,7 +388,7 @@ If you would like to have the Laravel container instance itself injected into a Alternatively, and importantly, you may type-hint the dependency in the constructor of a class that is resolved by the container, including [controllers](/docs/{{version}}/controllers), [event listeners](/docs/{{version}}/events), [middleware](/docs/{{version}}/middleware), and more. Additionally, you may type-hint dependencies in the `handle` method of [queued jobs](/docs/{{version}}/queues). In practice, this is how most of your objects should be resolved by the container. -For example, you may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class: +For example, you may type-hint a service defined by your application in a controller's constructor. The service will automatically be resolved and injected into the class: Date: Tue, 16 Jul 2024 13:56:11 -0400 Subject: [PATCH 1733/2609] Example of `assertSent` to email address (#9767) * Example of `assertSent` to email address * Update mail.md --------- Co-authored-by: Taylor Otwell --- mail.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index bd7e3c36997..b1ba0ab615f 100644 --- a/mail.md +++ b/mail.md @@ -700,7 +700,7 @@ Laravel's mail capabilities are powered by Symfony Mailer. Laravel allows you to use Illuminate\Mail\Mailables\Envelope; use Symfony\Component\Mime\Email; - + /** * Get the message envelope. */ @@ -1133,6 +1133,12 @@ test('orders can be shipped', function () { // Assert a mailable was sent twice... Mail::assertSent(OrderShipped::class, 2); + // Assert a mailable was sent to an email address... + Mail::assertSent(OrderShipped::class, 'example@laravel.com'); + + // Assert a mailable was sent to multiple email addresses... + Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']); + // Assert a mailable was not sent... Mail::assertNotSent(AnotherMailable::class); @@ -1167,6 +1173,12 @@ class ExampleTest extends TestCase // Assert a mailable was sent twice... Mail::assertSent(OrderShipped::class, 2); + // Assert a mailable was sent to an email address... + Mail::assertSent(OrderShipped::class, 'example@laravel.com'); + + // Assert a mailable was sent to multiple email addresses... + Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']); + // Assert a mailable was not sent... Mail::assertNotSent(AnotherMailable::class); From ce7e2f4cbd61092f04d543a2c180f54cb94d0229 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:42:51 +0700 Subject: [PATCH 1734/2609] Remove whitespaces (#9775) --- artisan.md | 2 +- console-tests.md | 2 +- controllers.md | 2 +- csrf.md | 4 ++-- events.md | 2 +- localization.md | 2 +- logging.md | 4 ++-- migrations.md | 2 +- queries.md | 2 +- reverb.md | 2 +- validation.md | 6 +++--- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/artisan.md b/artisan.md index 645163c9c81..1681994dd65 100644 --- a/artisan.md +++ b/artisan.md @@ -669,7 +669,7 @@ If necessary, you may also manually register commands by providing the command's SendEmails::class, ]) - When Artisan boots, all the commands in your application will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan. +When Artisan boots, all the commands in your application will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan. ## Programmatically Executing Commands diff --git a/console-tests.md b/console-tests.md index b949302012a..9b8c8d991fe 100644 --- a/console-tests.md +++ b/console-tests.md @@ -108,7 +108,7 @@ public function test_console_command(): void } ``` - The `expectsOutputToContain` and `doesntExpectOutputToContain` methods may be used to make assertions against a portion of the output: +The `expectsOutputToContain` and `doesntExpectOutputToContain` methods may be used to make assertions against a portion of the output: ```php tab=Pest test('console command', function () { diff --git a/controllers.md b/controllers.md index d85b60ef152..b49e0712e1f 100644 --- a/controllers.md +++ b/controllers.md @@ -337,7 +337,7 @@ By default, `Route::resource` will create the route parameters for your resource 'users' => 'admin_user' ]); - The example above generates the following URI for the resource's `show` route: +The example above generates the following URI for the resource's `show` route: /users/{admin_user} diff --git a/csrf.md b/csrf.md index 897600bd96f..fb825fb71b1 100644 --- a/csrf.md +++ b/csrf.md @@ -28,9 +28,9 @@ Without CSRF protection, a malicious website could create an HTML form that poin ``` - If the malicious website automatically submits the form when the page is loaded, the malicious user only needs to lure an unsuspecting user of your application to visit their website and their email address will be changed in your application. +If the malicious website automatically submits the form when the page is loaded, the malicious user only needs to lure an unsuspecting user of your application to visit their website and their email address will be changed in your application. - To prevent this vulnerability, we need to inspect every incoming `POST`, `PUT`, `PATCH`, or `DELETE` request for a secret session value that the malicious application is unable to access. +To prevent this vulnerability, we need to inspect every incoming `POST`, `PUT`, `PATCH`, or `DELETE` request for a secret session value that the malicious application is unable to access. ## Preventing CSRF Requests diff --git a/events.md b/events.md index 2c84b318632..916aad6b623 100644 --- a/events.md +++ b/events.md @@ -513,7 +513,7 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th } } - If you would like to conditionally dispatch an event, you may use the `dispatchIf` and `dispatchUnless` methods: +If you would like to conditionally dispatch an event, you may use the `dispatchIf` and `dispatchUnless` methods: OrderShipped::dispatchIf($condition, $order); diff --git a/localization.md b/localization.md index c001b512327..ea12a85f5f1 100644 --- a/localization.md +++ b/localization.md @@ -152,7 +152,7 @@ You may retrieve translation strings from your language files using the `__` hel If the specified translation string does not exist, the `__` function will return the translation string key. So, using the example above, the `__` function would return `messages.welcome` if the translation string does not exist. - If you are using your [default translation strings as your translation keys](#using-translation-strings-as-keys), you should pass the default translation of your string to the `__` function; +If you are using your [default translation strings as your translation keys](#using-translation-strings-as-keys), you should pass the default translation of your string to the `__` function; echo __('I love programming.'); diff --git a/logging.md b/logging.md index 2ea1ff8e682..ae71565accf 100644 --- a/logging.md +++ b/logging.md @@ -421,9 +421,9 @@ If you are using a Monolog handler that is capable of providing its own formatte #### Monolog Processors - Monolog can also process messages before logging them. You can create your own processors or use the [existing processors offered by Monolog](https://github.com/Seldaek/monolog/tree/main/src/Monolog/Processor). +Monolog can also process messages before logging them. You can create your own processors or use the [existing processors offered by Monolog](https://github.com/Seldaek/monolog/tree/main/src/Monolog/Processor). - If you would like to customize the processors for a `monolog` driver, add a `processors` configuration value to your channel's configuration: +If you would like to customize the processors for a `monolog` driver, add a `processors` configuration value to your channel's configuration: 'memory' => [ 'driver' => 'monolog', diff --git a/migrations.md b/migrations.md index 58f869b185f..b6f72109926 100644 --- a/migrations.md +++ b/migrations.md @@ -191,7 +191,7 @@ php artisan migrate:rollback --step=5 You may roll back a specific "batch" of migrations by providing the `batch` option to the `rollback` command, where the `batch` option corresponds to a batch value within your application's `migrations` database table. For example, the following command will roll back all migrations in batch three: ```shell - php artisan migrate:rollback --batch=3 +php artisan migrate:rollback --batch=3 ``` If you would like to see the SQL statements that will be executed by the migrations without actually running them, you may provide the `--pretend` flag to the `migrate:rollback` command: diff --git a/queries.md b/queries.md index e873bd8fadb..334d2ed1283 100644 --- a/queries.md +++ b/queries.md @@ -116,7 +116,7 @@ If you would like to retrieve an `Illuminate\Support\Collection` instance contai echo $title; } - You may specify the column that the resulting collection should use as its keys by providing a second argument to the `pluck` method: +You may specify the column that the resulting collection should use as its keys by providing a second argument to the `pluck` method: $titles = DB::table('users')->pluck('title', 'name'); diff --git a/reverb.md b/reverb.md index 627068d2965..ab054f31696 100644 --- a/reverb.md +++ b/reverb.md @@ -283,7 +283,7 @@ The configuration above will allow up to 10,000 Nginx workers per process to be Unix-based operating systems typically limit the number of ports which can be opened on the server. You may see the current allowed range via the following command: ```sh - cat /proc/sys/net/ipv4/ip_local_port_range +cat /proc/sys/net/ipv4/ip_local_port_range # 32768 60999 ``` diff --git a/validation.md b/validation.md index 1f24309e2ea..550a5531b9a 100644 --- a/validation.md +++ b/validation.md @@ -1547,7 +1547,7 @@ The field under validation must not be present in the input data. #### missing_if:_anotherfield_,_value_,... - The field under validation must not be present if the _anotherfield_ field is equal to any _value_. +The field under validation must not be present if the _anotherfield_ field is equal to any _value_. #### missing_unless:_anotherfield_,_value_ @@ -1557,12 +1557,12 @@ The field under validation must not be present unless the _anotherfield_ field i #### missing_with:_foo_,_bar_,... - The field under validation must not be present _only if_ any of the other specified fields are present. +The field under validation must not be present _only if_ any of the other specified fields are present. #### missing_with_all:_foo_,_bar_,... - The field under validation must not be present _only if_ all of the other specified fields are present. +The field under validation must not be present _only if_ all of the other specified fields are present. #### not_in:_foo_,_bar_,... From 6bb6d17a05144e3201e1c9ce3f1f688d41062daa Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:43:10 +0700 Subject: [PATCH 1735/2609] Add `/docs/{{version}}/` prefix to url (#9774) --- rate-limiting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rate-limiting.md b/rate-limiting.md index a1bd0c3b87f..8492dda2231 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -12,7 +12,7 @@ Laravel includes a simple to use rate limiting abstraction which, in conjunction with your application's [cache](cache), provides an easy way to limit any action during a specified window of time. > [!NOTE] -> If you are interested in rate limiting incoming HTTP requests, please consult the [rate limiter middleware documentation](routing#rate-limiting). +> If you are interested in rate limiting incoming HTTP requests, please consult the [rate limiter middleware documentation](/docs/{{version}}/routing#rate-limiting). ### Cache Configuration From 197edc3fb93b922e3adb5a90dc7d018716d08dd0 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:44:02 +0700 Subject: [PATCH 1736/2609] Remove next (#9772) --- contributions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributions.md b/contributions.md index 4e25e01ea6f..e390461c54d 100644 --- a/contributions.md +++ b/contributions.md @@ -49,7 +49,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Scout](https://github.com/laravel/scout) - [Laravel Socialite](https://github.com/laravel/socialite) - [Laravel Telescope](https://github.com/laravel/telescope) -- [Laravel Website](https://github.com/laravel/laravel.com-next) +- [Laravel Website](https://github.com/laravel/laravel.com)
    From e9cd4fd37890a01043e057467264899c1e85d5ae Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:45:10 +0700 Subject: [PATCH 1737/2609] Update return type & remove deprecated from contracts (#9771) * Update outdated return type * Update contracts table * Remove deprecated `ImplicitRule` from contracts table --- contracts.md | 9 ++++----- validation.md | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts.md b/contracts.md index de852b6a7bb..7ded2f46e9f 100644 --- a/contracts.md +++ b/contracts.md @@ -73,9 +73,9 @@ This table provides a quick reference to all of the Laravel contracts and their | Contract | References Facade | |--------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------| -| [Illuminate\Contracts\Auth\Access\Authorizable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Authorizable.php) |    | +| [Illuminate\Contracts\Auth\Access\Authorizable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Authorizable.php) |   | | [Illuminate\Contracts\Auth\Access\Gate](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Gate.php) | `Gate` | -| [Illuminate\Contracts\Auth\Authenticatable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Authenticatable.php) |    | +| [Illuminate\Contracts\Auth\Authenticatable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Authenticatable.php) |   | | [Illuminate\Contracts\Auth\CanResetPassword](https://github.com/illuminate/contracts/blob/{{version}}/Auth/CanResetPassword.php) |   | | [Illuminate\Contracts\Auth\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Factory.php) | `Auth` | | [Illuminate\Contracts\Auth\Guard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Guard.php) | `Auth::guard()` | @@ -119,7 +119,7 @@ This table provides a quick reference to all of the Laravel contracts and their | [Illuminate\Contracts\Pagination\LengthAwarePaginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/LengthAwarePaginator.php) |   | | [Illuminate\Contracts\Pagination\Paginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/Paginator.php) |   | | [Illuminate\Contracts\Pipeline\Hub](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Hub.php) |   | -| [Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) | `Pipeline`; | +| [Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) | `Pipeline`; | | [Illuminate\Contracts\Queue\EntityResolver](https://github.com/illuminate/contracts/blob/{{version}}/Queue/EntityResolver.php) |   | | [Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Factory.php) | `Queue` | | [Illuminate\Contracts\Queue\Job](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Job.php) |   | @@ -145,9 +145,8 @@ This table provides a quick reference to all of the Laravel contracts and their | [Illuminate\Contracts\Translation\Loader](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Loader.php) |   | | [Illuminate\Contracts\Translation\Translator](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Translator.php) | `Lang` | | [Illuminate\Contracts\Validation\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Factory.php) | `Validator` | -| [Illuminate\Contracts\Validation\ImplicitRule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ImplicitRule.php) |   | -| [Illuminate\Contracts\Validation\Rule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Rule.php) |   | | [Illuminate\Contracts\Validation\ValidatesWhenResolved](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidatesWhenResolved.php) |   | +| [Illuminate\Contracts\Validation\ValidationRule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidationRule.php) |   | | [Illuminate\Contracts\Validation\Validator](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Validator.php) | `Validator::make()` | | [Illuminate\Contracts\View\Engine](https://github.com/illuminate/contracts/blob/{{version}}/View/Engine.php) |   | | [Illuminate\Contracts\View\Factory](https://github.com/illuminate/contracts/blob/{{version}}/View/Factory.php) | `View` | diff --git a/validation.md b/validation.md index 550a5531b9a..14d7cee184c 100644 --- a/validation.md +++ b/validation.md @@ -311,7 +311,7 @@ As you might have guessed, the `authorize` method is responsible for determining /** * Get the validation rules that apply to the request. * - * @return array + * @return array|string> */ public function rules(): array { From 18dbb77df4a537e4f515d0f9ac9fbd8af4b11624 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:45:22 +0700 Subject: [PATCH 1738/2609] Add missing slash to routes (#9770) --- controllers.md | 2 +- eloquent-serialization.md | 2 +- routing.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers.md b/controllers.md index b49e0712e1f..edc4ab4096e 100644 --- a/controllers.md +++ b/controllers.md @@ -106,7 +106,7 @@ php artisan make:controller ProvisionServer --invokable [Middleware](/docs/{{version}}/middleware) may be assigned to the controller's routes in your route files: - Route::get('profile', [UserController::class, 'show'])->middleware('auth'); + Route::get('/profile', [UserController::class, 'show'])->middleware('auth'); Or, you may find it convenient to specify middleware within your controller class. To do so, your controller should implement the `HasMiddleware` interface, which dictates that the controller should have a static `middleware` method. From this method, you may return an array of middleware that should be applied to the controller's actions: diff --git a/eloquent-serialization.md b/eloquent-serialization.md index 98e1956a5f0..30821074124 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -61,7 +61,7 @@ Alternatively, you may cast a model or collection to a string, which will automa Since models and collections are converted to JSON when cast to a string, you can return Eloquent objects directly from your application's routes or controllers. Laravel will automatically serialize your Eloquent models and collections to JSON when they are returned from routes or controllers: - Route::get('users', function () { + Route::get('/users', function () { return User::all(); }); diff --git a/routing.md b/routing.md index 94a27da6983..57169b48c1c 100644 --- a/routing.md +++ b/routing.md @@ -479,7 +479,7 @@ If a group of routes all utilize the same [controller](/docs/{{version}}/control Route groups may also be used to handle subdomain routing. Subdomains may be assigned route parameters just like route URIs, allowing you to capture a portion of the subdomain for usage in your route or controller. The subdomain may be specified by calling the `domain` method before defining the group: Route::domain('{account}.example.com')->group(function () { - Route::get('user/{id}', function (string $account, string $id) { + Route::get('/user/{id}', function (string $account, string $id) { // ... }); }); From 5636016990758c42148b3d10ebfe78f5c1412048 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Thu, 18 Jul 2024 23:02:59 +0700 Subject: [PATCH 1739/2609] Redirect slash (#9776) * Add slash to `redirect()` helper method * Update pennant.md --- billing.md | 2 +- cashier-paddle.md | 2 +- middleware.md | 4 ++-- pennant.md | 2 +- requests.md | 4 ++-- responses.md | 4 ++-- validation.md | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/billing.md b/billing.md index dbc9b1e58a6..6a03e8ec69a 100644 --- a/billing.md +++ b/billing.md @@ -963,7 +963,7 @@ The `subscribed` method also makes a great candidate for a [route middleware](/d { if ($request->user() && ! $request->user()->subscribed('default')) { // This user is not a paying customer... - return redirect('billing'); + return redirect('/billing'); } return $next($request); diff --git a/cashier-paddle.md b/cashier-paddle.md index 48acd5dbb92..7e7e96bcdf7 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -755,7 +755,7 @@ The `subscribed` method also makes a great candidate for a [route middleware](/d { if ($request->user() && ! $request->user()->subscribed()) { // This user is not a paying customer... - return redirect('billing'); + return redirect('/billing'); } return $next($request); diff --git a/middleware.md b/middleware.md index a99d965b9a0..91c0fd0564b 100644 --- a/middleware.md +++ b/middleware.md @@ -27,7 +27,7 @@ To create a new middleware, use the `make:middleware` Artisan command: 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: +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: input('token') !== 'my-secret-token') { - return redirect('home'); + return redirect('/home'); } return $next($request); diff --git a/pennant.md b/pennant.md index 3970e9bcdeb..8ed326d05ad 100644 --- a/pennant.md +++ b/pennant.md @@ -524,7 +524,7 @@ You will notice that the closure we have defined is not expecting a `User`, but ```php if (Feature::for($user->team)->active('billing-v2')) { - return redirect()->to('/billing/v2'); + return redirect('/billing/v2'); } // ... diff --git a/requests.md b/requests.md index 9bc0797de15..aef7f57ae4c 100644 --- a/requests.md +++ b/requests.md @@ -471,11 +471,11 @@ You may also use the `flashOnly` and `flashExcept` methods to flash a subset of Since you often will want to flash input to the session and then redirect to the previous page, you may easily chain input flashing onto a redirect using the `withInput` method: - return redirect('form')->withInput(); + return redirect('/form')->withInput(); return redirect()->route('user.create')->withInput(); - return redirect('form')->withInput( + return redirect('/form')->withInput( $request->except('password') ); diff --git a/responses.md b/responses.md index 30435afc62e..1653629ed5c 100644 --- a/responses.md +++ b/responses.md @@ -152,7 +152,7 @@ By default, thanks to the `Illuminate\Cookie\Middleware\EncryptCookies` middlewa 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 () { - return redirect('home/dashboard'); + 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: @@ -225,7 +225,7 @@ Redirecting to a new URL and [flashing data to the session](/docs/{{version}}/se Route::post('/user/profile', function () { // ... - return redirect('dashboard')->with('status', 'Profile updated!'); + return redirect('/dashboard')->with('status', 'Profile updated!'); }); 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): diff --git a/validation.md b/validation.md index 14d7cee184c..664ba79390c 100644 --- a/validation.md +++ b/validation.md @@ -559,7 +559,7 @@ If you do not want to use the `validate` method on the request, you may create a ]); if ($validator->fails()) { - return redirect('post/create') + return redirect('/post/create') ->withErrors($validator) ->withInput(); } @@ -611,7 +611,7 @@ You may use the `validateWithBag` method to store the error messages in a [named 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: From 87463da2cce7c09a7e5ecc36664bf115dff6b908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Tom=C3=A9?= Date: Thu, 18 Jul 2024 17:45:05 -0300 Subject: [PATCH 1740/2609] Update octane.md (#9779) * Update octane.md --max-requests=1 will not work because the number of workers will be bigger than 1 > The number of maximum requests is per worker, and by default, FrankenPHP starts many workers (2 times the number of CPUs). https://github.com/laravel/octane/issues/816#issuecomment-1898126577 * Update octane.md --------- Co-authored-by: Taylor Otwell --- octane.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octane.md b/octane.md index 4cdae9776f4..c44c9c1dd4b 100644 --- a/octane.md +++ b/octane.md @@ -128,7 +128,7 @@ services: frankenphp: build: context: . - entrypoint: php artisan octane:frankenphp --max-requests=1 + entrypoint: php artisan octane:frankenphp --workers=1 --max-requests=1 ports: - "8000:8000" volumes: From 87c1dc3bbb78949d35a1af957ba76c4469490baa Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Fri, 19 Jul 2024 03:46:55 +0700 Subject: [PATCH 1741/2609] Update markdown tables (#9778) * Update tables * Fix quotes * Update `` examples * Add overflow-auto div to tables * Update requests.md --- authentication.md | 28 ++++---- blade.md | 6 +- cache.md | 12 ++-- configuration.md | 2 +- contracts.md | 164 ++++++++++++++++++++++++---------------------- controllers.md | 78 ++++++++++++++-------- facades.md | 112 +++++++++++++++---------------- logging.md | 38 +++++------ mail.md | 8 +-- middleware.md | 64 ++++++++++-------- migrations.md | 128 +++++++++++++++++++++--------------- notifications.md | 8 +-- pagination.md | 86 +++++++++++++----------- passport.md | 8 +-- requests.md | 1 - scheduling.md | 130 ++++++++++++++++++------------------ valet.md | 26 ++++---- 17 files changed, 487 insertions(+), 412 deletions(-) diff --git a/authentication.md b/authentication.md index dcdbff7f4d0..32248ef9c3a 100644 --- a/authentication.md +++ b/authentication.md @@ -741,17 +741,17 @@ Once the configuration file has been published, you may set the `rehash_on_login Laravel dispatches a variety of [events](/docs/{{version}}/events) during the authentication process. You may [define listeners](/docs/{{version}}/events) for any of the following events: -Event Name | -------------- | -`Illuminate\Auth\Events\Registered` | -`Illuminate\Auth\Events\Attempting` | -`Illuminate\Auth\Events\Authenticated` | -`Illuminate\Auth\Events\Login` | -`Illuminate\Auth\Events\Failed` | -`Illuminate\Auth\Events\Validated` | -`Illuminate\Auth\Events\Verified` | -`Illuminate\Auth\Events\Logout` | -`Illuminate\Auth\Events\CurrentDeviceLogout` | -`Illuminate\Auth\Events\OtherDeviceLogout` | -`Illuminate\Auth\Events\Lockout` | -`Illuminate\Auth\Events\PasswordReset` | +| Event Name | +| --- | +| `Illuminate\Auth\Events\Registered` | +| `Illuminate\Auth\Events\Attempting` | +| `Illuminate\Auth\Events\Authenticated` | +| `Illuminate\Auth\Events\Login` | +| `Illuminate\Auth\Events\Failed` | +| `Illuminate\Auth\Events\Validated` | +| `Illuminate\Auth\Events\Verified` | +| `Illuminate\Auth\Events\Logout` | +| `Illuminate\Auth\Events\CurrentDeviceLogout` | +| `Illuminate\Auth\Events\OtherDeviceLogout` | +| `Illuminate\Auth\Events\Lockout` | +| `Illuminate\Auth\Events\PasswordReset` | diff --git a/blade.md b/blade.md index 44c5db32223..9a99018e9bd 100644 --- a/blade.md +++ b/blade.md @@ -423,8 +423,10 @@ If you are in a nested loop, you may access the parent loop's `$loop` variable v The `$loop` variable also contains a variety of other useful properties: +
    + | Property | Description | -|--------------------|--------------------------------------------------------| +| ------------------ | ------------------------------------------------------ | | `$loop->index` | The index of the current loop iteration (starts at 0). | | `$loop->iteration` | The current loop iteration (starts at 1). | | `$loop->remaining` | The iterations remaining in the loop. | @@ -436,6 +438,8 @@ The `$loop` variable also contains a variety of other useful properties: | `$loop->depth` | The nesting level of the current loop. | | `$loop->parent` | When in a nested loop, the parent's loop variable. | +
    + ### Conditional Classes & Styles diff --git a/cache.md b/cache.md index dc896d2a3db..c7d4757ed0d 100644 --- a/cache.md +++ b/cache.md @@ -440,12 +440,12 @@ Once your extension is registered, update the `CACHE_STORE` environment variable To execute code on every cache operation, you may listen for various [events](/docs/{{version}}/events) dispatched by the cache: -Event Name | -------------- | -`Illuminate\Cache\Events\CacheHit` | -`Illuminate\Cache\Events\CacheMissed` | -`Illuminate\Cache\Events\KeyForgotten` | -`Illuminate\Cache\Events\KeyWritten` | +| Event Name | +| --- | +| `Illuminate\Cache\Events\CacheHit` | +| `Illuminate\Cache\Events\CacheMissed` | +| `Illuminate\Cache\Events\KeyForgotten` | +| `Illuminate\Cache\Events\KeyWritten` | To increase performance, you may disable cache events by setting the `events` configuration option to `false` for a given cache store in your application's `config/cache.php` configuration file: diff --git a/configuration.md b/configuration.md index cc6a81a35fd..ad3ed6a3a43 100644 --- a/configuration.md +++ b/configuration.md @@ -72,7 +72,7 @@ Before loading your application's environment variables, Laravel determines if a All variables in your `.env` files are typically parsed as strings, so some reserved values have been created to allow you to return a wider range of types from the `env()` function: | `.env` Value | `env()` Value | -|--------------|---------------| +| ------------ | ------------- | | true | (bool) true | | (true) | (bool) true | | false | (bool) false | diff --git a/contracts.md b/contracts.md index 7ded2f46e9f..f8c1884f74a 100644 --- a/contracts.md +++ b/contracts.md @@ -71,83 +71,87 @@ When the event listener is resolved, the service container will read the type-hi This table provides a quick reference to all of the Laravel contracts and their equivalent facades: -| Contract | References Facade | -|--------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------| -| [Illuminate\Contracts\Auth\Access\Authorizable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Authorizable.php) |   | -| [Illuminate\Contracts\Auth\Access\Gate](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Gate.php) | `Gate` | -| [Illuminate\Contracts\Auth\Authenticatable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Authenticatable.php) |   | -| [Illuminate\Contracts\Auth\CanResetPassword](https://github.com/illuminate/contracts/blob/{{version}}/Auth/CanResetPassword.php) |   | -| [Illuminate\Contracts\Auth\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Factory.php) | `Auth` | -| [Illuminate\Contracts\Auth\Guard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Guard.php) | `Auth::guard()` | -| [Illuminate\Contracts\Auth\PasswordBroker](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBroker.php) | `Password::broker()` | -| [Illuminate\Contracts\Auth\PasswordBrokerFactory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBrokerFactory.php) | `Password` | -| [Illuminate\Contracts\Auth\StatefulGuard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/StatefulGuard.php) |   | -| [Illuminate\Contracts\Auth\SupportsBasicAuth](https://github.com/illuminate/contracts/blob/{{version}}/Auth/SupportsBasicAuth.php) |   | -| [Illuminate\Contracts\Auth\UserProvider](https://github.com/illuminate/contracts/blob/{{version}}/Auth/UserProvider.php) |   | -| [Illuminate\Contracts\Bus\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/Dispatcher.php) | `Bus` | -| [Illuminate\Contracts\Bus\QueueingDispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/QueueingDispatcher.php) | `Bus::dispatchToQueue()` | -| [Illuminate\Contracts\Broadcasting\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Factory.php) | `Broadcast` | -| [Illuminate\Contracts\Broadcasting\Broadcaster](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Broadcaster.php) | `Broadcast::connection()` | -| [Illuminate\Contracts\Broadcasting\ShouldBroadcast](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcast.php) |   | -| [Illuminate\Contracts\Broadcasting\ShouldBroadcastNow](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcastNow.php) |   | -| [Illuminate\Contracts\Cache\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Factory.php) | `Cache` | -| [Illuminate\Contracts\Cache\Lock](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Lock.php) |   | -| [Illuminate\Contracts\Cache\LockProvider](https://github.com/illuminate/contracts/blob/{{version}}/Cache/LockProvider.php) |   | -| [Illuminate\Contracts\Cache\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Repository.php) | `Cache::driver()` | -| [Illuminate\Contracts\Cache\Store](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Store.php) |   | -| [Illuminate\Contracts\Config\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Config/Repository.php) | `Config` | -| [Illuminate\Contracts\Console\Application](https://github.com/illuminate/contracts/blob/{{version}}/Console/Application.php) |   | -| [Illuminate\Contracts\Console\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Console/Kernel.php) | `Artisan` | -| [Illuminate\Contracts\Container\Container](https://github.com/illuminate/contracts/blob/{{version}}/Container/Container.php) | `App` | -| [Illuminate\Contracts\Cookie\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/Factory.php) | `Cookie` | -| [Illuminate\Contracts\Cookie\QueueingFactory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/QueueingFactory.php) | `Cookie::queue()` | -| [Illuminate\Contracts\Database\ModelIdentifier](https://github.com/illuminate/contracts/blob/{{version}}/Database/ModelIdentifier.php) |   | -| [Illuminate\Contracts\Debug\ExceptionHandler](https://github.com/illuminate/contracts/blob/{{version}}/Debug/ExceptionHandler.php) |   | -| [Illuminate\Contracts\Encryption\Encrypter](https://github.com/illuminate/contracts/blob/{{version}}/Encryption/Encrypter.php) | `Crypt` | -| [Illuminate\Contracts\Events\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Events/Dispatcher.php) | `Event` | -| [Illuminate\Contracts\Filesystem\Cloud](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Cloud.php) | `Storage::cloud()` | -| [Illuminate\Contracts\Filesystem\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Factory.php) | `Storage` | -| [Illuminate\Contracts\Filesystem\Filesystem](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Filesystem.php) | `Storage::disk()` | -| [Illuminate\Contracts\Foundation\Application](https://github.com/illuminate/contracts/blob/{{version}}/Foundation/Application.php) | `App` | -| [Illuminate\Contracts\Hashing\Hasher](https://github.com/illuminate/contracts/blob/{{version}}/Hashing/Hasher.php) | `Hash` | -| [Illuminate\Contracts\Http\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Http/Kernel.php) |   | -| [Illuminate\Contracts\Mail\MailQueue](https://github.com/illuminate/contracts/blob/{{version}}/Mail/MailQueue.php) | `Mail::queue()` | -| [Illuminate\Contracts\Mail\Mailable](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailable.php) |   | -| [Illuminate\Contracts\Mail\Mailer](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailer.php) | `Mail` | -| [Illuminate\Contracts\Notifications\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Dispatcher.php) | `Notification` | -| [Illuminate\Contracts\Notifications\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Factory.php) | `Notification` | -| [Illuminate\Contracts\Pagination\LengthAwarePaginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/LengthAwarePaginator.php) |   | -| [Illuminate\Contracts\Pagination\Paginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/Paginator.php) |   | -| [Illuminate\Contracts\Pipeline\Hub](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Hub.php) |   | -| [Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) | `Pipeline`; | -| [Illuminate\Contracts\Queue\EntityResolver](https://github.com/illuminate/contracts/blob/{{version}}/Queue/EntityResolver.php) |   | -| [Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Factory.php) | `Queue` | -| [Illuminate\Contracts\Queue\Job](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Job.php) |   | -| [Illuminate\Contracts\Queue\Monitor](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Monitor.php) | `Queue` | -| [Illuminate\Contracts\Queue\Queue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Queue.php) | `Queue::connection()` | -| [Illuminate\Contracts\Queue\QueueableCollection](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableCollection.php) |   | -| [Illuminate\Contracts\Queue\QueueableEntity](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableEntity.php) |   | -| [Illuminate\Contracts\Queue\ShouldQueue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/ShouldQueue.php) |   | -| [Illuminate\Contracts\Redis\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Redis/Factory.php) | `Redis` | -| [Illuminate\Contracts\Routing\BindingRegistrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/BindingRegistrar.php) | `Route` | -| [Illuminate\Contracts\Routing\Registrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/Registrar.php) | `Route` | -| [Illuminate\Contracts\Routing\ResponseFactory](https://github.com/illuminate/contracts/blob/{{version}}/Routing/ResponseFactory.php) | `Response` | -| [Illuminate\Contracts\Routing\UrlGenerator](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlGenerator.php) | `URL` | -| [Illuminate\Contracts\Routing\UrlRoutable](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlRoutable.php) |   | -| [Illuminate\Contracts\Session\Session](https://github.com/illuminate/contracts/blob/{{version}}/Session/Session.php) | `Session::driver()` | -| [Illuminate\Contracts\Support\Arrayable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Arrayable.php) |   | -| [Illuminate\Contracts\Support\Htmlable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Htmlable.php) |   | -| [Illuminate\Contracts\Support\Jsonable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Jsonable.php) |   | -| [Illuminate\Contracts\Support\MessageBag](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageBag.php) |   | -| [Illuminate\Contracts\Support\MessageProvider](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageProvider.php) |   | -| [Illuminate\Contracts\Support\Renderable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Renderable.php) |   | -| [Illuminate\Contracts\Support\Responsable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Responsable.php) |   | -| [Illuminate\Contracts\Translation\Loader](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Loader.php) |   | -| [Illuminate\Contracts\Translation\Translator](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Translator.php) | `Lang` | -| [Illuminate\Contracts\Validation\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Factory.php) | `Validator` | -| [Illuminate\Contracts\Validation\ValidatesWhenResolved](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidatesWhenResolved.php) |   | -| [Illuminate\Contracts\Validation\ValidationRule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidationRule.php) |   | -| [Illuminate\Contracts\Validation\Validator](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Validator.php) | `Validator::make()` | -| [Illuminate\Contracts\View\Engine](https://github.com/illuminate/contracts/blob/{{version}}/View/Engine.php) |   | -| [Illuminate\Contracts\View\Factory](https://github.com/illuminate/contracts/blob/{{version}}/View/Factory.php) | `View` | -| [Illuminate\Contracts\View\View](https://github.com/illuminate/contracts/blob/{{version}}/View/View.php) | `View::make()` | +
    + +| Contract | References Facade | +| --- | --- | +| [Illuminate\Contracts\Auth\Access\Authorizable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Authorizable.php) |   | +| [Illuminate\Contracts\Auth\Access\Gate](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Access/Gate.php) | `Gate` | +| [Illuminate\Contracts\Auth\Authenticatable](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Authenticatable.php) |   | +| [Illuminate\Contracts\Auth\CanResetPassword](https://github.com/illuminate/contracts/blob/{{version}}/Auth/CanResetPassword.php) |   | +| [Illuminate\Contracts\Auth\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Factory.php) | `Auth` | +| [Illuminate\Contracts\Auth\Guard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Guard.php) | `Auth::guard()` | +| [Illuminate\Contracts\Auth\PasswordBroker](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBroker.php) | `Password::broker()` | +| [Illuminate\Contracts\Auth\PasswordBrokerFactory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBrokerFactory.php) | `Password` | +| [Illuminate\Contracts\Auth\StatefulGuard](https://github.com/illuminate/contracts/blob/{{version}}/Auth/StatefulGuard.php) |   | +| [Illuminate\Contracts\Auth\SupportsBasicAuth](https://github.com/illuminate/contracts/blob/{{version}}/Auth/SupportsBasicAuth.php) |   | +| [Illuminate\Contracts\Auth\UserProvider](https://github.com/illuminate/contracts/blob/{{version}}/Auth/UserProvider.php) |   | +| [Illuminate\Contracts\Broadcasting\Broadcaster](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Broadcaster.php) | `Broadcast::connection()` | +| [Illuminate\Contracts\Broadcasting\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Factory.php) | `Broadcast` | +| [Illuminate\Contracts\Broadcasting\ShouldBroadcast](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcast.php) |   | +| [Illuminate\Contracts\Broadcasting\ShouldBroadcastNow](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/ShouldBroadcastNow.php) |   | +| [Illuminate\Contracts\Bus\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/Dispatcher.php) | `Bus` | +| [Illuminate\Contracts\Bus\QueueingDispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/QueueingDispatcher.php) | `Bus::dispatchToQueue()` | +| [Illuminate\Contracts\Cache\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Factory.php) | `Cache` | +| [Illuminate\Contracts\Cache\Lock](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Lock.php) |   | +| [Illuminate\Contracts\Cache\LockProvider](https://github.com/illuminate/contracts/blob/{{version}}/Cache/LockProvider.php) |   | +| [Illuminate\Contracts\Cache\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Repository.php) | `Cache::driver()` | +| [Illuminate\Contracts\Cache\Store](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Store.php) |   | +| [Illuminate\Contracts\Config\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Config/Repository.php) | `Config` | +| [Illuminate\Contracts\Console\Application](https://github.com/illuminate/contracts/blob/{{version}}/Console/Application.php) |   | +| [Illuminate\Contracts\Console\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Console/Kernel.php) | `Artisan` | +| [Illuminate\Contracts\Container\Container](https://github.com/illuminate/contracts/blob/{{version}}/Container/Container.php) | `App` | +| [Illuminate\Contracts\Cookie\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/Factory.php) | `Cookie` | +| [Illuminate\Contracts\Cookie\QueueingFactory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/QueueingFactory.php) | `Cookie::queue()` | +| [Illuminate\Contracts\Database\ModelIdentifier](https://github.com/illuminate/contracts/blob/{{version}}/Database/ModelIdentifier.php) |   | +| [Illuminate\Contracts\Debug\ExceptionHandler](https://github.com/illuminate/contracts/blob/{{version}}/Debug/ExceptionHandler.php) |   | +| [Illuminate\Contracts\Encryption\Encrypter](https://github.com/illuminate/contracts/blob/{{version}}/Encryption/Encrypter.php) | `Crypt` | +| [Illuminate\Contracts\Events\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Events/Dispatcher.php) | `Event` | +| [Illuminate\Contracts\Filesystem\Cloud](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Cloud.php) | `Storage::cloud()` | +| [Illuminate\Contracts\Filesystem\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Factory.php) | `Storage` | +| [Illuminate\Contracts\Filesystem\Filesystem](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Filesystem.php) | `Storage::disk()` | +| [Illuminate\Contracts\Foundation\Application](https://github.com/illuminate/contracts/blob/{{version}}/Foundation/Application.php) | `App` | +| [Illuminate\Contracts\Hashing\Hasher](https://github.com/illuminate/contracts/blob/{{version}}/Hashing/Hasher.php) | `Hash` | +| [Illuminate\Contracts\Http\Kernel](https://github.com/illuminate/contracts/blob/{{version}}/Http/Kernel.php) |   | +| [Illuminate\Contracts\Mail\Mailable](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailable.php) |   | +| [Illuminate\Contracts\Mail\Mailer](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailer.php) | `Mail` | +| [Illuminate\Contracts\Mail\MailQueue](https://github.com/illuminate/contracts/blob/{{version}}/Mail/MailQueue.php) | `Mail::queue()` | +| [Illuminate\Contracts\Notifications\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Dispatcher.php) | `Notification`| +| [Illuminate\Contracts\Notifications\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Notifications/Factory.php) | `Notification` | +| [Illuminate\Contracts\Pagination\LengthAwarePaginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/LengthAwarePaginator.php) |   | +| [Illuminate\Contracts\Pagination\Paginator](https://github.com/illuminate/contracts/blob/{{version}}/Pagination/Paginator.php) |   | +| [Illuminate\Contracts\Pipeline\Hub](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Hub.php) |   | +| [Illuminate\Contracts\Pipeline\Pipeline](https://github.com/illuminate/contracts/blob/{{version}}/Pipeline/Pipeline.php) | `Pipeline` | +| [Illuminate\Contracts\Queue\EntityResolver](https://github.com/illuminate/contracts/blob/{{version}}/Queue/EntityResolver.php) |   | +| [Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Factory.php) | `Queue` | +| [Illuminate\Contracts\Queue\Job](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Job.php) |   | +| [Illuminate\Contracts\Queue\Monitor](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Monitor.php) | `Queue` | +| [Illuminate\Contracts\Queue\Queue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Queue.php) | `Queue::connection()` | +| [Illuminate\Contracts\Queue\QueueableCollection](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableCollection.php) |   | +| [Illuminate\Contracts\Queue\QueueableEntity](https://github.com/illuminate/contracts/blob/{{version}}/Queue/QueueableEntity.php) |   | +| [Illuminate\Contracts\Queue\ShouldQueue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/ShouldQueue.php) |   | +| [Illuminate\Contracts\Redis\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Redis/Factory.php) | `Redis` | +| [Illuminate\Contracts\Routing\BindingRegistrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/BindingRegistrar.php) | `Route` | +| [Illuminate\Contracts\Routing\Registrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/Registrar.php) | `Route` | +| [Illuminate\Contracts\Routing\ResponseFactory](https://github.com/illuminate/contracts/blob/{{version}}/Routing/ResponseFactory.php) | `Response` | +| [Illuminate\Contracts\Routing\UrlGenerator](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlGenerator.php) | `URL` | +| [Illuminate\Contracts\Routing\UrlRoutable](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlRoutable.php) |   | +| [Illuminate\Contracts\Session\Session](https://github.com/illuminate/contracts/blob/{{version}}/Session/Session.php) | `Session::driver()` | +| [Illuminate\Contracts\Support\Arrayable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Arrayable.php) |   | +| [Illuminate\Contracts\Support\Htmlable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Htmlable.php) |   | +| [Illuminate\Contracts\Support\Jsonable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Jsonable.php) |   | +| [Illuminate\Contracts\Support\MessageBag](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageBag.php) |   | +| [Illuminate\Contracts\Support\MessageProvider](https://github.com/illuminate/contracts/blob/{{version}}/Support/MessageProvider.php) |   | +| [Illuminate\Contracts\Support\Renderable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Renderable.php) |   | +| [Illuminate\Contracts\Support\Responsable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Responsable.php) |   | +| [Illuminate\Contracts\Translation\Loader](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Loader.php) |   | +| [Illuminate\Contracts\Translation\Translator](https://github.com/illuminate/contracts/blob/{{version}}/Translation/Translator.php) | `Lang` | +| [Illuminate\Contracts\Validation\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Factory.php) | `Validator` | +| [Illuminate\Contracts\Validation\ValidatesWhenResolved](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidatesWhenResolved.php) |   | +| [Illuminate\Contracts\Validation\ValidationRule](https://github.com/illuminate/contracts/blob/{{version}}/Validation/ValidationRule.php) |   | +| [Illuminate\Contracts\Validation\Validator](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Validator.php) | `Validator::make()` | +| [Illuminate\Contracts\View\Engine](https://github.com/illuminate/contracts/blob/{{version}}/View/Engine.php) |   | +| [Illuminate\Contracts\View\Factory](https://github.com/illuminate/contracts/blob/{{version}}/View/Factory.php) | `View` | +| [Illuminate\Contracts\View\View](https://github.com/illuminate/contracts/blob/{{version}}/View/View.php) | `View::make()` | + +
    diff --git a/controllers.md b/controllers.md index edc4ab4096e..54fc36bde6a 100644 --- a/controllers.md +++ b/controllers.md @@ -181,15 +181,19 @@ You may even register many resource controllers at once by passing an array to t #### Actions Handled by Resource Controllers -Verb | URI | Action | Route Name -----------|------------------------|--------------|--------------------- -GET | `/photos` | index | photos.index -GET | `/photos/create` | create | photos.create -POST | `/photos` | store | photos.store -GET | `/photos/{photo}` | show | photos.show -GET | `/photos/{photo}/edit` | edit | photos.edit -PUT/PATCH | `/photos/{photo}` | update | photos.update -DELETE | `/photos/{photo}` | destroy | photos.destroy +
    + +| Verb | URI | Action | Route Name | +| --------- | ---------------------- | ------- | -------------- | +| GET | `/photos` | index | photos.index | +| GET | `/photos/create` | create | photos.create | +| POST | `/photos` | store | photos.store | +| GET | `/photos/{photo}` | show | photos.show | +| GET | `/photos/{photo}/edit` | edit | photos.edit | +| PUT/PATCH | `/photos/{photo}` | update | photos.update | +| DELETE | `/photos/{photo}` | destroy | photos.destroy | + +
    #### Customizing Missing Model Behavior @@ -305,15 +309,19 @@ Often, it is not entirely necessary to have both the parent and the child IDs wi This route definition will define the following routes: -Verb | URI | Action | Route Name -----------|-----------------------------------|--------------|--------------------- -GET | `/photos/{photo}/comments` | index | photos.comments.index -GET | `/photos/{photo}/comments/create` | create | photos.comments.create -POST | `/photos/{photo}/comments` | store | photos.comments.store -GET | `/comments/{comment}` | show | comments.show -GET | `/comments/{comment}/edit` | edit | comments.edit -PUT/PATCH | `/comments/{comment}` | update | comments.update -DELETE | `/comments/{comment}` | destroy | comments.destroy +
    + +| Verb | URI | Action | Route Name | +| --------- | --------------------------------- | ------- | ---------------------- | +| GET | `/photos/{photo}/comments` | index | photos.comments.index | +| GET | `/photos/{photo}/comments/create` | create | photos.comments.create | +| POST | `/photos/{photo}/comments` | store | photos.comments.store | +| GET | `/comments/{comment}` | show | comments.show | +| GET | `/comments/{comment}/edit` | edit | comments.edit | +| PUT/PATCH | `/comments/{comment}` | update | comments.update | +| DELETE | `/comments/{comment}` | destroy | comments.destroy | + +
    ### Naming Resource Routes @@ -407,11 +415,15 @@ Route::singleton('profile', ProfileController::class); The singleton resource definition above will register the following routes. As you can see, "creation" routes are not registered for singleton resources, and the registered routes do not accept an identifier since only one instance of the resource may exist: -Verb | URI | Action | Route Name -----------|-----------------------------------|--------------|--------------------- -GET | `/profile` | show | profile.show -GET | `/profile/edit` | edit | profile.edit -PUT/PATCH | `/profile` | update | profile.update +
    + +| Verb | URI | Action | Route Name | +| --------- | --------------- | ------ | -------------- | +| GET | `/profile` | show | profile.show | +| GET | `/profile/edit` | edit | profile.edit | +| PUT/PATCH | `/profile` | update | profile.update | + +
    Singleton resources may also be nested within a standard resource: @@ -421,11 +433,15 @@ Route::singleton('photos.thumbnail', ThumbnailController::class); In this example, the `photos` resource would receive all of the [standard resource routes](#actions-handled-by-resource-controller); however, the `thumbnail` resource would be a singleton resource with the following routes: -| Verb | URI | Action | Route Name | -|-----------|----------------------------------|---------|--------------------------| -| GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | -| GET | `/photos/{photo}/thumbnail/edit` | edit | photos.thumbnail.edit | -| PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | +
    + +| Verb | URI | Action | Route Name | +| --------- | -------------------------------- | ------ | ----------------------- | +| GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | +| GET | `/photos/{photo}/thumbnail/edit` | edit | photos.thumbnail.edit | +| PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | + +
    #### Creatable Singleton Resources @@ -438,8 +454,10 @@ Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable(); In this example, the following routes will be registered. As you can see, a `DELETE` route will also be registered for creatable singleton resources: +
    + | Verb | URI | Action | Route Name | -|-----------|------------------------------------|---------|--------------------------| +| --------- | ---------------------------------- | ------- | ------------------------ | | GET | `/photos/{photo}/thumbnail/create` | create | photos.thumbnail.create | | POST | `/photos/{photo}/thumbnail` | store | photos.thumbnail.store | | GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | @@ -447,6 +465,8 @@ In this example, the following routes will be registered. As you can see, a `DEL | PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | | DELETE | `/photos/{photo}/thumbnail` | destroy | photos.thumbnail.destroy | +
    + If you would like Laravel to register the `DELETE` route for a singleton resource but not register the creation or storage routes, you may utilize the `destroyable` method: ```php diff --git a/facades.md b/facades.md index 9b72a8cf783..1c8fcbb0bd9 100644 --- a/facades.md +++ b/facades.md @@ -284,61 +284,61 @@ Below you will find every facade and its underlying class. This is a useful tool
    -Facade | Class | Service Container Binding -------------- | ------------- | ------------- -App | [Illuminate\Foundation\Application](https://laravel.com/api/{{version}}/Illuminate/Foundation/Application.html) | `app` -Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/{{version}}/Illuminate/Contracts/Console/Kernel.html) | `artisan` -Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/{{version}}/Illuminate/Auth/AuthManager.html) | `auth` -Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` -Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/{{version}}/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` -Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Broadcasting/Factory.html) |   -Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/{{version}}/Illuminate/Contracts/Broadcasting/Broadcaster.html) |   -Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Bus/Dispatcher.html) |   -Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/{{version}}/Illuminate/Cache/CacheManager.html) | `cache` -Cache (Instance) | [Illuminate\Cache\Repository](https://laravel.com/api/{{version}}/Illuminate/Cache/Repository.html) | `cache.store` -Config | [Illuminate\Config\Repository](https://laravel.com/api/{{version}}/Illuminate/Config/Repository.html) | `config` -Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/{{version}}/Illuminate/Cookie/CookieJar.html) | `cookie` -Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/{{version}}/Illuminate/Encryption/Encrypter.html) | `encrypter` -Date | [Illuminate\Support\DateFactory](https://laravel.com/api/{{version}}/Illuminate/Support/DateFactory.html) | `date` -DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/{{version}}/Illuminate/Database/DatabaseManager.html) | `db` -DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/{{version}}/Illuminate/Database/Connection.html) | `db.connection` -Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Events/Dispatcher.html) | `events` -Exceptions | [Illuminate\Foundation\Exceptions\Handler](https://laravel.com/api/{{version}}/Illuminate/Foundation/Exceptions/Handler.html) |   -Exceptions (Instance) | [Illuminate\Contracts\Debug\ExceptionHandler](https://laravel.com/api/{{version}}/Illuminate/Contracts/Debug/ExceptionHandler.html) |   -File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Filesystem/Filesystem.html) | `files` -Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Access/Gate.html) |   -Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Hashing/Hasher.html) | `hash` -Http | [Illuminate\Http\Client\Factory](https://laravel.com/api/{{version}}/Illuminate/Http/Client/Factory.html) |   -Lang | [Illuminate\Translation\Translator](https://laravel.com/api/{{version}}/Illuminate/Translation/Translator.html) | `translator` -Log | [Illuminate\Log\LogManager](https://laravel.com/api/{{version}}/Illuminate/Log/LogManager.html) | `log` -Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/{{version}}/Illuminate/Mail/Mailer.html) | `mailer` -Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/{{version}}/Illuminate/Notifications/ChannelManager.html) |   -Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` -Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` -Pipeline (Instance) | [Illuminate\Pipeline\Pipeline](https://laravel.com/api/{{version}}/Illuminate/Pipeline/Pipeline.html) |   -Process | [Illuminate\Process\Factory](https://laravel.com/api/{{version}}/Illuminate/Process/Factory.html) |   -Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` -Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` -Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) |   -RateLimiter | [Illuminate\Cache\RateLimiter](https://laravel.com/api/{{version}}/Illuminate/Cache/RateLimiter.html) |   -Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/{{version}}/Illuminate/Routing/Redirector.html) | `redirect` -Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/{{version}}/Illuminate/Redis/RedisManager.html) | `redis` -Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/{{version}}/Illuminate/Redis/Connections/Connection.html) | `redis.connection` -Request | [Illuminate\Http\Request](https://laravel.com/api/{{version}}/Illuminate/Http/Request.html) | `request` -Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Routing/ResponseFactory.html) |   -Response (Instance) | [Illuminate\Http\Response](https://laravel.com/api/{{version}}/Illuminate/Http/Response.html) |   -Route | [Illuminate\Routing\Router](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) | `router` -Schedule | [Illuminate\Console\Scheduling\Schedule](https://laravel.com/api/{{version}}/Illuminate/Console/Scheduling/Schedule.html) |   -Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/{{version}}/Illuminate/Database/Schema/Builder.html) |   -Session | [Illuminate\Session\SessionManager](https://laravel.com/api/{{version}}/Illuminate/Session/SessionManager.html) | `session` -Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/{{version}}/Illuminate/Session/Store.html) | `session.store` -Storage | [Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/{{version}}/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` -Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` -URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/{{version}}/Illuminate/Routing/UrlGenerator.html) | `url` -Validator | [Illuminate\Validation\Factory](https://laravel.com/api/{{version}}/Illuminate/Validation/Factory.html) | `validator` -Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/{{version}}/Illuminate/Validation/Validator.html) |   -View | [Illuminate\View\Factory](https://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` -View (Instance) | [Illuminate\View\View](https://laravel.com/api/{{version}}/Illuminate/View/View.html) |   -Vite | [Illuminate\Foundation\Vite](https://laravel.com/api/{{version}}/Illuminate/Foundation/Vite.html) |   +| Facade | Class | Service Container Binding | +| --- | --- | --- | +| App | [Illuminate\Foundation\Application](https://laravel.com/api/{{version}}/Illuminate/Foundation/Application.html) | `app` | +| Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/{{version}}/Illuminate/Contracts/Console/Kernel.html) | `artisan` | +| Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` | +| Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/{{version}}/Illuminate/Auth/AuthManager.html) | `auth` | +| Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/{{version}}/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` | +| Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/{{version}}/Illuminate/Contracts/Broadcasting/Broadcaster.html) |   | +| Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Broadcasting/Factory.html) |   | +| Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Bus/Dispatcher.html) |   | +| Cache (Instance) | [Illuminate\Cache\Repository](https://laravel.com/api/{{version}}/Illuminate/Cache/Repository.html) | `cache.store` | +| Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/{{version}}/Illuminate/Cache/CacheManager.html) | `cache` | +| Config | [Illuminate\Config\Repository](https://laravel.com/api/{{version}}/Illuminate/Config/Repository.html) | `config` | +| Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/{{version}}/Illuminate/Cookie/CookieJar.html) | `cookie` | +| Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/{{version}}/Illuminate/Encryption/Encrypter.html) | `encrypter` | +| Date | [Illuminate\Support\DateFactory](https://laravel.com/api/{{version}}/Illuminate/Support/DateFactory.html) | `date` | +| DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/{{version}}/Illuminate/Database/Connection.html) | `db.connection` | +| DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/{{version}}/Illuminate/Database/DatabaseManager.html) | `db` | +| Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Events/Dispatcher.html) | `events` | +| Exceptions (Instance) | [Illuminate\Contracts\Debug\ExceptionHandler](https://laravel.com/api/{{version}}/Illuminate/Contracts/Debug/ExceptionHandler.html) |   | +| Exceptions | [Illuminate\Foundation\Exceptions\Handler](https://laravel.com/api/{{version}}/Illuminate/Foundation/Exceptions/Handler.html) |   | +| File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Filesystem/Filesystem.html) | `files` | +| Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Access/Gate.html) |   | +| Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Hashing/Hasher.html) | `hash` | +| Http | [Illuminate\Http\Client\Factory](https://laravel.com/api/{{version}}/Illuminate/Http/Client/Factory.html) |   | +| Lang | [Illuminate\Translation\Translator](https://laravel.com/api/{{version}}/Illuminate/Translation/Translator.html) | `translator` | +| Log | [Illuminate\Log\LogManager](https://laravel.com/api/{{version}}/Illuminate/Log/LogManager.html) | `log` | +| Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/{{version}}/Illuminate/Mail/Mailer.html) | `mailer` | +| Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/{{version}}/Illuminate/Notifications/ChannelManager.html) |   | +| Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` | +| Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` | +| Pipeline (Instance) | [Illuminate\Pipeline\Pipeline](https://laravel.com/api/{{version}}/Illuminate/Pipeline/Pipeline.html) |   | +| Process | [Illuminate\Process\Factory](https://laravel.com/api/{{version}}/Illuminate/Process/Factory.html) |   | +| Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) |   | +| Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` | +| Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` | +| RateLimiter | [Illuminate\Cache\RateLimiter](https://laravel.com/api/{{version}}/Illuminate/Cache/RateLimiter.html) |   | +| Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/{{version}}/Illuminate/Routing/Redirector.html) | `redirect` | +| Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/{{version}}/Illuminate/Redis/Connections/Connection.html) | `redis.connection` | +| Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/{{version}}/Illuminate/Redis/RedisManager.html) | `redis` | +| Request | [Illuminate\Http\Request](https://laravel.com/api/{{version}}/Illuminate/Http/Request.html) | `request` | +| Response (Instance) | [Illuminate\Http\Response](https://laravel.com/api/{{version}}/Illuminate/Http/Response.html) |   | +| Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Routing/ResponseFactory.html) |   | +| Route | [Illuminate\Routing\Router](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) | `router` | +| Schedule | [Illuminate\Console\Scheduling\Schedule](https://laravel.com/api/{{version}}/Illuminate/Console/Scheduling/Schedule.html) |   | +| Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/{{version}}/Illuminate/Database/Schema/Builder.html) |   | +| Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/{{version}}/Illuminate/Session/Store.html) | `session.store` | +| Session | [Illuminate\Session\SessionManager](https://laravel.com/api/{{version}}/Illuminate/Session/SessionManager.html) | `session` | +| Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` | +| Storage | [Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/{{version}}/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` | +| URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/{{version}}/Illuminate/Routing/UrlGenerator.html) | `url` | +| Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/{{version}}/Illuminate/Validation/Validator.html) |   | +| Validator | [Illuminate\Validation\Factory](https://laravel.com/api/{{version}}/Illuminate/Validation/Factory.html) | `validator` | +| View (Instance) | [Illuminate\View\View](https://laravel.com/api/{{version}}/Illuminate/View/View.html) |   | +| View | [Illuminate\View\Factory](https://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` | +| Vite | [Illuminate\Foundation\Vite](https://laravel.com/api/{{version}}/Illuminate/Foundation/Vite.html) |   |
    diff --git a/logging.md b/logging.md index ae71565accf..01a203e8690 100644 --- a/logging.md +++ b/logging.md @@ -41,17 +41,17 @@ Each log channel is powered by a "driver". The driver determines how and where t
    -Name | Description -------------- | ------------- -`custom` | A driver that calls a specified factory to create a channel -`daily` | A `RotatingFileHandler` based Monolog driver which rotates daily -`errorlog` | An `ErrorLogHandler` based Monolog driver -`monolog` | A Monolog factory driver that may use any supported Monolog handler -`papertrail` | A `SyslogUdpHandler` based Monolog driver -`single` | A single file or path based logger channel (`StreamHandler`) -`slack` | A `SlackWebhookHandler` based Monolog driver -`stack` | A wrapper to facilitate creating "multi-channel" channels -`syslog` | A `SyslogHandler` based Monolog driver +| Name | Description | +| ------------ | -------------------------------------------------------------------- | +| `custom` | A driver that calls a specified factory to create a channel. | +| `daily` | A `RotatingFileHandler` based Monolog driver which rotates daily. | +| `errorlog` | An `ErrorLogHandler` based Monolog driver. | +| `monolog` | A Monolog factory driver that may use any supported Monolog handler. | +| `papertrail` | A `SyslogUdpHandler` based Monolog driver. | +| `single` | A single file or path based logger channel (`StreamHandler`). | +| `slack` | A `SlackWebhookHandler` based Monolog driver. | +| `stack` | A wrapper to facilitate creating "multi-channel" channels. | +| `syslog` | A `SyslogHandler` based Monolog driver. |
    @@ -79,11 +79,11 @@ The `single` and `daily` channels have three optional configuration options: `bu
    -Name | Description | Default -------------- | ------------- | ------------- -`bubble` | Indicates if messages should bubble up to other channels after being handled | `true` -`locking` | Attempt to lock the log file before writing to it | `false` -`permission` | The log file's permissions | `0644` +| Name | Description | Default | +| ------------ | ----------------------------------------------------------------------------- | ------- | +| `bubble` | Indicates if messages should bubble up to other channels after being handled. | `true` | +| `locking` | Attempt to lock the log file before writing to it. | `false` | +| `permission` | The log file's permissions. | `0644` |
    @@ -91,9 +91,9 @@ Additionally, the retention policy for the `daily` channel can be configured via
    -Name | Description | Default -------------- |-------------------------------------------------------------------| ------------- -`days` | The number of days that daily log files should be retained | `7` +| Name | Description | Default | +| ------ | ----------------------------------------------------------- | ------- | +| `days` | The number of days that daily log files should be retained. | `7` |
    diff --git a/mail.md b/mail.md index b1ba0ab615f..df469d22326 100644 --- a/mail.md +++ b/mail.md @@ -799,10 +799,10 @@ The table component allows you to transform a Markdown table into an HTML table. ```blade -| Laravel | Table | Example | -| ------------- |:-------------:| --------:| -| Col 2 is | Centered | $10 | -| Col 3 is | Right-Aligned | $20 | +| Laravel | Table | Example | +| ------------- | :-----------: | ------------: | +| Col 2 is | Centered | $10 | +| Col 3 is | Right-Aligned | $20 | ``` diff --git a/middleware.md b/middleware.md index 91c0fd0564b..b56ca30820a 100644 --- a/middleware.md +++ b/middleware.md @@ -222,18 +222,26 @@ Middleware groups may be assigned to routes and controller actions using the sam Laravel includes predefined `web` and `api` middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, Laravel automatically applies these middleware groups to the corresponding `routes/web.php` and `routes/api.php` files: -| The `web` Middleware Group -|-------------- -| `Illuminate\Cookie\Middleware\EncryptCookies` -| `Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse` -| `Illuminate\Session\Middleware\StartSession` -| `Illuminate\View\Middleware\ShareErrorsFromSession` -| `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` -| `Illuminate\Routing\Middleware\SubstituteBindings` - -| The `api` Middleware Group -|-------------- -| `Illuminate\Routing\Middleware\SubstituteBindings` +
    + +| The `web` Middleware Group | +| --- | +| `Illuminate\Cookie\Middleware\EncryptCookies` | +| `Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse` | +| `Illuminate\Session\Middleware\StartSession` | +| `Illuminate\View\Middleware\ShareErrorsFromSession` | +| `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` | +| `Illuminate\Routing\Middleware\SubstituteBindings` | + +
    + +
    + +| The `api` Middleware Group | +| --- | +| `Illuminate\Routing\Middleware\SubstituteBindings` | + +
    If you would like to append or prepend middleware to these groups, you may use the `web` and `api` methods within your application's `bootstrap/app.php` file. The `web` and `api` methods are convenient alternatives to the `appendToGroup` method: @@ -312,20 +320,24 @@ Once the middleware alias has been defined in your application's `bootstrap/app. For convenience, some of Laravel's built-in middleware are aliased by default. For example, the `auth` middleware is an alias for the `Illuminate\Auth\Middleware\Authenticate` middleware. Below is a list of the default middleware aliases: -| Alias | Middleware -|-------|------------ -`auth` | `Illuminate\Auth\Middleware\Authenticate` -`auth.basic` | `Illuminate\Auth\Middleware\AuthenticateWithBasicAuth` -`auth.session` | `Illuminate\Session\Middleware\AuthenticateSession` -`cache.headers` | `Illuminate\Http\Middleware\SetCacheHeaders` -`can` | `Illuminate\Auth\Middleware\Authorize` -`guest` | `Illuminate\Auth\Middleware\RedirectIfAuthenticated` -`password.confirm` | `Illuminate\Auth\Middleware\RequirePassword` -`precognitive` | `Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests` -`signed` | `Illuminate\Routing\Middleware\ValidateSignature` -`subscribed` | `\Spark\Http\Middleware\VerifyBillableIsSubscribed` -`throttle` | `Illuminate\Routing\Middleware\ThrottleRequests` or `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` -`verified` | `Illuminate\Auth\Middleware\EnsureEmailIsVerified` +
    + +| Alias | Middleware | +| --- | --- | +| `auth` | `Illuminate\Auth\Middleware\Authenticate` | +| `auth.basic` | `Illuminate\Auth\Middleware\AuthenticateWithBasicAuth` | +| `auth.session` | `Illuminate\Session\Middleware\AuthenticateSession` | +| `cache.headers` | `Illuminate\Http\Middleware\SetCacheHeaders` | +| `can` | `Illuminate\Auth\Middleware\Authorize` | +| `guest` | `Illuminate\Auth\Middleware\RedirectIfAuthenticated` | +| `password.confirm` | `Illuminate\Auth\Middleware\RequirePassword` | +| `precognitive` | `Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests` | +| `signed` | `Illuminate\Routing\Middleware\ValidateSignature` | +| `subscribed` | `\Spark\Http\Middleware\VerifyBillableIsSubscribed` | +| `throttle` | `Illuminate\Routing\Middleware\ThrottleRequests` or `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` | +| `verified` | `Illuminate\Auth\Middleware\EnsureEmailIsVerified` | + +
    ### Sorting Middleware diff --git a/migrations.md b/migrations.md index b6f72109926..7f8dcb89817 100644 --- a/migrations.md +++ b/migrations.md @@ -939,25 +939,29 @@ In addition to the column types listed above, there are several column "modifier 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 (MariaDB / MySQL). -`->autoIncrement()` | Set INTEGER columns as auto-incrementing (primary key). -`->charset('utf8mb4')` | Specify a character set for the column (MariaDB / MySQL). -`->collation('utf8mb4_unicode_ci')` | Specify a collation for the column. -`->comment('my comment')` | Add a comment to a column (MariaDB / MySQL / PostgreSQL). -`->default($value)` | Specify a "default" value for the column. -`->first()` | Place the column "first" in the table (MariaDB / MySQL). -`->from($integer)` | Set the starting value of an auto-incrementing field (MariaDB / MySQL / PostgreSQL). -`->invisible()` | Make the column "invisible" to `SELECT *` queries (MariaDB / MySQL). -`->nullable($value = true)` | Allow NULL values to be inserted into the column. -`->storedAs($expression)` | Create a stored generated column (MariaDB / MySQL / PostgreSQL / SQLite). -`->unsigned()` | Set INTEGER columns as UNSIGNED (MariaDB / 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 (MariaDB / MySQL). -`->virtualAs($expression)` | Create a virtual generated column (MariaDB / MySQL / SQLite). -`->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). +
    + +| Modifier | Description | +| ----------------------------------- | ---------------------------------------------------------------------------------------------- | +| `->after('column')` | Place the column "after" another column (MariaDB / MySQL). | +| `->autoIncrement()` | Set `INTEGER` columns as auto-incrementing (primary key). | +| `->charset('utf8mb4')` | Specify a character set for the column (MariaDB / MySQL). | +| `->collation('utf8mb4_unicode_ci')` | Specify a collation for the column. | +| `->comment('my comment')` | Add a comment to a column (MariaDB / MySQL / PostgreSQL). | +| `->default($value)` | Specify a "default" value for the column. | +| `->first()` | Place the column "first" in the table (MariaDB / MySQL). | +| `->from($integer)` | Set the starting value of an auto-incrementing field (MariaDB / MySQL / PostgreSQL). | +| `->invisible()` | Make the column "invisible" to `SELECT *` queries (MariaDB / MySQL). | +| `->nullable($value = true)` | Allow `NULL` values to be inserted into the column. | +| `->storedAs($expression)` | Create a stored generated column (MariaDB / MySQL / PostgreSQL / SQLite). | +| `->unsigned()` | Set `INTEGER` columns as `UNSIGNED` (MariaDB / 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 (MariaDB / MySQL). | +| `->virtualAs($expression)` | Create a virtual generated column (MariaDB / MySQL / SQLite). | +| `->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 @@ -1054,14 +1058,18 @@ You may drop multiple columns from a table by passing an array of column names t 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. -`$table->dropRememberToken();` | Drop the `remember_token` column. -`$table->dropSoftDeletes();` | Drop the `deleted_at` column. -`$table->dropSoftDeletesTz();` | Alias of `dropSoftDeletes()` method. -`$table->dropTimestamps();` | Drop the `created_at` and `updated_at` columns. -`$table->dropTimestampsTz();` | Alias of `dropTimestamps()` method. +
    + +| Command | Description | +| ----------------------------------- | ----------------------------------------------------- | +| `$table->dropMorphs('morphable');` | Drop the `morphable_id` and `morphable_type` columns. | +| `$table->dropRememberToken();` | Drop the `remember_token` column. | +| `$table->dropSoftDeletes();` | Drop the `deleted_at` column. | +| `$table->dropSoftDeletesTz();` | Alias of `dropSoftDeletes()` method. | +| `$table->dropTimestamps();` | Drop the `created_at` and `updated_at` columns. | +| `$table->dropTimestampsTz();` | Alias of `dropTimestamps()` method. | + +
    ## Indexes @@ -1095,15 +1103,19 @@ When creating an index, Laravel will automatically generate an index name based 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 is 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 an index. -`$table->fullText('body');` | Adds a full text index (MariaDB / MySQL / PostgreSQL). -`$table->fullText('body')->language('english');` | Adds a full text index of the specified language (PostgreSQL). -`$table->spatialIndex('location');` | Adds a spatial index (except SQLite). +
    + +| 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 an index. | +| `$table->fullText('body');` | Adds a full text index (MariaDB / MySQL / PostgreSQL). | +| `$table->fullText('body')->language('english');` | Adds a full text index of the specified language (PostgreSQL). | +| `$table->spatialIndex('location');` | Adds a spatial index (except SQLite). | + +
    ### Renaming Indexes @@ -1117,13 +1129,17 @@ To rename an index, you may use the `renameIndex` method provided by the schema To drop an index, you must specify the index's name. By default, Laravel automatically assigns an index name based on the table name, the name of the indexed column, and the index type. Here are some examples: -Command | Description -------- | ----------- -`$table->dropPrimary('users_id_primary');` | Drop a primary key from the "users" table. -`$table->dropUnique('users_email_unique');` | Drop a unique index from the "users" table. -`$table->dropIndex('geo_state_index');` | Drop a basic index from the "geo" table. -`$table->dropFullText('posts_body_fulltext');` | Drop a full text index from the "posts" table. -`$table->dropSpatialIndex('geo_location_spatialindex');` | Drop a spatial index from the "geo" table (except SQLite). +
    + +| Command | Description | +| -------------------------------------------------------- | ----------------------------------------------------------- | +| `$table->dropPrimary('users_id_primary');` | Drop a primary key from the "users" table. | +| `$table->dropUnique('users_email_unique');` | Drop a unique index from the "users" table. | +| `$table->dropIndex('geo_state_index');` | Drop a basic index from the "geo" table. | +| `$table->dropFullText('posts_body_fulltext');` | Drop a full text index from the "posts" 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 index type: @@ -1168,8 +1184,10 @@ You may also specify the desired action for the "on delete" and "on update" prop An alternative, expressive syntax is also provided for these actions: +
    + | Method | Description | -|-------------------------------|---------------------------------------------------| +| ----------------------------- | ------------------------------------------------- | | `$table->cascadeOnUpdate();` | Updates should cascade. | | `$table->restrictOnUpdate();` | Updates should be restricted. | | `$table->noActionOnUpdate();` | No action on updates. | @@ -1177,6 +1195,8 @@ An alternative, expressive syntax is also provided for these actions: | `$table->restrictOnDelete();` | Deletes should be restricted. | | `$table->nullOnDelete();` | Deletes should set the foreign key value to null. | +
    + Any additional [column modifiers](#column-modifiers) must be called before the `constrained` method: $table->foreignId('user_id') @@ -1215,12 +1235,16 @@ You may enable or disable foreign key constraints within your migrations by usin For convenience, each migration operation will dispatch an [event](/docs/{{version}}/events). All of the following events extend the base `Illuminate\Database\Events\MigrationEvent` class: - Class | Description --------|------- -| `Illuminate\Database\Events\MigrationsStarted` | A batch of migrations is about to be executed. | -| `Illuminate\Database\Events\MigrationsEnded` | A batch of migrations has finished executing. | -| `Illuminate\Database\Events\MigrationStarted` | A single migration is about to be executed. | -| `Illuminate\Database\Events\MigrationEnded` | A single migration has finished executing. | +
    + +| Class | Description | +| ------------------------------------------------ | ------------------------------------------------ | +| `Illuminate\Database\Events\MigrationsStarted` | A batch of migrations is about to be executed. | +| `Illuminate\Database\Events\MigrationsEnded` | A batch of migrations has finished executing. | +| `Illuminate\Database\Events\MigrationStarted` | A single migration is about to be executed. | +| `Illuminate\Database\Events\MigrationEnded` | A single migration has finished executing. | | `Illuminate\Database\Events\NoPendingMigrations` | A migration command found no pending migrations. | -| `Illuminate\Database\Events\SchemaDumped` | A database schema dump has completed. | -| `Illuminate\Database\Events\SchemaLoaded` | An existing database schema dump has loaded. | +| `Illuminate\Database\Events\SchemaDumped` | A database schema dump has completed. | +| `Illuminate\Database\Events\SchemaLoaded` | An existing database schema dump has loaded. | + +
    diff --git a/notifications.md b/notifications.md index e6676d8d99b..c195e8b6827 100644 --- a/notifications.md +++ b/notifications.md @@ -780,10 +780,10 @@ The table component allows you to transform a Markdown table into an HTML table. ```blade -| Laravel | Table | Example | -| ------------- |:-------------:| --------:| -| Col 2 is | Centered | $10 | -| Col 3 is | Right-Aligned | $20 | +| Laravel | Table | Example | +| ------------- | :-----------: | ------------: | +| Col 2 is | Centered | $10 | +| Col 3 is | Right-Aligned | $20 | ``` diff --git a/pagination.md b/pagination.md index d55a67fa6c2..91ba4af9141 100644 --- a/pagination.md +++ b/pagination.md @@ -332,48 +332,56 @@ Laravel includes pagination views built using [Bootstrap CSS](https://getbootstr Each paginator instance provides additional pagination information via the following methods: -Method | Description -------- | ----------- -`$paginator->count()` | Get the number of items for the current page. -`$paginator->currentPage()` | Get the current page number. -`$paginator->firstItem()` | Get the result number of the first item in the results. -`$paginator->getOptions()` | Get the paginator options. -`$paginator->getUrlRange($start, $end)` | Create a range of pagination URLs. -`$paginator->hasPages()` | Determine if there are enough items to split into multiple pages. -`$paginator->hasMorePages()` | Determine if there are more items in the data store. -`$paginator->items()` | Get the items for the current page. -`$paginator->lastItem()` | Get the result number of the last item in the results. -`$paginator->lastPage()` | Get the page number of the last available page. (Not available when using `simplePaginate`). -`$paginator->nextPageUrl()` | Get the URL for the next page. -`$paginator->onFirstPage()` | Determine if the paginator is on the first page. -`$paginator->perPage()` | The number of items to be shown per page. -`$paginator->previousPageUrl()` | Get the URL for the previous page. -`$paginator->total()` | Determine the total number of matching items in the data store. (Not available when using `simplePaginate`). -`$paginator->url(https://github.com/$page)` | Get the URL for a given page number. -`$paginator->getPageName()` | Get the query string variable used to store the page. -`$paginator->setPageName($name)` | Set the query string variable used to store the page. -`$paginator->through($callback)` | Transform each item using a callback. +
    + +| Method | Description | +| --- | --- | +| `$paginator->count()` | Get the number of items for the current page. | +| `$paginator->currentPage()` | Get the current page number. | +| `$paginator->firstItem()` | Get the result number of the first item in the results. | +| `$paginator->getOptions()` | Get the paginator options. | +| `$paginator->getUrlRange($start, $end)` | Create a range of pagination URLs. | +| `$paginator->hasPages()` | Determine if there are enough items to split into multiple pages. | +| `$paginator->hasMorePages()` | Determine if there are more items in the data store. | +| `$paginator->items()` | Get the items for the current page. | +| `$paginator->lastItem()` | Get the result number of the last item in the results. | +| `$paginator->lastPage()` | Get the page number of the last available page. (Not available when using `simplePaginate`). | +| `$paginator->nextPageUrl()` | Get the URL for the next page. | +| `$paginator->onFirstPage()` | Determine if the paginator is on the first page. | +| `$paginator->perPage()` | The number of items to be shown per page. | +| `$paginator->previousPageUrl()` | Get the URL for the previous page. | +| `$paginator->total()` | Determine the total number of matching items in the data store. (Not available when using `simplePaginate`). | +| `$paginator->url(https://github.com/$page)` | Get the URL for a given page number. | +| `$paginator->getPageName()` | Get the query string variable used to store the page. | +| `$paginator->setPageName($name)` | Set the query string variable used to store the page. | +| `$paginator->through($callback)` | Transform each item using a callback. | + +
    ## Cursor Paginator Instance Methods Each cursor paginator instance provides additional pagination information via the following methods: -Method | Description -------- | ----------- -`$paginator->count()` | Get the number of items for the current page. -`$paginator->cursor()` | Get the current cursor instance. -`$paginator->getOptions()` | Get the paginator options. -`$paginator->hasPages()` | Determine if there are enough items to split into multiple pages. -`$paginator->hasMorePages()` | Determine if there are more items in the data store. -`$paginator->getCursorName()` | Get the query string variable used to store the cursor. -`$paginator->items()` | Get the items for the current page. -`$paginator->nextCursor()` | Get the cursor instance for the next set of items. -`$paginator->nextPageUrl()` | Get the URL for the next page. -`$paginator->onFirstPage()` | Determine if the paginator is on the first page. -`$paginator->onLastPage()` | Determine if the paginator is on the last page. -`$paginator->perPage()` | The number of items to be shown per page. -`$paginator->previousCursor()` | Get the cursor instance for the previous set of items. -`$paginator->previousPageUrl()` | Get the URL for the previous page. -`$paginator->setCursorName()` | Set the query string variable used to store the cursor. -`$paginator->url(https://github.com/$cursor)` | Get the URL for a given cursor instance. +
    + +| Method | Description | +| ------------------------------- | ----------------------------------------------------------------- | +| `$paginator->count()` | Get the number of items for the current page. | +| `$paginator->cursor()` | Get the current cursor instance. | +| `$paginator->getOptions()` | Get the paginator options. | +| `$paginator->hasPages()` | Determine if there are enough items to split into multiple pages. | +| `$paginator->hasMorePages()` | Determine if there are more items in the data store. | +| `$paginator->getCursorName()` | Get the query string variable used to store the cursor. | +| `$paginator->items()` | Get the items for the current page. | +| `$paginator->nextCursor()` | Get the cursor instance for the next set of items. | +| `$paginator->nextPageUrl()` | Get the URL for the next page. | +| `$paginator->onFirstPage()` | Determine if the paginator is on the first page. | +| `$paginator->onLastPage()` | Determine if the paginator is on the last page. | +| `$paginator->perPage()` | The number of items to be shown per page. | +| `$paginator->previousCursor()` | Get the cursor instance for the previous set of items. | +| `$paginator->previousPageUrl()` | Get the URL for the previous page. | +| `$paginator->setCursorName()` | Set the query string variable used to store the cursor. | +| `$paginator->url(https://github.com/$cursor)` | Get the URL for a given cursor instance. | + +
    diff --git a/passport.md b/passport.md index 315d906bbbf..c3b5cc4308c 100644 --- a/passport.md +++ b/passport.md @@ -1162,10 +1162,10 @@ When using this method of authentication, you will need to ensure a valid CSRF t Passport raises events when issuing access tokens and refresh tokens. You may [listen for these events](/docs/{{version}}/events) to prune or revoke other access tokens in your database: -Event Name | -------------- | -`Laravel\Passport\Events\AccessTokenCreated` | -`Laravel\Passport\Events\RefreshTokenCreated` | +| Event Name | +| --- | +| `Laravel\Passport\Events\AccessTokenCreated` | +| `Laravel\Passport\Events\RefreshTokenCreated` | ## Testing diff --git a/requests.md b/requests.md index aef7f57ae4c..65871c45a82 100644 --- a/requests.md +++ b/requests.md @@ -649,4 +649,3 @@ By default, requests coming from subdomains of the application's URL are also au ->withMiddleware(function (Middleware $middleware) { $middleware->trustHosts(at: ['laravel.test'], subdomains: false); }) - diff --git a/scheduling.md b/scheduling.md index 9afd97f8ca0..3c472d87ffc 100644 --- a/scheduling.md +++ b/scheduling.md @@ -120,46 +120,46 @@ We've already seen a few examples of how you may configure a task to run at spec
    -Method | Description -------------- | ------------- -`->cron('* * * * *');` | Run the task on a custom cron schedule -`->everySecond();` | Run the task every second -`->everyTwoSeconds();` | Run the task every two seconds -`->everyFiveSeconds();` | Run the task every five seconds -`->everyTenSeconds();` | Run the task every ten seconds -`->everyFifteenSeconds();` | Run the task every fifteen seconds -`->everyTwentySeconds();` | Run the task every twenty seconds -`->everyThirtySeconds();` | Run the task every thirty seconds -`->everyMinute();` | Run the task every minute -`->everyTwoMinutes();` | Run the task every two minutes -`->everyThreeMinutes();` | Run the task every three minutes -`->everyFourMinutes();` | Run the task every four minutes -`->everyFiveMinutes();` | Run the task every five minutes -`->everyTenMinutes();` | Run the task every ten minutes -`->everyFifteenMinutes();` | Run the task every fifteen minutes -`->everyThirtyMinutes();` | Run the task every thirty minutes -`->hourly();` | Run the task every hour -`->hourlyAt(17);` | Run the task every hour at 17 minutes past the hour -`->everyOddHour($minutes = 0);` | Run the task every odd hour -`->everyTwoHours($minutes = 0);` | Run the task every two hours -`->everyThreeHours($minutes = 0);` | Run the task every three hours -`->everyFourHours($minutes = 0);` | Run the task every four hours -`->everySixHours($minutes = 0);` | Run the task every six hours -`->daily();` | Run the task every day at midnight -`->dailyAt('13:00');` | Run the task every day at 13:00 -`->twiceDaily(1, 13);` | Run the task daily at 1:00 & 13:00 -`->twiceDailyAt(1, 13, 15);` | Run the task daily at 1:15 & 13:15 -`->weekly();` | Run the task every Sunday at 00:00 -`->weeklyOn(1, '8:00');` | Run the task every week on Monday at 8:00 -`->monthly();` | Run the task on the first day of every month at 00:00 -`->monthlyOn(4, '15:00');` | Run the task every month on the 4th at 15:00 -`->twiceMonthly(1, 16, '13:00');` | Run the task monthly on the 1st and 16th at 13:00 -`->lastDayOfMonth('15:00');` | Run the task on the last day of the month at 15:00 -`->quarterly();` | Run the task on the first day of every quarter at 00:00 -`->quarterlyOn(4, '14:00');` | Run the task every quarter on the 4th at 14: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 for the task +| Method | Description | +| ---------------------------------- | -------------------------------------------------------- | +| `->cron('* * * * *');` | Run the task on a custom cron schedule. | +| `->everySecond();` | Run the task every second. | +| `->everyTwoSeconds();` | Run the task every two seconds. | +| `->everyFiveSeconds();` | Run the task every five seconds. | +| `->everyTenSeconds();` | Run the task every ten seconds. | +| `->everyFifteenSeconds();` | Run the task every fifteen seconds. | +| `->everyTwentySeconds();` | Run the task every twenty seconds. | +| `->everyThirtySeconds();` | Run the task every thirty seconds. | +| `->everyMinute();` | Run the task every minute. | +| `->everyTwoMinutes();` | Run the task every two minutes. | +| `->everyThreeMinutes();` | Run the task every three minutes. | +| `->everyFourMinutes();` | Run the task every four minutes. | +| `->everyFiveMinutes();` | Run the task every five minutes. | +| `->everyTenMinutes();` | Run the task every ten minutes. | +| `->everyFifteenMinutes();` | Run the task every fifteen minutes. | +| `->everyThirtyMinutes();` | Run the task every thirty minutes. | +| `->hourly();` | Run the task every hour. | +| `->hourlyAt(17);` | Run the task every hour at 17 minutes past the hour. | +| `->everyOddHour($minutes = 0);` | Run the task every odd hour. | +| `->everyTwoHours($minutes = 0);` | Run the task every two hours. | +| `->everyThreeHours($minutes = 0);` | Run the task every three hours. | +| `->everyFourHours($minutes = 0);` | Run the task every four hours. | +| `->everySixHours($minutes = 0);` | Run the task every six hours. | +| `->daily();` | Run the task every day at midnight. | +| `->dailyAt('13:00');` | Run the task every day at 13:00. | +| `->twiceDaily(1, 13);` | Run the task daily at 1:00 & 13:00. | +| `->twiceDailyAt(1, 13, 15);` | Run the task daily at 1:15 & 13:15. | +| `->weekly();` | Run the task every Sunday at 00:00. | +| `->weeklyOn(1, '8:00');` | Run the task every week on Monday at 8:00. | +| `->monthly();` | Run the task on the first day of every month at 00:00. | +| `->monthlyOn(4, '15:00');` | Run the task every month on the 4th at 15:00. | +| `->twiceMonthly(1, 16, '13:00');` | Run the task monthly on the 1st and 16th at 13:00. | +| `->lastDayOfMonth('15:00');` | Run the task on the last day of the month at 15:00. | +| `->quarterly();` | Run the task on the first day of every quarter at 00:00. | +| `->quarterlyOn(4, '14:00');` | Run the task every quarter on the 4th at 14: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 for the task. |
    @@ -183,22 +183,22 @@ A list of additional schedule constraints may be found below:
    -Method | Description -------------- | ------------- -`->weekdays();` | Limit the task to weekdays -`->weekends();` | Limit the task to weekends -`->sundays();` | Limit the task to Sunday -`->mondays();` | Limit the task to Monday -`->tuesdays();` | Limit the task to Tuesday -`->wednesdays();` | Limit the task to Wednesday -`->thursdays();` | Limit the task to Thursday -`->fridays();` | Limit the task to Friday -`->saturdays();` | Limit the task to Saturday -`->days(array\|mixed);` | Limit the task to specific days -`->between($startTime, $endTime);` | Limit the task to run between start and end times -`->unlessBetween($startTime, $endTime);` | Limit the task to not run between start and end times -`->when(Closure);` | Limit the task based on a truth test -`->environments($env);` | Limit the task to specific environments +| Method | Description | +| ---------------------------------------- | ------------------------------------------------------ | +| `->weekdays();` | Limit the task to weekdays. | +| `->weekends();` | Limit the task to weekends. | +| `->sundays();` | Limit the task to Sunday. | +| `->mondays();` | Limit the task to Monday. | +| `->tuesdays();` | Limit the task to Tuesday. | +| `->wednesdays();` | Limit the task to Wednesday. | +| `->thursdays();` | Limit the task to Thursday. | +| `->fridays();` | Limit the task to Friday. | +| `->saturdays();` | Limit the task to Saturday. | +| `->days(array\|mixed);` | Limit the task to specific days. | +| `->between($startTime, $endTime);` | Limit the task to run between start and end times. | +| `->unlessBetween($startTime, $endTime);` | Limit the task to not run between start and end times. | +| `->when(Closure);` | Limit the task based on a truth test. | +| `->environments($env);` | Limit the task to specific environments. |
    @@ -519,10 +519,14 @@ The `pingOnSuccess` and `pingOnFailure` methods may be used to ping a given URL Laravel dispatches a variety of [events](/docs/{{version}}/events) during the scheduling process. You may [define listeners](/docs/{{version}}/events) for any of the following events: -Event Name | -------------- | -`Illuminate\Console\Events\ScheduledTaskStarting` | -`Illuminate\Console\Events\ScheduledTaskFinished` | -`Illuminate\Console\Events\ScheduledBackgroundTaskFinished` | -`Illuminate\Console\Events\ScheduledTaskSkipped` | -`Illuminate\Console\Events\ScheduledTaskFailed` | +
    + +| Event Name | +| --- | +| `Illuminate\Console\Events\ScheduledTaskStarting` | +| `Illuminate\Console\Events\ScheduledTaskFinished` | +| `Illuminate\Console\Events\ScheduledBackgroundTaskFinished` | +| `Illuminate\Console\Events\ScheduledTaskSkipped` | +| `Illuminate\Console\Events\ScheduledTaskFailed` | + +
    diff --git a/valet.md b/valet.md index 1676404a460..0a324237735 100644 --- a/valet.md +++ b/valet.md @@ -465,19 +465,19 @@ If you would like to define a custom Valet driver for a single application, crea
    -Command | Description -------------- | ------------- -`valet list` | Display a list of all Valet commands. -`valet diagnose` | Output diagnostics to aid in debugging Valet. -`valet directory-listing` | Determine directory-listing behavior. Default is "off", which renders a 404 page for directories. -`valet forget` | Run this command from a "parked" directory to remove it from the parked directory list. -`valet log` | View a list of logs which are written by Valet's services. -`valet paths` | View all of your "parked" paths. -`valet restart` | Restart the Valet daemons. -`valet start` | Start the Valet daemons. -`valet stop` | Stop the Valet daemons. -`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. +| Command | Description | +| --- | --- | +| `valet list` | Display a list of all Valet commands. | +| `valet diagnose` | Output diagnostics to aid in debugging Valet. | +| `valet directory-listing` | Determine directory-listing behavior. Default is "off", which renders a 404 page for directories. | +| `valet forget` | Run this command from a "parked" directory to remove it from the parked directory list. | +| `valet log` | View a list of logs which are written by Valet's services. | +| `valet paths` | View all of your "parked" paths. | +| `valet restart` | Restart the Valet daemons. | +| `valet start` | Start the Valet daemons. | +| `valet stop` | Stop the Valet daemons. | +| `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. |
    From 9f36b02f2c2968ad2c6945df79d9eaf31dfdd224 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Fri, 19 Jul 2024 21:09:56 +0700 Subject: [PATCH 1742/2609] Fix whitespaces (#9780) --- artisan.md | 2 +- authentication.md | 4 ++-- billing.md | 10 +++++----- cashier-paddle.md | 6 +++--- collections.md | 40 +++++++++++++++++++-------------------- configuration.md | 2 +- container.md | 2 +- context.md | 4 ++-- controllers.md | 2 +- database-testing.md | 2 +- database.md | 2 +- dusk.md | 22 ++++++++++----------- eloquent-collections.md | 6 +++--- eloquent-relationships.md | 2 +- eloquent-resources.md | 2 +- eloquent.md | 4 ++-- errors.md | 2 +- events.md | 6 +++--- helpers.md | 4 ++-- http-tests.md | 6 +++--- installation.md | 8 ++++---- localization.md | 4 ++-- logging.md | 2 +- migrations.md | 2 +- mocking.md | 2 +- notifications.md | 2 +- octane.md | 2 +- pagination.md | 2 +- pennant.md | 9 ++++++--- precognition.md | 4 ++-- pulse.md | 14 +++++++------- routing.md | 2 +- scout.md | 2 +- seeding.md | 2 +- strings.md | 6 +++--- testing.md | 4 ++-- upgrade.md | 2 +- vite.md | 2 +- 38 files changed, 102 insertions(+), 99 deletions(-) diff --git a/artisan.md b/artisan.md index 1681994dd65..ae3d061a6ff 100644 --- a/artisan.md +++ b/artisan.md @@ -649,7 +649,7 @@ Sometimes, you may need more manual control over how a progress bar is advanced. $bar->finish(); -> [!NOTE] +> [!NOTE] > For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/7.0/components/console/helpers/progressbar.html). diff --git a/authentication.md b/authentication.md index 32248ef9c3a..ffad7e5445a 100644 --- a/authentication.md +++ b/authentication.md @@ -280,8 +280,8 @@ For complex query conditions, you may provide a closure in your array of credent use Illuminate\Database\Eloquent\Builder; if (Auth::attempt([ - 'email' => $email, - 'password' => $password, + 'email' => $email, + 'password' => $password, fn (Builder $query) => $query->has('activeSubscription'), ])) { // Authentication was successful... diff --git a/billing.md b/billing.md index 6a03e8ec69a..782851c54d5 100644 --- a/billing.md +++ b/billing.md @@ -251,7 +251,7 @@ Offering product and subscription billing via your application can be intimidati To charge customers for non-recurring, single-charge products, we'll utilize Cashier to direct customers to Stripe Checkout, where they will provide their payment details and confirm their purchase. Once the payment has been made via Checkout, the customer will be redirected to a success URL of your choosing within your application: use Illuminate\Http\Request; - + Route::get('/checkout', function (Request $request) { $stripePriceId = 'price_deluxe_album'; @@ -276,11 +276,11 @@ If necessary, the `checkout` method will automatically create a customer in Stri When selling products, it's common to keep track of completed orders and purchased products via `Cart` and `Order` models defined by your own application. When redirecting customers to Stripe Checkout to complete a purchase, you may need to provide an existing order identifier so that you can associate the completed purchase with the corresponding order when the customer is redirected back to your application. To accomplish this, you may provide an array of `metadata` to the `checkout` method. Let's imagine that a pending `Order` is created within our application when a user begins the checkout process. Remember, the `Cart` and `Order` models in this example are illustrative and not provided by Cashier. You are free to implement these concepts based on the needs of your own application: - + use App\Models\Cart; use App\Models\Order; use Illuminate\Http\Request; - + Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) { $order = Order::create([ 'cart_id' => $cart->id, @@ -340,7 +340,7 @@ To learn how to sell subscriptions using Cashier and Stripe Checkout, let's cons First, let's discover how a customer can subscribe to our services. Of course, you can imagine the customer might click a "subscribe" button for the Basic plan on our application's pricing page. This button or link should direct the user to a Laravel route which creates the Stripe Checkout session for their chosen plan: use Illuminate\Http\Request; - + Route::get('/subscription-checkout', function (Request $request) { return $request->user() ->newSubscription('default', 'price_basic_monthly') @@ -2215,7 +2215,7 @@ Some payment methods require additional data in order to confirm payments. For e $subscription->withPaymentConfirmationOptions([ 'mandate_data' => '...', ])->swap('price_xxx'); - + You may consult the [Stripe API documentation](https://stripe.com/docs/api/payment_intents/confirm) to review all of the options accepted when confirming payments. diff --git a/cashier-paddle.md b/cashier-paddle.md index 7e7e96bcdf7..dc89180111b 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -201,7 +201,7 @@ After defining your model, you may instruct Cashier to use your custom model via ### Selling Products -> [!NOTE] +> [!NOTE] > Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should [configure Paddle's webhook handling](#handling-paddle-webhooks). Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://www.paddle.com/billing/checkout), you can easily build modern, robust payment integrations. @@ -235,11 +235,11 @@ In the `buy` view, we will include a button to display the Checkout Overlay. The When selling products, it's common to keep track of completed orders and purchased products via `Cart` and `Order` models defined by your own application. When redirecting customers to Paddle's Checkout Overlay to complete a purchase, you may need to provide an existing order identifier so that you can associate the completed purchase with the corresponding order when the customer is redirected back to your application. To accomplish this, you may provide an array of custom data to the `checkout` method. Let's imagine that a pending `Order` is created within our application when a user begins the checkout process. Remember, the `Cart` and `Order` models in this example are illustrative and not provided by Cashier. You are free to implement these concepts based on the needs of your own application: - + use App\Models\Cart; use App\Models\Order; use Illuminate\Http\Request; - + Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) { $order = Order::create([ 'cart_id' => $cart->id, diff --git a/collections.md b/collections.md index c4cfff89371..17641933a6f 100644 --- a/collections.md +++ b/collections.md @@ -31,7 +31,7 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle $collection = collect([1, 2, 3]); -> [!NOTE] +> [!NOTE] > The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. @@ -432,7 +432,7 @@ The `collect` method is primarily useful for converting [lazy collections](#lazy // [1, 2, 3] -> [!NOTE] +> [!NOTE] > The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. @@ -525,7 +525,7 @@ The `containsOneItem` method determines whether the collection contains a single This method has the same signature as the [`contains`](#method-contains) method; however, all values are compared using "strict" comparisons. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). @@ -636,7 +636,7 @@ The `diff` method compares the collection against another collection or a plain // [1, 3, 5] -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). @@ -856,7 +856,7 @@ Primitive types such as `string`, `int`, `float`, `bool`, and `array` may also b return $collection->ensure('int'); -> [!WARNING] +> [!WARNING] > The `ensure` method does not guarantee that elements of different types will not be added to the collection at a later time. @@ -895,7 +895,7 @@ The `except` method returns all items in the collection except for those with th For the inverse of `except`, see the [only](#method-only) method. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). @@ -1078,7 +1078,7 @@ The `forget` method removes an item from the collection by its key: // ['framework' => 'laravel'] -> [!WARNING] +> [!WARNING] > Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. @@ -1281,7 +1281,7 @@ The `intersect` method removes any values from the original collection that are // [0 => 'Desk', 2 => 'Chair'] -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). @@ -1470,7 +1470,7 @@ The `map` method iterates through the collection and passes each value to the gi // [2, 4, 6, 8, 10] -> [!WARNING] +> [!WARNING] > Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. @@ -1750,7 +1750,7 @@ The `only` method returns the items in the collection with the specified keys: For the inverse of `only`, see the [except](#method-except) method. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). @@ -2322,7 +2322,7 @@ You may also pass a simple value to the `skipUntil` method to skip all items unt // [3, 4] -> [!WARNING] +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. @@ -2340,7 +2340,7 @@ The `skipWhile` method skips over items from the collection while the given call // [4] -> [!WARNING] +> [!WARNING] > If the callback never returns `false`, the `skipWhile` method will return an empty collection. @@ -2449,7 +2449,7 @@ The `sort` method sorts the collection. The sorted collection keeps the original If your sorting needs are more advanced, you may pass a callback to `sort` with your own algorithm. Refer to the PHP documentation on [`uasort`](https://secure.php.net/manual/en/function.uasort.php#refsect1-function.uasort-parameters), which is what the collection's `sort` method calls utilizes internally. -> [!NOTE] +> [!NOTE] > If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. @@ -2793,7 +2793,7 @@ You may also pass a simple value to the `takeUntil` method to get the items unti // [1, 2] -> [!WARNING] +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. @@ -2811,7 +2811,7 @@ The `takeWhile` method returns items in the collection until the given callback // [1, 2] -> [!WARNING] +> [!WARNING] > If the callback never returns `false`, the `takeWhile` method will return all items in the collection. @@ -2856,7 +2856,7 @@ The `toArray` method converts the collection into a plain PHP `array`. If the co ] */ -> [!WARNING] +> [!WARNING] > `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw array underlying the collection, use the [`all`](#method-all) method instead. @@ -2885,7 +2885,7 @@ The `transform` method iterates over the collection and calls the given callback // [2, 4, 6, 8, 10] -> [!WARNING] +> [!WARNING] > Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. @@ -2989,7 +2989,7 @@ Finally, you may also pass your own closure to the `unique` method to specify wh The `unique` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`uniqueStrict`](#method-uniquestrict) method to filter using "strict" comparisons. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). @@ -3498,7 +3498,7 @@ Likewise, we can use the `sum` higher order message to gather the total number o ### Introduction -> [!WARNING] +> [!WARNING] > Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). To supplement the already powerful `Collection` class, the `LazyCollection` class leverages PHP's [generators](https://www.php.net/manual/en/language.generators.overview.php) to allow you to work with very large datasets while keeping memory usage low. @@ -3689,7 +3689,7 @@ Almost all methods available on the `Collection` class are also available on the -> [!WARNING] +> [!WARNING] > Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. diff --git a/configuration.md b/configuration.md index ad3ed6a3a43..93306a3e2a9 100644 --- a/configuration.md +++ b/configuration.md @@ -51,7 +51,7 @@ Laravel's default `.env` file contains some common configuration values that may If you are developing with a team, you may wish to continue including and updating the `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. -> [!NOTE] +> [!NOTE] > Any variable in your `.env` file can be overridden by external environment variables such as server-level or system-level environment variables. diff --git a/container.md b/container.md index dc804e9ba04..7a1709cad4f 100644 --- a/container.md +++ b/container.md @@ -231,7 +231,7 @@ Sometimes you may have two classes that utilize the same interface, but you wish Sometimes you may have a class that receives some injected classes, but also needs an injected primitive value such as an integer. You may easily use contextual binding to inject any value your class may need: use App\Http\Controllers\UserController; - + $this->app->when(UserController::class) ->needs('$variableName') ->give($value); diff --git a/context.md b/context.md index 1ef39c20f83..43b91b00387 100644 --- a/context.md +++ b/context.md @@ -318,7 +318,7 @@ public function boot(): void } ``` -> [!NOTE] +> [!NOTE] > You should not use the `Context` facade within the `dehydrating` callback, as that will change the context of the current process. Ensure you only make changes to the repository passed to the callback. @@ -346,5 +346,5 @@ public function boot(): void } ``` -> [!NOTE] +> [!NOTE] > You should not use the `Context` facade within the `hydrated` callback and instead ensure you only make changes to the repository passed to the callback. diff --git a/controllers.md b/controllers.md index 54fc36bde6a..8bd7b1b1fff 100644 --- a/controllers.md +++ b/controllers.md @@ -38,7 +38,7 @@ Let's take a look at an example of a basic controller. A controller may have any assertSoftDeleted($user); - + #### assertNotSoftDeleted diff --git a/database.md b/database.md index dd528b6546b..ffa38b9d1b7 100644 --- a/database.md +++ b/database.md @@ -50,7 +50,7 @@ By default, foreign key constraints are enabled for SQLite connections. If you w DB_FOREIGN_KEYS=false ``` -> [!NOTE] +> [!NOTE] > If you use the [Laravel installer](/docs/{{version}}/installation#creating-a-laravel-project) to create your Laravel application and select SQLite as your database, Laravel will automatically create a `database/database.sqlite` file and run the default [database migrations](/docs/{{version}}/migrations) for you. diff --git a/dusk.md b/dusk.md index 22acb86dccc..dc4d1d7d9b1 100644 --- a/dusk.md +++ b/dusk.md @@ -64,7 +64,7 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome composer require laravel/dusk --dev ``` -> [!WARNING] +> [!WARNING] > If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operating system: @@ -75,7 +75,7 @@ php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. -> [!NOTE] +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -97,7 +97,7 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> [!WARNING] +> [!WARNING] > Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. @@ -181,7 +181,7 @@ class ExampleTest extends DuskTestCase } ``` -> [!WARNING] +> [!WARNING] > SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. @@ -220,7 +220,7 @@ class ExampleTest extends DuskTestCase By default, this trait will truncate all tables except the `migrations` table. If you would like to customize the tables that should be truncated, you may define a `$tablesToTruncate` property on your test class: -> [!NOTE] +> [!NOTE] > If you are using Pest, you should define properties or methods on the base `DuskTestCase` class or on any class your test file extends. /** @@ -287,7 +287,7 @@ The `dusk` command accepts any argument that is normally accepted by the Pest / php artisan dusk --group=foo ``` -> [!NOTE] +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -506,7 +506,7 @@ Often, you will be testing pages that require authentication. You can use Dusk's ->visit('/home'); }); -> [!WARNING] +> [!WARNING] > After using the `loginAs` method, the user session will be maintained for all tests within the file. @@ -707,7 +707,7 @@ The `attach` method may be used to attach a file to a `file` input element. Like $browser->attach('photo', __DIR__.'/photos/mountains.png'); -> [!WARNING] +> [!WARNING] > The attach function requires the `Zip` PHP extension to be installed and enabled on your server. @@ -738,7 +738,7 @@ You may use the `seeLink` method to determine if a link with the given display t // ... } -> [!WARNING] +> [!WARNING] > These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. @@ -752,7 +752,7 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" $browser->keys('.app', ['{command}', 'j']); -> [!NOTE] +> [!NOTE] > All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). @@ -2108,7 +2108,7 @@ class ExampleTest extends DuskTestCase ## Continuous Integration -> [!WARNING] +> [!WARNING] > Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. diff --git a/eloquent-collections.md b/eloquent-collections.md index 10f2192e1c5..5808b9c5868 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -88,7 +88,7 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe The `append` method may be used to indicate that an attribute should be [appended](/docs/{{version}}/eloquent-serialization#appending-values-to-json) for every model in the collection. This method accepts an array of attributes or a single attribute: $users->append('team'); - + $users->append(['team', 'is_admin']); @@ -151,7 +151,7 @@ The `load` method eager loads the given relationships for all models in the coll $users->load(['comments', 'posts']); $users->load('comments.author'); - + $users->load(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); @@ -162,7 +162,7 @@ The `loadMissing` method eager loads the given relationships for all models in t $users->loadMissing(['comments', 'posts']); $users->loadMissing('comments.author'); - + $users->loadMissing(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 088dc480544..0c64bac641a 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1941,7 +1941,7 @@ The `createQuietly` and `createManyQuietly` methods may be used to create a mode $user->posts()->createQuietly([ 'title' => 'Post title.', ]); - + $user->posts()->createManyQuietly([ ['title' => 'First post.'], ['title' => 'Second post.'], diff --git a/eloquent-resources.md b/eloquent-resources.md index 2623c7412b5..20e0122c2bd 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -493,7 +493,7 @@ If you would like to customize the information included in the `links` or `meta` return $default; } - + ### Conditional Attributes diff --git a/eloquent.md b/eloquent.md index c31d51832bd..46d75a76917 100644 --- a/eloquent.md +++ b/eloquent.md @@ -895,7 +895,7 @@ Eloquent's `upsert` method may be used to update or create records in a single, ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] ], uniqueBy: ['departure', 'destination'], update: ['price']); - + > [!WARNING] > All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MariaDB and MySQL database drivers ignore the second argument of the `upsert` method and always use the "primary" and "unique" indexes of the table to detect existing records. @@ -1510,7 +1510,7 @@ This command will place the new observer in your `app/Observers` directory. If t { // ... } - + /** * Handle the User "restored" event. */ diff --git a/errors.md b/errors.md index 1562d825a85..b87b4f1ef44 100644 --- a/errors.md +++ b/errors.md @@ -410,6 +410,6 @@ php artisan vendor:publish --tag=laravel-errors #### Fallback HTTP Error Pages -You may also define a "fallback" error page for a given series of HTTP status codes. This page will be rendered if there is not a corresponding page for the specific HTTP status code that occurred. To accomplish this, define a `4xx.blade.php` template and a `5xx.blade.php` template in your application's `resources/views/errors` directory. +You may also define a "fallback" error page for a given series of HTTP status codes. This page will be rendered if there is not a corresponding page for the specific HTTP status code that occurred. To accomplish this, define a `4xx.blade.php` template and a `5xx.blade.php` template in your application's `resources/views/errors` directory. When defining fallback error pages, the fallback pages will not affect `404`, `500`, and `503` error responses since Laravel has internal, dedicated pages for these status codes. To customize the pages rendered for these status codes, you should define a custom error page for each of them individually. diff --git a/events.md b/events.md index 916aad6b623..a5e21f2dde1 100644 --- a/events.md +++ b/events.md @@ -234,7 +234,7 @@ Next, let's take a look at the listener for our example event. Event listeners r } } -> [!NOTE] +> [!NOTE] > Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. @@ -404,7 +404,7 @@ If your queue connection's `after_commit` configuration option is set to `false` use InteractsWithQueue; } -> [!NOTE] +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -519,7 +519,7 @@ If you would like to conditionally dispatch an event, you may use the `dispatchI OrderShipped::dispatchUnless($condition, $order); -> [!NOTE] +> [!NOTE] > When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) make it a cinch. diff --git a/helpers.md b/helpers.md index 3baf5b3fa19..72724035102 100644 --- a/helpers.md +++ b/helpers.md @@ -1322,7 +1322,7 @@ $result = Number::pairs(25, 10); // [[1, 10], [11, 20], [21, 25]] $result = Number::pairs(25, 10, offset: 0); - + // [[0, 10], [10, 20], [20, 25]] ``` @@ -2260,7 +2260,7 @@ Additional arguments may be passed to the `value` function. If the first argumen $result = value(function (string $name) { return $name; }, 'Taylor'); - + // 'Taylor' diff --git a/http-tests.md b/http-tests.md index 13e2e095c06..73f6faeafef 100644 --- a/http-tests.md +++ b/http-tests.md @@ -95,7 +95,7 @@ class ExampleTest extends TestCase In general, each of your tests should only make one request to your application. Unexpected behavior may occur if multiple requests are executed within a single test method. -> [!NOTE] +> [!NOTE] > For convenience, the CSRF middleware is automatically disabled when running tests. @@ -480,7 +480,7 @@ expect($response['created'])->toBeTrue(); $this->assertTrue($response['created']); ``` -> [!NOTE] +> [!NOTE] > The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. @@ -1312,7 +1312,7 @@ Assert that the response has a moved permanently (301) HTTP status code: Assert that the response has the given URI value in the `Location` header: $response->assertLocation($uri); - + #### assertContent diff --git a/installation.md b/installation.md index 43c643a3038..227e2ee1c99 100644 --- a/installation.md +++ b/installation.md @@ -82,7 +82,7 @@ php artisan serve Once you have started the Artisan development server, your application will be accessible in your web browser at [http://localhost:8000](http://localhost:8000). Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). -> [!NOTE] +> [!NOTE] > If you would like a head start when developing your Laravel application, 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. @@ -99,7 +99,7 @@ Since many of Laravel's configuration option values may vary depending on whethe Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would be exposed. -> [!NOTE] +> [!NOTE] > For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). @@ -126,7 +126,7 @@ If you choose to use a database other than SQLite, you will need to create the d php artisan migrate ``` -> [!NOTE] +> [!NOTE] > If you are developing on macOS or Windows and need to install MySQL, PostgreSQL, or Redis locally, consider using [Herd Pro](https://herd.laravel.com/#plans). @@ -141,7 +141,7 @@ Laravel should always be served out of the root of the "web directory" configure Once you install Herd, you're ready to start developing with Laravel. Herd includes command line tools for `php`, `composer`, `laravel`, `expose`, `node`, `npm`, and `nvm`. -> [!NOTE] +> [!NOTE] > [Herd Pro](https://herd.laravel.com/#plans) augments Herd with additional powerful features, such as the ability to create and manage local MySQL, Postgres, and Redis databases, as well as local mail viewing and log monitoring. diff --git a/localization.md b/localization.md index ea12a85f5f1..c5e0313db6b 100644 --- a/localization.md +++ b/localization.md @@ -91,9 +91,9 @@ You may instruct Laravel's "pluralizer", which is used by Eloquent and other por */ public function boot(): void { - Pluralizer::useLanguage('spanish'); + Pluralizer::useLanguage('spanish'); - // ... + // ... } > [!WARNING] diff --git a/logging.md b/logging.md index 01a203e8690..a289763ff95 100644 --- a/logging.md +++ b/logging.md @@ -295,7 +295,7 @@ If you would like to share contextual information across _all_ logging channels, } } -> [!NOTE] +> [!NOTE] > If you need to share log context while processing queued jobs, you may utilize [job middleware](/docs/{{version}}/queues#job-middleware). diff --git a/migrations.md b/migrations.md index 7f8dcb89817..1b9b47e2f87 100644 --- a/migrations.md +++ b/migrations.md @@ -627,7 +627,7 @@ The `integer` method creates an `INTEGER` equivalent column: The `ipAddress` method creates a `VARCHAR` equivalent column: $table->ipAddress('visitor'); - + When using PostgreSQL, an `INET` column will be created. diff --git a/mocking.md b/mocking.md index 550da118b19..186e29f6bdf 100644 --- a/mocking.md +++ b/mocking.md @@ -240,7 +240,7 @@ You may also provide a closure to the various time travel methods. The closure w $this->travel(5)->days(function () { // Test something five days into the future... }); - + $this->travelTo(now()->subDays(10), function () { // Test something during a given moment... }); diff --git a/notifications.md b/notifications.md index c195e8b6827..995d404cb2f 100644 --- a/notifications.md +++ b/notifications.md @@ -1267,7 +1267,7 @@ To direct Slack notifications to the appropriate Slack team and channel, define - A `SlackRoute` instance, which allows you to specify an OAuth token and channel name, e.g. `SlackRoute::make($this->slack_channel, $this->slack_token)`. This method should be used to send notifications to external workspaces. For instance, returning `#support-channel` from the `routeNotificationForSlack` method will send the notification to the `#support-channel` channel in the workspace associated with the Bot User OAuth token located in your application's `services.php` configuration file: - + diff --git a/pennant.md b/pennant.md index 8ed326d05ad..2b7682c75f3 100644 --- a/pennant.md +++ b/pennant.md @@ -143,7 +143,8 @@ class NewApi } ``` -> [!NOTE] Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. +> [!NOTE] +> Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. #### Customizing the Stored Feature Name @@ -679,7 +680,8 @@ Pennant's included Blade directive also makes it easy to conditionally render co @endfeature ``` -> [!NOTE] When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. +> [!NOTE] +> When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. When calling the [conditional `when`](#conditional-execution) method, the feature's rich value will be provided to the first closure: @@ -847,7 +849,8 @@ Alternatively, you may deactivate the feature for all users: Feature::deactivateForEveryone('new-api'); ``` -> [!NOTE] This will only update the resolved feature values that have been stored by Pennant's storage driver. You will also need to update the feature definition in your application. +> [!NOTE] +> This will only update the resolved feature values that have been stored by Pennant's storage driver. You will also need to update the feature definition in your application. ### Purging Features diff --git a/precognition.md b/precognition.md index 600c96ae9b2..158409a74c2 100644 --- a/precognition.md +++ b/precognition.md @@ -314,7 +314,7 @@ If you are validating a subset of a form's inputs with Precognition, it can be u + onChange={(e) => form.setData('avatar', e.target.value); form.forgetError('avatar'); @@ -526,7 +526,7 @@ In the user creation example discussed above, we are using Precognition to perfo Alternatively, if you would like to submit the form via XHR you may use the form's `submit` function, which returns an Axios request promise: ```html - @@ -190,7 +190,7 @@ If you wish to view all usage metrics on screen at the same time, you may includ To learn how to customize how Pulse retrieves and displays user information, consult our documentation on [resolving users](#dashboard-resolving-users). -> [!NOTE] +> [!NOTE] > If your application receives a lot of requests or dispatches a lot of jobs, you may wish to enable [sampling](#sampling). See the [user requests recorder](#user-requests-recorder), [user jobs recorder](#user-jobs-recorder), and [slow jobs recorder](#slow-jobs-recorder) documentation for more information. @@ -251,7 +251,7 @@ Most Pulse recorders will automatically capture entries based on framework event php artisan pulse:check ``` -> [!NOTE] +> [!NOTE] > To keep the `pulse:check` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the command does not stop running. As the `pulse:check` command is a long-lived process, it will not see changes to your codebase without being restarted. You should gracefully restart the command by calling the `pulse:restart` command during your application's deployment process: @@ -471,7 +471,7 @@ PULSE_DB_CONNECTION=pulse ### Redis Ingest -> [!WARNING] +> [!WARNING] > The Redis Ingest requires Redis 6.2 or greater and `phpredis` or `predis` as the application's configured Redis client driver. By default, Pulse will store entries directly to the [configured database connection](#using-a-different-database) after the HTTP response has been sent to the client or a job has been processed; however, you may use Pulse's Redis ingest driver to send entries to a Redis stream instead. This can be enabled by configuring the `PULSE_INGEST_DRIVER` environment variable: @@ -492,7 +492,7 @@ When using the Redis ingest, you will need to run the `pulse:work` command to mo php artisan pulse:work ``` -> [!NOTE] +> [!NOTE] > To keep the `pulse:work` process running permanently in the background, you should use a process monitor such as Supervisor to ensure that the Pulse worker does not stop running. As the `pulse:work` command is a long-lived process, it will not see changes to your codebase without being restarted. You should gracefully restart the command by calling the `pulse:restart` command during your application's deployment process: @@ -593,7 +593,7 @@ Once you have defined your Livewire component and template, the card may be incl ``` -> [!NOTE] +> [!NOTE] > If your card is included in a package, you will need to register the component with Livewire using the `Livewire::component` method. @@ -707,7 +707,7 @@ The available aggregation methods are: * `min` * `sum` -> [!NOTE] +> [!NOTE] > When building a card package that captures the currently authenticated user ID, you should use the `Pulse::resolveAuthenticatedUserId()` method, which respects any [user resolver customizations](#dashboard-resolving-users) made to the application. diff --git a/routing.md b/routing.md index 57169b48c1c..12e4d050166 100644 --- a/routing.md +++ b/routing.md @@ -323,7 +323,7 @@ For convenience, some commonly used regular expression patterns have helper meth Route::get('/category/{category}', function (string $category) { // ... })->whereIn('category', ['movie', 'song', 'painting']); - + Route::get('/category/{category}', function (string $category) { // ... })->whereIn('category', CategoryEnum::cases()); diff --git a/scout.md b/scout.md index 2a2c28246bb..ba64255c8aa 100644 --- a/scout.md +++ b/scout.md @@ -177,7 +177,7 @@ public function toSearchableArray() } ``` -You should also define your Typesense collection schemas in your application's `config/scout.php` file. A collection schema describes the data types of each field that is searchable via Typesense. For more information on all available schema options, please consult the [Typesense documentation](https://typesense.org/docs/latest/api/collections.html#schema-parameters). +You should also define your Typesense collection schemas in your application's `config/scout.php` file. A collection schema describes the data types of each field that is searchable via Typesense. For more information on all available schema options, please consult the [Typesense documentation](https://typesense.org/docs/latest/api/collections.html#schema-parameters). If you need to change your Typesense collection's schema after it has been defined, you may either run `scout:flush` and `scout:import`, which will delete all existing indexed data and recreate the schema. Or, you may use Typesense's API to modify the collection's schema without removing any indexed data. diff --git a/seeding.md b/seeding.md index 4dd70fab4f2..400bbcb308d 100644 --- a/seeding.md +++ b/seeding.md @@ -135,7 +135,7 @@ You may also seed your database using the `migrate:fresh` command in combination ```shell php artisan migrate:fresh --seed -php artisan migrate:fresh --seed --seeder=UserSeeder +php artisan migrate:fresh --seed --seeder=UserSeeder ``` diff --git a/strings.md b/strings.md index ff3566a28ce..4be9d5b2ea9 100644 --- a/strings.md +++ b/strings.md @@ -545,12 +545,12 @@ The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline H By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: use Illuminate\Support\Str; - + Str::inlineMarkdown('Inject: ', [ 'html_input' => 'strip', 'allow_unsafe_links' => false, ]); - + // Inject: alert("Hello XSS!"); @@ -1326,7 +1326,7 @@ The `Str::ulid` method generates a ULID, which is a compact, time-ordered unique use Illuminate\Support\Str; return (string) Str::ulid(); - + // 01gd6r360bp37zj17nxb55yv40 If you would like to retrieve a `Illuminate\Support\Carbon` date instance representing the date and time that a given ULID was created, you may use the `createFromId` method provided by Laravel's Carbon integration: diff --git a/testing.md b/testing.md index 15a918a735d..84c7e216418 100644 --- a/testing.md +++ b/testing.md @@ -46,7 +46,7 @@ If you would like to create a test within the `tests/Unit` directory, you may us php artisan make:test UserTest --unit ``` -> [!NOTE] +> [!NOTE] > Test stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). Once the test has been generated, you may define test as you normally would using Pest or PHPUnit. To run your tests, execute the `vendor/bin/pest`, `vendor/bin/phpunit`, or `php artisan test` command from your terminal: @@ -78,7 +78,7 @@ class ExampleTest extends TestCase } ``` -> [!WARNING] +> [!WARNING] > If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. Typically, you should invoke `parent::setUp()` at the start of your own `setUp` method, and `parent::tearDown()` at the end of your `tearDown` method. diff --git a/upgrade.md b/upgrade.md index c8eea49b09b..3aaaa06cf1a 100644 --- a/upgrade.md +++ b/upgrade.md @@ -48,7 +48,7 @@ #### Estimated Upgrade Time: 15 Minutes -> [!NOTE] +> [!NOTE] > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. diff --git a/vite.md b/vite.md index 0b8d56c4489..559549289b5 100644 --- a/vite.md +++ b/vite.md @@ -866,7 +866,7 @@ For example, the `vite-imagetools` plugin outputs URLs like the following while ``` -The `vite-imagetools` plugin is expecting that the output URL will be intercepted by Vite and the plugin may then handle all URLs that start with `/@imagetools`. If you are using plugins that are expecting this behaviour, you will need to manually correct the URLs. You can do this in your `vite.config.js` file by using the `transformOnServe` option. +The `vite-imagetools` plugin is expecting that the output URL will be intercepted by Vite and the plugin may then handle all URLs that start with `/@imagetools`. If you are using plugins that are expecting this behaviour, you will need to manually correct the URLs. You can do this in your `vite.config.js` file by using the `transformOnServe` option. In this particular example, we will prepend the dev server URL to all occurrences of `/@imagetools` within the generated code: From 46ad3e9ec7fd3c083dd3a997d158f89e1254e81c Mon Sep 17 00:00:00 2001 From: Yusuf Bouzekri Date: Mon, 22 Jul 2024 15:21:16 +0100 Subject: [PATCH 1743/2609] Update strings.md (#9782) --- strings.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/strings.md b/strings.md index 4be9d5b2ea9..462b7aeec2d 100644 --- a/strings.md +++ b/strings.md @@ -1655,17 +1655,17 @@ The `chopEnd` method removes the last occurrence of the given value only if the use Illuminate\Support\Str; - $url = Str::of('https://laravel.com/')->chopEnd('https://'); + $url = Str::of('https://laravel.com/')->chopEnd('.com'); - // 'laravel.com' + // 'https://laravel/' You may also pass an array. If the string ends with any of the values in the array then that value will be removed from string: use Illuminate\Support\Str; - $url = Str::of('http://laravel.com/')->chopEnd(['https://', 'http://']); + $url = Str::of('http://laravel.com/')->chopEnd(['.com', '.io']); - // 'laravel.com' + // 'http://laravel/' #### `contains` {.collection-method} From fa659e6bd8f4a82fb0dd7e611886f084efcd61a2 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 23 Jul 2024 17:37:05 +0330 Subject: [PATCH 1744/2609] extend SQLite support (#9768) --- database.md | 2 +- upgrade.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/database.md b/database.md index ffa38b9d1b7..2f1e189a40a 100644 --- a/database.md +++ b/database.md @@ -22,7 +22,7 @@ Almost every modern web application interacts with a database. Laravel makes int - MariaDB 10.3+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) - MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) - PostgreSQL 10.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) -- SQLite 3.35.0+ +- SQLite 3.26.0+ - SQL Server 2017+ ([Version Policy](https://docs.microsoft.com/en-us/lifecycle/products/?products=sql-server)) diff --git a/upgrade.md b/upgrade.md index 3aaaa06cf1a..d76be13755d 100644 --- a/upgrade.md +++ b/upgrade.md @@ -205,11 +205,11 @@ public function dump(...$args); ### Database -#### SQLite 3.35.0+ +#### SQLite 3.26.0+ **Likelihood Of Impact: High** -If your application is utilizing an SQLite database, SQLite 3.35.0 or greater is required. +If your application is utilizing an SQLite database, SQLite 3.26.0 or greater is required. #### Eloquent Model `casts` Method From 2e3c39607018927090fd2cdcf62980ae332f4039 Mon Sep 17 00:00:00 2001 From: Volodya Kurshudyan <70023120+xurshudyan@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:33:39 +0400 Subject: [PATCH 1745/2609] rename to for clarity (#9783) Co-authored-by: Xurshudyan --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 664ba79390c..c73f5079073 100644 --- a/validation.md +++ b/validation.md @@ -1934,7 +1934,7 @@ Alternatively, you may use the `exclude_unless` rule to not validate a given fie 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, [ + $validator = Validator::make($data, [ 'email' => 'sometimes|required|email', ]); From dbd430b52d06a095691c1d37a43cedb6480bcb41 Mon Sep 17 00:00:00 2001 From: Einar Hansen <49709354+einar-hansen@users.noreply.github.com> Date: Tue, 23 Jul 2024 23:05:32 +0200 Subject: [PATCH 1746/2609] Update docs with information about the whereLike query (#9765) * Update docs with information about the whereLike query * Update queries.md * Update queries.md --------- Co-authored-by: Taylor Otwell --- queries.md | 69 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/queries.md b/queries.md index 334d2ed1283..27eaea884bf 100644 --- a/queries.md +++ b/queries.md @@ -583,35 +583,42 @@ You may use `whereJsonLength` method to query JSON arrays by their length: ### Additional Where Clauses -**whereBetween / orWhereBetween** +**whereLike / orWhereLike / whereNotLike / orWhereNotLike** -The `whereBetween` method verifies that a column's value is between two values: +The `whereLike` method allows you to add "LIKE" clauses to your query for pattern matching. These methods provide a database-agnostic way of performing string matching queries, with the ability to toggle case-sensitivity. By default, string matching is case-insensitive: $users = DB::table('users') - ->whereBetween('votes', [1, 100]) + ->whereLike('name', '%John%') ->get(); -**whereNotBetween / orWhereNotBetween** +You can enable a case-sensitive search via the `caseSensitive` argument: -The `whereNotBetween` method verifies that a column's value lies outside of two values: + $users = DB::table('users') + ->whereLike('name', '%John%', caseSensitive: true) + ->get(); + +The `orWhereLike` method allows you to add an "or" clause with a LIKE condition: $users = DB::table('users') - ->whereNotBetween('votes', [1, 100]) - ->get(); + ->where('votes', '>', 100) + ->orWhereLike('name', '%John%') + ->get(); -**whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns** +The `whereNotLike` method allows you to add "NOT LIKE" clauses to your query: -The `whereBetweenColumns` method verifies that a column's value is between the two values of two columns in the same table row: + $users = DB::table('users') + ->whereNotLike('name', '%John%') + ->get(); - $patients = DB::table('patients') - ->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) - ->get(); +Similarly, you can use `orWhereNotLike` to add an "or" clause with a NOT LIKE condition: -The `whereNotBetweenColumns` method verifies that a column's value lies outside the two values of two columns in the same table row: + $users = DB::table('users') + ->where('votes', '>', 100) + ->orWhereNotLike('name', '%John%') + ->get(); - $patients = DB::table('patients') - ->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) - ->get(); +> [!WARNING] +> The `whereLike` case-sensitive search option is currently not supported on SQL Server. **whereIn / whereNotIn / orWhereIn / orWhereNotIn** @@ -648,6 +655,36 @@ select * from comments where user_id in ( > [!WARNING] > 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. +**whereBetween / orWhereBetween** + +The `whereBetween` method verifies that a column's value is between two values: + + $users = DB::table('users') + ->whereBetween('votes', [1, 100]) + ->get(); + +**whereNotBetween / orWhereNotBetween** + +The `whereNotBetween` method verifies that a column's value lies outside of two values: + + $users = DB::table('users') + ->whereNotBetween('votes', [1, 100]) + ->get(); + +**whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns** + +The `whereBetweenColumns` method verifies that a column's value is between the two values of two columns in the same table row: + + $patients = DB::table('patients') + ->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); + +The `whereNotBetweenColumns` method verifies that a column's value lies outside the two values of two columns in the same table row: + + $patients = DB::table('patients') + ->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); + **whereNull / whereNotNull / orWhereNull / orWhereNotNull** The `whereNull` method verifies that the value of the given column is `NULL`: From c3bd12850534ea3a92e70bf9f3a68cc093a0a704 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Jul 2024 16:07:45 -0500 Subject: [PATCH 1747/2609] wip --- pennant.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pennant.md b/pennant.md index 2b7682c75f3..71da2a38e88 100644 --- a/pennant.md +++ b/pennant.md @@ -799,6 +799,12 @@ Feature::for($users)->loadMissing([ ]); ``` +You may load all defined features using the `loadAll` method: + +```php +Feature::for($user)->loadAll(); +``` + ## Updating Values From f08454501ad9e80affb6591190e183c541e1fed6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Jul 2024 16:09:24 -0500 Subject: [PATCH 1748/2609] wip --- http-tests.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/http-tests.md b/http-tests.md index 73f6faeafef..bf2cc4f0a93 100644 --- a/http-tests.md +++ b/http-tests.md @@ -426,6 +426,15 @@ $this->assertThrows( ); ``` +If you would like to inspect and make assertions against the exception that is thrown, you may provide a closure as the second argument to the `assertThrows` method: + +```php +$this->assertThrows( + fn () => (new ProcessOrder)->execute(), + fn (OrderInvalid $e) => $e->orderId() === 123; +); +``` + ## Testing JSON APIs From ff7f8ac6f249f521a4e4702351254172e722c215 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Jul 2024 16:13:31 -0500 Subject: [PATCH 1749/2609] wip --- strings.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/strings.md b/strings.md index 462b7aeec2d..d6b8e715a13 100644 --- a/strings.md +++ b/strings.md @@ -695,9 +695,13 @@ The `Str::limit` method truncates the given string to the specified length: // The quick brown fox... -You may pass a third argument to the method to change the string that will be appended to the end of the truncated string: +If you would like to preserve complete words when truncating the string, you may utilize the `preserveWords` argument. When this argument is `true`, the string will be truncated to the nearest complete word boundary: - use Illuminate\Support\Str; + $truncated = Str::limit('The quick brown fox', 12, preserveWords: true); + + // The quick... + +You may pass a third argument to the method to change the string that will be appended to the end of the truncated string: $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20, ' (...)'); From f78985d7ffab32b5dbb0dfffbced88f693ec058d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 23 Jul 2024 16:14:39 -0500 Subject: [PATCH 1750/2609] wip --- strings.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/strings.md b/strings.md index d6b8e715a13..3ba90b62e65 100644 --- a/strings.md +++ b/strings.md @@ -695,18 +695,18 @@ The `Str::limit` method truncates the given string to the specified length: // The quick brown fox... -If you would like to preserve complete words when truncating the string, you may utilize the `preserveWords` argument. When this argument is `true`, the string will be truncated to the nearest complete word boundary: - - $truncated = Str::limit('The quick brown fox', 12, preserveWords: true); - - // The quick... - You may pass a third argument to the method to change the string that will be appended to the end of the truncated string: $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20, ' (...)'); // The quick brown fox (...) +If you would like to preserve complete words when truncating the string, you may utilize the `preserveWords` argument. When this argument is `true`, the string will be truncated to the nearest complete word boundary: + + $truncated = Str::limit('The quick brown fox', 12, preserveWords: true); + + // The quick... + #### `Str::lower()` {.collection-method} @@ -2021,12 +2021,16 @@ The `limit` method truncates the given string to the specified length: You may also pass a second argument to change the string that will be appended to the end of the truncated string: - use Illuminate\Support\Str; - $truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20, ' (...)'); // The quick brown fox (...) +If you would like to preserve complete words when truncating the string, you may utilize the `preserveWords` argument. When this argument is `true`, the string will be truncated to the nearest complete word boundary: + + $truncated = Str::of('The quick brown fox')->limit(12, preserveWords: true); + + // The quick... + #### `lower` {.collection-method} From 9c9e90ae3c73d0a3becd33a10a3086b663a8ec77 Mon Sep 17 00:00:00 2001 From: Sergey Pashkevich Date: Wed, 24 Jul 2024 18:49:49 +0300 Subject: [PATCH 1751/2609] Update queries.md (#9784) All documentation uses lowercase for `like` operator: https://laravel.com/docs/11.x/queries https://laravel.com/docs/11.x/eloquent-relationships --- queries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queries.md b/queries.md index 27eaea884bf..402cb651a8d 100644 --- a/queries.md +++ b/queries.md @@ -513,7 +513,7 @@ Sometimes you may need to apply the same query constraints to multiple columns. 'name', 'email', 'phone', - ], 'LIKE', 'Example%') + ], 'like', 'Example%') ->get(); The query above will result in the following SQL: @@ -535,7 +535,7 @@ Similarly, the `whereAll` method may be used to retrieve records where all of th ->whereAll([ 'title', 'content', - ], 'LIKE', '%Laravel%') + ], 'like', '%Laravel%') ->get(); The query above will result in the following SQL: From 0672b9354731561a93107f3ece894804fbd9dcc1 Mon Sep 17 00:00:00 2001 From: Sergey Pashkevich Date: Wed, 24 Jul 2024 18:50:50 +0300 Subject: [PATCH 1752/2609] Update database.md (#9785) * Update database.md * Update database.md --------- Co-authored-by: Taylor Otwell --- database.md | 1 + 1 file changed, 1 insertion(+) diff --git a/database.md b/database.md index 2f1e189a40a..1e2cf203759 100644 --- a/database.md +++ b/database.md @@ -291,6 +291,7 @@ If you would like to specify a closure that is invoked for each SQL query execut // $query->sql; // $query->bindings; // $query->time; + // $query->toRawSql(); }); } } From 7e041c42a2518b7c55bcbed429bee933b5574ce1 Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:34:07 +0200 Subject: [PATCH 1753/2609] Document process count configuration for Horizon (#9790) * Document process count configuration for Horizon * Update horizon.md --------- Co-authored-by: Taylor Otwell --- horizon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/horizon.md b/horizon.md index 3e34b47cf28..08319cd5c74 100644 --- a/horizon.md +++ b/horizon.md @@ -126,7 +126,7 @@ Unlike Laravel's default queue system, Horizon allows you to choose from three w The `auto` strategy, which is the configuration file's default, adjusts the number of worker processes per queue based on the current workload of the queue. For example, if your `notifications` queue has 1,000 pending jobs while your `render` queue is empty, Horizon will allocate more workers to your `notifications` queue until the queue is empty. -When using the `auto` strategy, you may define the `minProcesses` and `maxProcesses` configuration options to control the minimum and the maximum number of worker processes Horizon should scale up and down to: +When using the `auto` strategy, you may define the `minProcesses` and `maxProcesses` configuration options to control the minimum number of processes per queue and the maximum number of worker processes in total Horizon should scale up and down to: 'environments' => [ 'production' => [ From b0ac2130ee3ba124f6f7141e63584599946a45c2 Mon Sep 17 00:00:00 2001 From: James <16080646+James-buzz@users.noreply.github.com> Date: Fri, 26 Jul 2024 06:35:15 +0100 Subject: [PATCH 1754/2609] Fix typo on queues.md (#9788) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 617156d21f7..af2aa0b86c1 100644 --- a/queues.md +++ b/queues.md @@ -1583,7 +1583,7 @@ Then, set the `queue.batching.driver` configuration option's value to `dynamodb` ```php 'batching' => [ - 'driver' => env('QUEUE_FAILED_DRIVER', 'dynamodb'), + 'driver' => env('QUEUE_BATCHING_DRIVER', 'dynamodb'), 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), From e1f9a340efffc94646db3d4b70925bbbb8458f12 Mon Sep 17 00:00:00 2001 From: Bart Stavenuiter Date: Fri, 26 Jul 2024 07:43:52 +0200 Subject: [PATCH 1755/2609] change default collation to match default config in framework (#9787) --- database.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.md b/database.md index 1e2cf203759..0390f489696 100644 --- a/database.md +++ b/database.md @@ -103,7 +103,7 @@ To see how read / write connections should be configured, let's look at this exa 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => env('DB_CHARSET', 'utf8mb4'), - 'collation' => env('DB_COLLATION', 'utf8mb4_0900_ai_ci'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 'prefix' => '', 'prefix_indexes' => true, 'strict' => true, From 973948223359c5c918a939a71ce00531b75ce761 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:54:05 +0700 Subject: [PATCH 1756/2609] Update prompts.md (#9793) --- prompts.md | 100 ++++++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/prompts.md b/prompts.md index fb7d19eab18..dabba4d6bea 100644 --- a/prompts.md +++ b/prompts.md @@ -113,7 +113,7 @@ Alternatively, you may leverage the power of Laravel's [validator](/docs/{{versi ```php $name = text( label: 'What is your name?', - validate: ['name' => 'required|max:255|unique:users,name'] + validate: ['name' => 'required|max:255|unique:users'] ); ``` @@ -307,8 +307,8 @@ If you need the user to select from a predefined set of choices, you may use the use function Laravel\Prompts\select; $role = select( - 'What role should the user have?', - ['Member', 'Contributor', 'Owner'], + label: 'What role should the user have?', + options: ['Member', 'Contributor', 'Owner'] ); ``` @@ -331,7 +331,7 @@ $role = select( options: [ 'member' => 'Member', 'contributor' => 'Contributor', - 'owner' => 'Owner' + 'owner' => 'Owner', ], default: 'owner' ); @@ -348,7 +348,7 @@ $role = select( ``` -#### Validation +#### Additional Validation Unlike other prompt functions, the `select` function doesn't accept the `required` argument because it is not possible to select nothing. However, you may pass a closure to the `validate` argument if you need to present an option but prevent it from being selected: @@ -358,7 +358,7 @@ $role = select( options: [ 'member' => 'Member', 'contributor' => 'Contributor', - 'owner' => 'Owner' + 'owner' => 'Owner', ], validate: fn (string $value) => $value === 'owner' && User::where('role', 'owner')->exists() @@ -378,8 +378,8 @@ If you need the user to be able to select multiple options, you may use the `mul use function Laravel\Prompts\multiselect; $permissions = multiselect( - 'What permissions should be assigned?', - ['Read', 'Create', 'Update', 'Delete'] + label: 'What permissions should be assigned?', + options: ['Read', 'Create', 'Update', 'Delete'] ); ``` @@ -398,14 +398,14 @@ $permissions = multiselect( You may also pass an associative array to the `options` argument to return the selected options' keys instead of their values: -``` +```php $permissions = multiselect( label: 'What permissions should be assigned?', options: [ 'read' => 'Read', 'create' => 'Create', 'update' => 'Update', - 'delete' => 'Delete' + 'delete' => 'Delete', ], default: ['read', 'create'] ); @@ -423,11 +423,13 @@ $categories = multiselect( You may allow the user to easily select all options via the `canSelectAll` argument: +```php $categories = multiselect( label: 'What categories should be assigned?', options: Category::pluck('name', 'id'), canSelectAll: true ); +``` #### Requiring a Value @@ -438,7 +440,7 @@ By default, the user may select zero or more options. You may pass the `required $categories = multiselect( label: 'What categories should be assigned?', options: Category::pluck('name', 'id'), - required: true, + required: true ); ``` @@ -448,23 +450,23 @@ If you would like to customize the validation message, you may provide a string $categories = multiselect( label: 'What categories should be assigned?', options: Category::pluck('name', 'id'), - required: 'You must select at least one category', + required: 'You must select at least one category' ); ``` -#### Validation +#### Additional Validation You may pass a closure to the `validate` argument if you need to present an option but prevent it from being selected: -``` +```php $permissions = multiselect( label: 'What permissions should the user have?', options: [ 'read' => 'Read', 'create' => 'Create', 'update' => 'Update', - 'delete' => 'Delete' + 'delete' => 'Delete', ], validate: fn (array $values) => ! in_array('read', $values) ? 'All users require the read permission.' @@ -489,8 +491,8 @@ Alternatively, you may pass a closure as the second argument to the `suggest` fu ```php $name = suggest( - 'What is your name?', - fn ($value) => collect(['Taylor', 'Dayle']) + label: 'What is your name?', + options: fn ($value) => collect(['Taylor', 'Dayle']) ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true)) ) ``` @@ -568,9 +570,9 @@ If you have a lot of options for the user to select from, the `search` function use function Laravel\Prompts\search; $id = search( - 'Search for the user that should receive the mail', - fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + label: 'Search for the user that should receive the mail', + options: fn (string $value) => strlen($value) > 0 + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [] ); ``` @@ -584,7 +586,7 @@ $id = search( label: 'Search for the user that should receive the mail', placeholder: 'E.g. Taylor Otwell', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], hint: 'The user will receive an email immediately.' ); @@ -596,14 +598,14 @@ Up to five options will be displayed before the list begins to scroll. You may c $id = search( label: 'Search for the user that should receive the mail', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], scroll: 10 ); ``` -#### Validation +#### Additional Validation If you would like to perform additional validation logic, you may pass a closure to the `validate` argument: @@ -611,7 +613,7 @@ If you would like to perform additional validation logic, you may pass a closure $id = search( label: 'Search for the user that should receive the mail', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], validate: function (int|string $value) { $user = User::findOrFail($value); @@ -636,7 +638,7 @@ use function Laravel\Prompts\multisearch; $ids = multisearch( 'Search for the users that should receive the mail', fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [] ); ``` @@ -650,7 +652,7 @@ $ids = multisearch( label: 'Search for the users that should receive the mail', placeholder: 'E.g. Taylor Otwell', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], hint: 'The user will receive an email immediately.' ); @@ -662,7 +664,7 @@ Up to five options will be displayed before the list begins to scroll. You may c $ids = multisearch( label: 'Search for the users that should receive the mail', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], scroll: 10 ); @@ -675,11 +677,11 @@ By default, the user may select zero or more options. You may pass the `required ```php $ids = multisearch( - 'Search for the users that should receive the mail', - fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + label: 'Search for the users that should receive the mail', + options: fn (string $value) => strlen($value) > 0 + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], - required: true, + required: true ); ``` @@ -687,16 +689,16 @@ If you would like to customize the validation message, you may also provide a st ```php $ids = multisearch( - 'Search for the users that should receive the mail', - fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + label: 'Search for the users that should receive the mail', + options: fn (string $value) => strlen($value) > 0 + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], required: 'You must select at least one user.' ); ``` -#### Validation +#### Additional Validation If you would like to perform additional validation logic, you may pass a closure to the `validate` argument: @@ -704,10 +706,10 @@ If you would like to perform additional validation logic, you may pass a closure $ids = multisearch( label: 'Search for the users that should receive the mail', options: fn (string $value) => strlen($value) > 0 - ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all() : [], validate: function (array $values) { - $optedOut = User::where('name', 'like', '%a%')->findMany($values); + $optedOut = User::whereLike('name', '%a%')->findMany($values); if ($optedOut->isNotEmpty()) { return $optedOut->pluck('name')->join(', ', ', and ').' have opted out.'; @@ -753,16 +755,16 @@ use function Laravel\Prompts\form; $responses = form() ->text('What is your name?', required: true, name: 'name') ->password( - 'What is your password?', + label: 'What is your password?', validate: ['password' => 'min:8'], - name: 'password', + name: 'password' ) ->confirm('Do you accept the terms?') ->submit(); User::create([ 'name' => $responses['name'], - 'password' => $responses['password'] + 'password' => $responses['password'], ]); ``` @@ -804,8 +806,8 @@ The `table` function makes it easy to display multiple rows and columns of data. use function Laravel\Prompts\table; table( - ['Name', 'Email'], - User::all(['name', 'email'])->toArray() + headers: ['Name', 'Email'], + rows: User::all(['name', 'email'])->toArray() ); ``` @@ -818,8 +820,8 @@ The `spin` function displays a spinner along with an optional message while exec use function Laravel\Prompts\spin; $response = spin( - fn () => Http::get('http://example.com/'), - 'Fetching response...' + message: 'Fetching response...', + callback: fn () => Http::get('http://example.com/') ); ``` @@ -837,13 +839,13 @@ use function Laravel\Prompts\progress; $users = progress( label: 'Updating users', steps: User::all(), - callback: fn ($user) => $this->performTask($user), + callback: fn ($user) => $this->performTask($user) ); ``` The `progress` function acts like a map function and will return an array containing the return value of each iteration of your callback. -The callback may also accept the `\Laravel\Prompts\Progress` instance, allowing you to modify the label and hint on each iteration: +The callback may also accept the `Laravel\Prompts\Progress` instance, allowing you to modify the label and hint on each iteration: ```php $users = progress( @@ -856,7 +858,7 @@ $users = progress( return $this->performTask($user); }, - hint: 'This may take some time.', + hint: 'This may take some time.' ); ``` @@ -928,7 +930,9 @@ TextPrompt::fallbackUsing(function (TextPrompt $prompt) use ($input, $output) { $question = (new Question($prompt->label, $prompt->default ?: null)) ->setValidator(function ($answer) use ($prompt) { if ($prompt->required && $answer === null) { - throw new \RuntimeException(is_string($prompt->required) ? $prompt->required : 'Required.'); + throw new \RuntimeException( + is_string($prompt->required) ? $prompt->required : 'Required.' + ); } if ($prompt->validate) { From 1b69438a67473dc3a9c7f351b3fa742460d11054 Mon Sep 17 00:00:00 2001 From: TENIOS <40282681+TENIOS@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:54:18 +0700 Subject: [PATCH 1757/2609] Fix double newline typos (#9795) --- collections.md | 1 - errors.md | 1 - helpers.md | 3 --- horizon.md | 1 - logging.md | 6 ++---- middleware.md | 1 - queues.md | 2 -- releases.md | 1 - scheduling.md | 1 - strings.md | 3 --- validation.md | 16 ++++++++-------- 11 files changed, 10 insertions(+), 26 deletions(-) diff --git a/collections.md b/collections.md index 17641933a6f..e5794876299 100644 --- a/collections.md +++ b/collections.md @@ -3434,7 +3434,6 @@ The `whereNull` method returns items from the collection where the given key is ] */ - #### `wrap()` {.collection-method} diff --git a/errors.md b/errors.md index b87b4f1ef44..3d5fbf1f92f 100644 --- a/errors.md +++ b/errors.md @@ -368,7 +368,6 @@ By default, limits will use the exception's class as the rate limit key. You can }); }) - Of course, you may return a mixture of `Lottery` and `Limit` instances for different exceptions: use App\Exceptions\ApiMonitoringException; diff --git a/helpers.md b/helpers.md index 72724035102..7ba4e6b1a38 100644 --- a/helpers.md +++ b/helpers.md @@ -105,7 +105,6 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct - ### Paths @@ -244,7 +243,6 @@ The `Arr::add` method adds a given key / value pair to an array if the given key // ['name' => 'Desk', 'price' => 100] - #### `Arr::collapse()` {.collection-method} @@ -1364,7 +1362,6 @@ The `Number::spell` method transforms the given number into a string of words: // quatre-vingt-huit - The `after` argument allows you to specify a value after which all numbers should be spelled out: $number = Number::spell(10, after: 10); diff --git a/horizon.md b/horizon.md index 08319cd5c74..5ad26553a37 100644 --- a/horizon.md +++ b/horizon.md @@ -375,7 +375,6 @@ When retrieving the tags for a queued event listener, Horizon will automatically } } - ## Notifications diff --git a/logging.md b/logging.md index a289763ff95..3d33582d290 100644 --- a/logging.md +++ b/logging.md @@ -417,9 +417,8 @@ If you are using a Monolog handler that is capable of providing its own formatte 'formatter' => 'default', ], - - - #### Monolog Processors + +#### Monolog Processors Monolog can also process messages before logging them. You can create your own processors or use the [existing processors offered by Monolog](https://github.com/Seldaek/monolog/tree/main/src/Monolog/Processor). @@ -443,7 +442,6 @@ If you would like to customize the processors for a `monolog` driver, add a `pro ], ], - ### Creating Custom Channels via Factories diff --git a/middleware.md b/middleware.md index b56ca30820a..5268ebd3428 100644 --- a/middleware.md +++ b/middleware.md @@ -140,7 +140,6 @@ If you would like to manage Laravel's global middleware stack manually, you may ]); }) - ### Assigning Middleware to Routes diff --git a/queues.md b/queues.md index af2aa0b86c1..baf03958e00 100644 --- a/queues.md +++ b/queues.md @@ -572,7 +572,6 @@ class ProviderIsDown { // ... - public function middleware(): array { return [ @@ -585,7 +584,6 @@ class ProviderIsUp { // ... - public function middleware(): array { return [ diff --git a/releases.md b/releases.md index 6a3663c05ed..692cbfb16f3 100644 --- a/releases.md +++ b/releases.md @@ -21,7 +21,6 @@ When referencing the Laravel framework or its components from your application o For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest major release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). -
    | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | diff --git a/scheduling.md b/scheduling.md index 3c472d87ffc..5ef90e01c73 100644 --- a/scheduling.md +++ b/scheduling.md @@ -343,7 +343,6 @@ Schedule::call(fn () => User::resetApiRequestCount()) ->onOneServer(); ``` - ### Background Tasks diff --git a/strings.md b/strings.md index 3ba90b62e65..661faf47915 100644 --- a/strings.md +++ b/strings.md @@ -460,7 +460,6 @@ The `Str::endsWith` method determines if the given string ends with the given va // true - You may also pass an array of values to determine if the given string ends with any of the values in the array: use Illuminate\Support\Str; @@ -1895,7 +1894,6 @@ The `isEmpty` method determines if the given string is empty: The `isNotEmpty` method determines if the given string is not empty: - use Illuminate\Support\Str; $result = Str::of(' ')->trim()->isNotEmpty(); @@ -1996,7 +1994,6 @@ The `lcfirst` method returns the given string with the first character lowercase // foo Bar - #### `length` {.collection-method} diff --git a/validation.md b/validation.md index c73f5079073..b77486a7bdf 100644 --- a/validation.md +++ b/validation.md @@ -1544,23 +1544,23 @@ The field under validation must be a multiple of _value_. The field under validation must not be present in the input data. - - #### missing_if:_anotherfield_,_value_,... + +#### missing_if:_anotherfield_,_value_,... The field under validation must not be present if the _anotherfield_ field is equal to any _value_. - - #### missing_unless:_anotherfield_,_value_ + +#### missing_unless:_anotherfield_,_value_ The field under validation must not be present unless the _anotherfield_ field is equal to any _value_. - - #### missing_with:_foo_,_bar_,... + +#### missing_with:_foo_,_bar_,... The field under validation must not be present _only if_ any of the other specified fields are present. - - #### missing_with_all:_foo_,_bar_,... + +#### missing_with_all:_foo_,_bar_,... The field under validation must not be present _only if_ all of the other specified fields are present. From 75690b5d23e2b7a30f31ea3ec244ca3f53829c5f Mon Sep 17 00:00:00 2001 From: Joel Bladt Date: Mon, 29 Jul 2024 08:54:52 +0200 Subject: [PATCH 1758/2609] Update dusk.md (#9792) --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index dc4d1d7d9b1..36d74238380 100644 --- a/dusk.md +++ b/dusk.md @@ -2140,7 +2140,7 @@ To run your Dusk tests on [Travis CI](https://travis-ci.org), use the following language: php php: - - 7.3 + - 8.2 addons: chrome: stable From 524f3893f21821a5334d048c0eaadd66ffa12537 Mon Sep 17 00:00:00 2001 From: Robin Straub Date: Mon, 29 Jul 2024 09:20:52 +0200 Subject: [PATCH 1759/2609] doc: bump actions/upload-artifact github action to v4 (#9791) Co-authored-by: Robin Straub --- dusk.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dusk.md b/dusk.md index 36d74238380..f17e268e9c3 100644 --- a/dusk.md +++ b/dusk.md @@ -2198,13 +2198,13 @@ jobs: run: php artisan dusk - name: Upload Screenshots if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: screenshots path: tests/Browser/screenshots - name: Upload Console Logs if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: console path: tests/Browser/console From 6d7a00b539d8281cc8d7037e82b978fec3c00866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20H=2E?= <36476318+Tamas-hi@users.noreply.github.com> Date: Thu, 1 Aug 2024 14:38:36 +0200 Subject: [PATCH 1760/2609] Update sanctum.md (#9804) * Update sanctum.md Vue CLI is in maintenance mode for quite a while now (https://cli.vuejs.org/) so we should refer to the preferred-way as well. (https://github.com/vuejs/create-vue) * Update sanctum.md --------- Co-authored-by: Taylor Otwell --- sanctum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index 8830c7d5b9f..c1fd78ab487 100644 --- a/sanctum.md +++ b/sanctum.md @@ -42,7 +42,7 @@ Laravel Sanctum offers this feature by storing user API tokens in a single datab #### 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 an SPA created using Vue CLI or a Next.js application. +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 an SPA created using Next.js or Nuxt. 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. From 31699722eb75b0ec71d199a8a7d6a8335787cdee Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 1 Aug 2024 16:09:28 +0330 Subject: [PATCH 1761/2609] support SQLite foreign keys (#9803) --- migrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations.md b/migrations.md index 1b9b47e2f87..f86f2cab482 100644 --- a/migrations.md +++ b/migrations.md @@ -1228,7 +1228,7 @@ You may enable or disable foreign key constraints within your migrations by usin }); > [!WARNING] -> SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. In addition, SQLite only supports foreign keys upon creation of the table and [not when tables are altered](https://www.sqlite.org/omitted.html). +> SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. ## Events From 8575d328c680b6d77ec194894f2a57db9c7e7a01 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:11:52 +0900 Subject: [PATCH 1762/2609] Remove the `assertArraySubset' part (#9802) --- http-tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-tests.md b/http-tests.md index bf2cc4f0a93..2f1b5d15ce3 100644 --- a/http-tests.md +++ b/http-tests.md @@ -490,7 +490,7 @@ $this->assertTrue($response['created']); ``` > [!NOTE] -> The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. +> The `assertJson` method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. #### Asserting Exact JSON Matches @@ -1122,7 +1122,7 @@ Assert that the response contains the given JSON data: $response->assertJson(array $data, $strict = false); -The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. +The `assertJson` method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. #### assertJsonCount From 0be3a636f7198068f9836474e96f3d13f58c172c Mon Sep 17 00:00:00 2001 From: AJ <60591772+devajmeireles@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:08:05 -0300 Subject: [PATCH 1763/2609] removing semicolon from view example of Pulse page (#9801) --- pulse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pulse.md b/pulse.md index 99a0c72f59e..4020822192b 100644 --- a/pulse.md +++ b/pulse.md @@ -721,7 +721,7 @@ class TopSellers extends Card public function render() { return view('livewire.pulse.top-sellers', [ - 'topSellers' => $this->aggregate('user_sale', ['sum', 'count']); + 'topSellers' => $this->aggregate('user_sale', ['sum', 'count']) ]); } } From 8b5efa9f8eeeff062961b2791179b6bb55666728 Mon Sep 17 00:00:00 2001 From: ChrissBott Date: Thu, 1 Aug 2024 16:08:21 +0200 Subject: [PATCH 1764/2609] Fix grammatical error in middleware.md (#9800) Use "allow" instead of "allows" because the subject "aliases" is plural. --- middleware.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware.md b/middleware.md index 5268ebd3428..3d245a89313 100644 --- a/middleware.md +++ b/middleware.md @@ -301,7 +301,7 @@ If you would like to manually manage all of the middleware within Laravel's defa ### Middleware Aliases -You may assign aliases to middleware in your application's `bootstrap/app.php` file. Middleware aliases allows you to define a short alias for a given middleware class, which can be especially useful for middleware with long class names: +You may assign aliases to middleware in your application's `bootstrap/app.php` file. Middleware aliases allow you to define a short alias for a given middleware class, which can be especially useful for middleware with long class names: use App\Http\Middleware\EnsureUserIsSubscribed; From b874bc07a34f0a9c960f3e1b7ced2370724abcf9 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:14:55 +0330 Subject: [PATCH 1765/2609] [11.x] Add `assertExactJsonStructure` to http-tests (#9799) * add `assertExactJsonStructure` to http-tests * formatting --------- Co-authored-by: Taylor Otwell --- http-tests.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/http-tests.md b/http-tests.md index 2f1b5d15ce3..4bb481f31e8 100644 --- a/http-tests.md +++ b/http-tests.md @@ -922,6 +922,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertDontSeeText](#assert-dont-see-text) [assertDownload](#assert-download) [assertExactJson](#assert-exact-json) +[assertExactJsonStructure](#assert-exact-json-structure) [assertForbidden](#assert-forbidden) [assertFound](#assert-found) [assertGone](#assert-gone) @@ -1073,6 +1074,15 @@ Assert that the response contains an exact match of the given JSON data: $response->assertExactJson(array $data); + +#### assertExactJsonStructure + +Assert that the response contains an exact match of the given JSON structure: + + $response->assertExactJsonStructure(array $data); + +This method is a more strict variant of [assertJsonStructure](#assert-json-structure). In contrast with `assertJsonStructure`, this method will fail if the response contains any keys that aren't explicitly included in the expected JSON structure. + #### assertForbidden From c9dfe787f83a020b09562c1e19bf46e9d9942bc7 Mon Sep 17 00:00:00 2001 From: Einar Hansen <49709354+einar-hansen@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:48:34 +0200 Subject: [PATCH 1766/2609] Add documentation for the `whereNone` method (#9786) --- queries.md | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/queries.md b/queries.md index 402cb651a8d..1d47735cb48 100644 --- a/queries.md +++ b/queries.md @@ -13,7 +13,7 @@ - [Where Clauses](#where-clauses) - [Or Where Clauses](#or-where-clauses) - [Where Not Clauses](#where-not-clauses) - - [Where Any / All Clauses](#where-any-all-clauses) + - [Where Any / All / None Clauses](#where-any-all-none-clauses) - [JSON Where Clauses](#json-where-clauses) - [Additional Where Clauses](#additional-where-clauses) - [Logical Grouping](#logical-grouping) @@ -502,8 +502,8 @@ The `whereNot` and `orWhereNot` methods may be used to negate a given group of q }) ->get(); - -### Where Any / All Clauses + +### Where Any / All / None Clauses Sometimes you may need to apply the same query constraints to multiple columns. For example, you may want to retrieve all records where any columns in a given list are `LIKE` a given value. You may accomplish this using the `whereAny` method: @@ -549,6 +549,29 @@ WHERE published = true AND ( ) ``` +The `whereNone` method may be used to retrieve records where none of the given columns match a given constraint: + + $posts = DB::table('albums') + ->where('published', true) + ->whereNone([ + 'title', + 'lyrics', + 'tags', + ], 'like', '%explicit%') + ->get(); + +The query above will result in the following SQL: + +```sql +SELECT * +FROM albums +WHERE published = true AND NOT ( + title LIKE '%explicit%' OR + lyrics LIKE '%explicit%' OR + tags LIKE '%explicit%' +) +``` + ### JSON Where Clauses From 7c37dcbd8b069397002fcb852dcf2a96ff22abe2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Aug 2024 15:50:33 -0500 Subject: [PATCH 1767/2609] wip --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 7ba4e6b1a38..e9fe2b66f96 100644 --- a/helpers.md +++ b/helpers.md @@ -100,6 +100,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Number::pairs](#method-number-pairs) [Number::percentage](#method-number-percentage) [Number::spell](#method-number-spell) +[Number::trim](#method-number-trim) [Number::useLocale](#method-number-use-locale) [Number::withLocale](#method-number-with-locale) @@ -1382,6 +1383,21 @@ The `until` argument allows you to specify a value before which all numbers shou // 10 + +#### `Number::trim()` {.collection-method} + +The `Number::trim` method removes any trailing zero digits after the decimal point of the given number: + + use Illuminate\Support\Number; + + $number = Number::trim(12.0); + + // 12 + + $number = Number::trim(12.30); + + // 12.3 + #### `Number::useLocale()` {.collection-method} From 19515e2285039cea35cd111047d6540b23641e21 Mon Sep 17 00:00:00 2001 From: Billy <44404782+dev-billy@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:37:38 +0300 Subject: [PATCH 1768/2609] update: heroku CI chrome buildpack (#9807) --- dusk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dusk.md b/dusk.md index f17e268e9c3..382867eba1e 100644 --- a/dusk.md +++ b/dusk.md @@ -2121,7 +2121,7 @@ To run Dusk tests on [Heroku CI](https://www.heroku.com/continuous-integration), "test": { "buildpacks": [ { "url": "heroku/php" }, - { "url": "https://github.com/heroku/heroku-buildpack-google-chrome" } + { "url": "https://github.com/heroku/heroku-buildpack-chrome-for-testing" } ], "scripts": { "test-setup": "cp .env.testing .env", From 696cad17f80605f1cf2ff68c97f60873500c55ca Mon Sep 17 00:00:00 2001 From: Jamie Wood Date: Mon, 5 Aug 2024 14:40:17 +0100 Subject: [PATCH 1769/2609] Correct import statement for Laravel Cashier Paddle (#9806) --- cashier-paddle.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index dc89180111b..0894f29b7b6 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -276,8 +276,8 @@ In this example, the `CompleteOrder` listener might look like the following: namespace App\Listeners; use App\Models\Order; - use Laravel\Cashier\Cashier; - use Laravel\Cashier\Events\TransactionCompleted; + use Laravel\Paddle\Cashier; + use Laravel\Paddle\Events\TransactionCompleted; class CompleteOrder { @@ -669,7 +669,7 @@ These defaults will be used for every action in Cashier that generates a [checko You can retrieve a customer by their Paddle Customer ID using the `Cashier::findBillable` method. This method will return an instance of the billable model: - use Laravel\Cashier\Cashier; + use Laravel\Paddle\Cashier; $user = Cashier::findBillable($customerId); @@ -1372,7 +1372,7 @@ When listing the transactions for a customer, you may use the transaction instan The `download-invoice` route may look like the following: use Illuminate\Http\Request; - use Laravel\Cashier\Transaction; + use Laravel\Paddle\Transaction; Route::get('/download-invoice/{transaction}', function (Request $request, Transaction $transaction) { return $transaction->redirectToInvoicePdf(); From 0d2416ba760b34f142688b853c9b6b7817de12da Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Tue, 6 Aug 2024 14:50:13 +0100 Subject: [PATCH 1770/2609] Expand `forget()` example to include removing multiple keys at once (#9809) * Expand `forget()` example to include removing multiple keys at once * Update collections.md --------- Co-authored-by: Taylor Otwell --- collections.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/collections.md b/collections.md index e5794876299..c8c9198a7dc 100644 --- a/collections.md +++ b/collections.md @@ -1072,12 +1072,16 @@ The `forget` method removes an item from the collection by its key: $collection = collect(['name' => 'taylor', 'framework' => 'laravel']); + // Forget a single key... $collection->forget('name'); - $collection->all(); - // ['framework' => 'laravel'] + // Forget multiple keys... + $collection->forget(['name', 'framework']); + + // [] + > [!WARNING] > Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. From f3ddbdea6c9b79bcf096bee439ce202763d1447a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 6 Aug 2024 09:04:10 -0500 Subject: [PATCH 1771/2609] wip --- queues.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/queues.md b/queues.md index baf03958e00..b54d1a90689 100644 --- a/queues.md +++ b/queues.md @@ -2395,7 +2395,7 @@ In addition, you may occasionally need to test an individual job's interaction w Sometimes, you may need to test that a queued job [releases itself back onto the queue](#manually-releasing-a-job). Or, you may need to test that the job deleted itself. You may test these queue interactions by instantiating the job and invoking the `withFakeQueueInteractions` method. -Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assertReleased`, `assertDeleted`, and `assertFailed` methods may be used to make assertions against the job's queue interactions: +Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assertReleased`, `assertDeleted`, `assertNotDeleted`, `assertFailed`, and `assertNotFailed` methods may be used to make assertions against the job's queue interactions: ```php use App\Jobs\ProcessPodcast; @@ -2406,7 +2406,9 @@ $job->handle(); $job->assertReleased(delay: 30); $job->assertDeleted(); +$job->assertNotDeleted(); $job->assertFailed(); +$job->assertNotFailed(); ``` From 37a8b499fc099ea49dd232d64c6cc4fd3f7ad7f7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 6 Aug 2024 09:08:24 -0500 Subject: [PATCH 1772/2609] wip --- errors.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/errors.md b/errors.md index 3d5fbf1f92f..15c743d1fc6 100644 --- a/errors.md +++ b/errors.md @@ -167,6 +167,22 @@ When building your application, there will be some types of exceptions you never ]); }) +Alternatively, you may simply "mark" an exception class with the `Illuminate\Contracts\Debug\ShouldntReport` interface. When an exception is marked with this interface, it will never be reported by Laravel's exception handler: + +```php + Date: Tue, 6 Aug 2024 09:17:21 -0500 Subject: [PATCH 1773/2609] wip --- context.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/context.md b/context.md index 43b91b00387..a524d47508b 100644 --- a/context.md +++ b/context.md @@ -183,6 +183,29 @@ DB::listen(function ($event) { }); ``` +You may determine if a value is in a stack using the `stackContains` and `hiddenStackContains` methods: + +```php +if (Context::stackContains('breadcrumbs', 'first_value')) { + // +} + +if (Context::hiddenStackContains('secrets', 'first_value')) { + // +} +``` + +The `stackContains` and `hiddenStackContains` methods also accept a closure as their second argument, allowing more control over the value comparison operation: + +```php +use Illuminate\Support\Facades\Context; +use Illuminate\Support\Str; + +return Context::stackContains('breadcrumbs', function ($value) { + return Str::startsWith($value, 'query_'); +}); +``` + ## Retrieving Context From 31e0ff9637c196639731f4aae69eb718f5ed31ed Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 Aug 2024 13:25:42 -0500 Subject: [PATCH 1774/2609] wip --- blade.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/blade.md b/blade.md index 9a99018e9bd..1a414a60322 100644 --- a/blade.md +++ b/blade.md @@ -869,7 +869,7 @@ You may execute this method from your component template by invoking the variabl #### Accessing Attributes and 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. This array will contain several elements that provide information about the component: +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: use Closure; @@ -878,15 +878,24 @@ Blade components also allow you to access the component name, attributes, and sl */ public function render(): Closure { - return function (array $data) { - // $data['componentName']; - // $data['attributes']; - // $data['slot']; - - return '
    Components content
    '; + return function () { + return '
    Components content
    '; }; } +The closure returned by your component's `render` method may also receive a `$data` array as its only argument. This array will contain several elements that provide information about the component: + + return function (array $data) { + // $data['componentName']; + // $data['attributes']; + // $data['slot']; + + return '
    Components content
    '; + } + +> [!WARNING] +> The elements in the `$data` array should never be directly embedded into the Blade string returned by your `render` method, as doing so could allow remote code execution via malicious attribute content. + 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. From e0d1667c25ab949e0a7c60c3e940f22f745a4e17 Mon Sep 17 00:00:00 2001 From: Samuel Date: Thu, 8 Aug 2024 15:31:33 +0200 Subject: [PATCH 1775/2609] Include Str::transliterate method (#9810) * Include Str::transliterate method * Methods in alphabetical order --- strings.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/strings.md b/strings.md index 661faf47915..fc2881bb116 100644 --- a/strings.md +++ b/strings.md @@ -99,6 +99,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::title](#method-title-case) [Str::toBase64](#method-str-to-base64) [Str::toHtmlString](#method-str-to-html-string) +[Str::transliterate](#method-str-transliterate) [Str::trim](#method-str-trim) [Str::ltrim](#method-str-ltrim) [Str::rtrim](#method-str-rtrim) @@ -201,6 +202,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [test](#method-fluent-str-test) [title](#method-fluent-str-title) [toBase64](#method-fluent-str-to-base64) +[transliterate](#method-fluent-str-transliterate) [trim](#method-fluent-str-trim) [ltrim](#method-fluent-str-ltrim) [rtrim](#method-fluent-str-rtrim) @@ -1255,6 +1257,17 @@ The `Str::toHtmlString` method converts the string instance to an instance of `I $htmlString = Str::of('Nuno Maduro')->toHtmlString(); + +#### `Str::transliterate()` {.collection-method} + +The `Str::transliterate` method will attempt to convert a given string into its closest ASCII representation: + + use Illuminate\Support\Str; + + $email = Str::transliterate('ⓣⓔⓢⓣ@ⓛⓐⓡⓐⓥⓔⓛ.ⓒⓞⓜ'); + + // 'test@laravel.com' + #### `Str::trim()` {.collection-method} @@ -2626,6 +2639,17 @@ The `toBase64` method converts the given string to Base64: // TGFyYXZlbA== + +#### `transliterate` {.collection-method} + +The `transliterate` method will attempt to convert a given string into its closest ASCII representation: + + use Illuminate\Support\Str; + + $email = Str::of('ⓣⓔⓢⓣ@ⓛⓐⓡⓐⓥⓔⓛ.ⓒⓞⓜ')->transliterate() + + // 'test@laravel.com' + #### `trim` {.collection-method} From 2eca63c072ec744450de8d45383a3e209205c51d Mon Sep 17 00:00:00 2001 From: "Devon S." <20716219+NintenZone@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:02:20 -0600 Subject: [PATCH 1776/2609] Feat: Add Warning to Clarify optimize:clear Command Behavior (#9817) * Feat: Add Warning to Clarify optimize:clear Command Behavior * Update deployment.md --------- Co-authored-by: Taylor Otwell --- deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index 1cc4a57297f..ccf8ee042b9 100644 --- a/deployment.md +++ b/deployment.md @@ -115,7 +115,7 @@ When deploying your application to production, there are a variety of files that php artisan optimize ``` -The `optimize:clear` method may be used to remove all of the cache files generated by the `optimize` command: +The `optimize:clear` method may be used to remove all of the cache files generated by the `optimize` command as well as all keys in the default cache driver: ```shell php artisan optimize:clear From de9fad85c69806902a3b809731229513142a66f3 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 12 Aug 2024 17:08:54 -0500 Subject: [PATCH 1777/2609] [11.x] add clarification on component slot escaping (#9813) * add clarification on component slot escaping this was a little confusing to me when I first saw it, so wanted to add some clarification. it is likely, especially when using a component for your layout, that the `$slot` variable will contain HTML. however, the docs show the `{{ $slot }}` escaped version. although I'm not exactly sure where this happens in the code, it seems that a slot is treated specially, and does not escape content. All code inside the slot appears to respect any escaped or unescaped content. I've also added a wrapping `
    ` around each task to more closely reflect a real world use, and to further drive home the `$slot` will handle any HTML content it is provided. * Update blade.md --------- Co-authored-by: Taylor Otwell --- blade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade.md b/blade.md index 1a414a60322..d1a15aab66b 100644 --- a/blade.md +++ b/blade.md @@ -1506,7 +1506,7 @@ Once the `layout` component has been defined, we may create a Blade view that ut @foreach ($tasks as $task) - {{ $task }} +
    {{ $task }}
    @endforeach
    ``` @@ -1522,7 +1522,7 @@ Remember, content that is injected into a component will be supplied to the defa @foreach ($tasks as $task) - {{ $task }} +
    {{ $task }}
    @endforeach ``` From 40205ebb0d28371a20c2dc5b0b035f74e3056ae7 Mon Sep 17 00:00:00 2001 From: Brandon Surowiec Date: Mon, 12 Aug 2024 18:09:43 -0400 Subject: [PATCH 1778/2609] Bring Fortify::authenticateThrough() default pipeline definition up to date (#9816) * Update fortify.md Bring Fortify default pipeline definition up to date. * Update fortify.md Add missing Laravel\Fortify\Features import. --- fortify.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fortify.md b/fortify.md index 4e6c83dbbf4..28d5b6e1ff5 100644 --- a/fortify.md +++ b/fortify.md @@ -189,15 +189,18 @@ The example below contains the default pipeline definition that you may use as a ```php use Laravel\Fortify\Actions\AttemptToAuthenticate; +use Laravel\Fortify\Actions\CanonicalizeUsername; use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled; use Laravel\Fortify\Actions\PrepareAuthenticatedSession; use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable; +use Laravel\Fortify\Features; use Laravel\Fortify\Fortify; use Illuminate\Http\Request; Fortify::authenticateThrough(function (Request $request) { return array_filter([ config('fortify.limiters.login') ? null : EnsureLoginIsNotThrottled::class, + config('fortify.lowercase_usernames') ? CanonicalizeUsername::class : null, Features::enabled(Features::twoFactorAuthentication()) ? RedirectIfTwoFactorAuthenticatable::class : null, AttemptToAuthenticate::class, PrepareAuthenticatedSession::class, From c6bcf0c0842a910b2118791a10ab57e624009576 Mon Sep 17 00:00:00 2001 From: LiveSource Date: Wed, 14 Aug 2024 05:18:46 +1200 Subject: [PATCH 1779/2609] Document the ability to forget all horizon failed jobs (#9818) * Document the ability to forget all horizon failed jobs * Update horizon.md --------- Co-authored-by: Taylor Otwell --- horizon.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/horizon.md b/horizon.md index 5ad26553a37..1124a947ebf 100644 --- a/horizon.md +++ b/horizon.md @@ -424,6 +424,12 @@ If you would like to delete a failed job, you may use the `horizon:forget` comma php artisan horizon:forget 5 ``` +If you would like to delete all failed jobs, you may provide the `--all` option to the `horizon:forget` command: + +```shell +php artisan horizon:forget --all +``` + ## Clearing Jobs From Queues From fe1d4567fe5402750c54d3f6277f9b84c8b441a1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 13 Aug 2024 08:16:00 -1000 Subject: [PATCH 1780/2609] update potentially incorrect interface --- events.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/events.md b/events.md index a5e21f2dde1..dc14cabc4f1 100644 --- a/events.md +++ b/events.md @@ -389,17 +389,16 @@ If you need to manually access the listener's underlying queue job's `delete` an When queued listeners are dispatched within database transactions, they may be processed by the queue before the database transaction has committed. When this happens, any updates you have made to models or database records during the database transaction may not yet be reflected in the database. In addition, any models or database records created within the transaction may not exist in the database. If your listener depends on these models, unexpected errors can occur when the job that dispatches the queued listener is processed. -If your queue connection's `after_commit` configuration option is set to `false`, you may still indicate that a particular queued listener should be dispatched after all open database transactions have been committed by implementing the `ShouldHandleEventsAfterCommit` interface on the listener class: +If your queue connection's `after_commit` configuration option is set to `false`, you may still indicate that a particular queued listener should be dispatched after all open database transactions have been committed by implementing the `ShouldQueueAfterCommit` interface on the listener class: Date: Wed, 14 Aug 2024 22:57:29 +0100 Subject: [PATCH 1781/2609] fix: avoid page overflow with long table rows (#9820) --- authentication.md | 4 ++++ cache.md | 4 ++++ configuration.md | 4 ++++ passport.md | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/authentication.md b/authentication.md index ffad7e5445a..aea40410552 100644 --- a/authentication.md +++ b/authentication.md @@ -741,6 +741,8 @@ Once the configuration file has been published, you may set the `rehash_on_login Laravel dispatches a variety of [events](/docs/{{version}}/events) during the authentication process. You may [define listeners](/docs/{{version}}/events) for any of the following events: +
    + | Event Name | | --- | | `Illuminate\Auth\Events\Registered` | @@ -755,3 +757,5 @@ Laravel dispatches a variety of [events](/docs/{{version}}/events) during the au | `Illuminate\Auth\Events\OtherDeviceLogout` | | `Illuminate\Auth\Events\Lockout` | | `Illuminate\Auth\Events\PasswordReset` | + +
    diff --git a/cache.md b/cache.md index c7d4757ed0d..e4df87a66b0 100644 --- a/cache.md +++ b/cache.md @@ -440,6 +440,8 @@ Once your extension is registered, update the `CACHE_STORE` environment variable To execute code on every cache operation, you may listen for various [events](/docs/{{version}}/events) dispatched by the cache: +
    + | Event Name | | --- | | `Illuminate\Cache\Events\CacheHit` | @@ -447,6 +449,8 @@ To execute code on every cache operation, you may listen for various [events](/d | `Illuminate\Cache\Events\KeyForgotten` | | `Illuminate\Cache\Events\KeyWritten` | +
    + To increase performance, you may disable cache events by setting the `events` configuration option to `false` for a given cache store in your application's `config/cache.php` configuration file: ```php diff --git a/configuration.md b/configuration.md index 93306a3e2a9..eed3d3cf03e 100644 --- a/configuration.md +++ b/configuration.md @@ -71,6 +71,8 @@ Before loading your application's environment variables, Laravel determines if a All variables in your `.env` files are typically parsed as strings, so some reserved values have been created to allow you to return a wider range of types from the `env()` function: +
    + | `.env` Value | `env()` Value | | ------------ | ------------- | | true | (bool) true | @@ -82,6 +84,8 @@ All variables in your `.env` files are typically parsed as strings, so some rese | null | (null) null | | (null) | (null) null | +
    + If you need to define an environment variable with a value that contains spaces, you may do so by enclosing the value in double quotes: ```ini diff --git a/passport.md b/passport.md index c3b5cc4308c..8e06f93ff57 100644 --- a/passport.md +++ b/passport.md @@ -1162,11 +1162,15 @@ When using this method of authentication, you will need to ensure a valid CSRF t Passport raises events when issuing access tokens and refresh tokens. You may [listen for these events](/docs/{{version}}/events) to prune or revoke other access tokens in your database: +
    + | Event Name | | --- | | `Laravel\Passport\Events\AccessTokenCreated` | | `Laravel\Passport\Events\RefreshTokenCreated` | +
    + ## Testing From 527e6a3ba876a0b249dfe25de0582d5b1cd9907f Mon Sep 17 00:00:00 2001 From: Adam <56907039+adamjsturge@users.noreply.github.com> Date: Thu, 15 Aug 2024 11:09:25 -0700 Subject: [PATCH 1782/2609] Added subnet to trustedProxies example to show it's supported (#9821) --- requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests.md b/requests.md index 65871c45a82..1911bc51ae4 100644 --- a/requests.md +++ b/requests.md @@ -604,7 +604,7 @@ To solve this, you may enable the `Illuminate\Http\Middleware\TrustProxies` midd ->withMiddleware(function (Middleware $middleware) { $middleware->trustProxies(at: [ '192.168.1.1', - '192.168.1.2', + '10.0.0.0/8', ]); }) From cd97d3e2b5871b662ed4c2cdfbc104473f13d207 Mon Sep 17 00:00:00 2001 From: Francisco Amaral Date: Thu, 15 Aug 2024 19:15:06 +0100 Subject: [PATCH 1783/2609] Update trustHosts() documentation to reflect fix (#9822) * Add trustHosts() documentation when hosts are fetched from configuration file Document the fix from https://github.com/laravel/framework/pull/50877, triggered by issue https://github.com/laravel/framework/issues/50845 * Update requests.md --------- Co-authored-by: Taylor Otwell --- requests.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/requests.md b/requests.md index 1911bc51ae4..673bc7c83c2 100644 --- a/requests.md +++ b/requests.md @@ -649,3 +649,9 @@ By default, requests coming from subdomains of the application's URL are also au ->withMiddleware(function (Middleware $middleware) { $middleware->trustHosts(at: ['laravel.test'], subdomains: false); }) + +If you need to access your application's configuration files or database to determine your trusted hosts, you may provide a closure to the `at` argument: + + ->withMiddleware(function (Middleware $middleware) { + $middleware->trustHosts(at: fn () => config('app.trusted_hosts')); + }) From 736c61401b4dbcdad73362c66a906aaef4c0c158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Baconnier?= Date: Fri, 16 Aug 2024 20:01:33 +0200 Subject: [PATCH 1784/2609] Fix trustProxies (#9825) --- sail.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sail.md b/sail.md index 3811046a3d6..bd20494facb 100644 --- a/sail.md +++ b/sail.md @@ -460,9 +460,7 @@ sail share When sharing your site via the `share` command, you should configure your application's trusted proxies using the `trustProxies` middleware method in your application's `bootstrap/app.php` file. Otherwise, URL generation helpers such as `url` and `route` will be unable to determine the correct HTTP host that should be used during URL generation: ->withMiddleware(function (Middleware $middleware) { - $middleware->trustProxies(at: [ - '*', - ]); + $middleware->trustProxies(at: '*'); }) If you would like to choose the subdomain for your shared site, you may provide the `subdomain` option when executing the `share` command: From 35e8bf2eb40a1e871501dad494ede4fd31aef157 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Sat, 17 Aug 2024 04:05:17 +1000 Subject: [PATCH 1785/2609] [11.x] Update caveat for Vite URL processing with absolute paths and dedicated CSS entrypoints. (#9824) * Improve caveat on Vite URL processing with absolute paths * Update vite.md --------- Co-authored-by: Taylor Otwell --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 559549289b5..e04548df157 100644 --- a/vite.md +++ b/vite.md @@ -411,7 +411,7 @@ createInertiaApp({ ### URL Processing -When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of caveats to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory. +When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of caveats to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory. You should avoid using absolute paths when using a [dedicated CSS entrypoint](#configuring-vite) because, during development, browsers will try to load these paths from the Vite development server, where the CSS is hosted, rather than from your public directory. When referencing relative asset paths, you should remember that the paths are relative to the file where they are referenced. Any assets referenced via a relative path will be re-written, versioned, and bundled by Vite. From a87913b96b32f8e31022388626a9b008b7f17b9b Mon Sep 17 00:00:00 2001 From: Andrew Jorgenson Date: Fri, 16 Aug 2024 20:32:51 -0400 Subject: [PATCH 1786/2609] Update PHPunit test reference to match Pest (#9828) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index e9fe2b66f96..6b24ab85eeb 100644 --- a/helpers.md +++ b/helpers.md @@ -2530,7 +2530,7 @@ it('checks if ready three times', function () { ``` ```php tab=PHPUnit -public function test_it_checks_if_ready_four_times() +public function test_it_checks_if_ready_three_times() { Sleep::fake(); From 6e5d9d7ce2304218bc19aca7d77b6e4327688ab2 Mon Sep 17 00:00:00 2001 From: "Axel C. Lopez" Date: Sun, 18 Aug 2024 23:04:13 -0300 Subject: [PATCH 1787/2609] Fix broken links to #middleware-aliases (#9829) --- authentication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authentication.md b/authentication.md index aea40410552..0935e54aca8 100644 --- a/authentication.md +++ b/authentication.md @@ -182,7 +182,7 @@ To determine if the user making the incoming HTTP request is authenticated, you ### 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 is a [middleware alias](/docs/{{version}}/middleware#middleware-alias) for the `Illuminate\Auth\Middleware\Authenticate` class. Since this middleware is already aliased internally by Laravel, 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 is a [middleware alias](/docs/{{version}}/middleware#middleware-aliases) for the `Illuminate\Auth\Middleware\Authenticate` class. Since this middleware is already aliased internally by Laravel, all you need to do is attach the middleware to a route definition: Route::get('/flights', function () { // Only authenticated users may access this route... @@ -458,7 +458,7 @@ In addition to calling the `logout` method, it is recommended that you invalidat 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 included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` [middleware alias](/docs/{{version}}/middleware#middleware-alias): +Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is included on the routes that should receive session authentication. Typically, you should place this middleware on a route group definition so that it can be applied to the majority of your application's routes. By default, the `AuthenticateSession` middleware may be attached to a route using the `auth.session` [middleware alias](/docs/{{version}}/middleware#middleware-aliases): Route::middleware(['auth', 'auth.session'])->group(function () { Route::get('/', function () { From 8a54e92ce9007cc47b505a52fa97c1ad9b2555b4 Mon Sep 17 00:00:00 2001 From: adhham Date: Mon, 19 Aug 2024 07:10:14 +0500 Subject: [PATCH 1788/2609] Added info on ignoring case for "contains" and "containsAll" methods (#9811) * Add info on ignoring the case for "contains" and "containsAll" methods Added information on how to ignore the case when using "contains" and "containsAll" methods. * Use named arguments * Update strings.md * Update strings.md --------- Co-authored-by: Taylor Otwell --- strings.md | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/strings.md b/strings.md index fc2881bb116..ed7382cd60f 100644 --- a/strings.md +++ b/strings.md @@ -424,7 +424,7 @@ You may also pass an array as the second argument. If the string ends with any o #### `Str::contains()` {.collection-method} -The `Str::contains` method determines if the given string contains the given value. This method is case sensitive: +The `Str::contains` method determines if the given string contains the given value. By default this method is case sensitive: use Illuminate\Support\Str; @@ -440,6 +440,14 @@ You may also pass an array of values to determine if the given string contains a // true +You may disable case sensitivity by setting the `ignoreCase` argument to `true`: + + use Illuminate\Support\Str; + + $contains = Str::contains('This is my name', 'MY', ignoreCase: true); + + // true + #### `Str::containsAll()` {.collection-method} @@ -451,6 +459,14 @@ The `Str::containsAll` method determines if the given string contains all of the // true +You may disable case sensitivity by setting the `ignoreCase` argument to `true`: + + use Illuminate\Support\Str; + + $containsAll = Str::containsAll('This is my name', ['MY', 'NAME'], ignoreCase: true); + + // true + #### `Str::endsWith()` {.collection-method} @@ -1686,7 +1702,7 @@ You may also pass an array. If the string ends with any of the values in the arr #### `contains` {.collection-method} -The `contains` method determines if the given string contains the given value. This method is case sensitive: +The `contains` method determines if the given string contains the given value. By default this method is case sensitive: use Illuminate\Support\Str; @@ -1702,6 +1718,14 @@ You may also pass an array of values to determine if the given string contains a // true +You can disable case sensitivity by setting the `ignoreCase` argument to `true`: + + use Illuminate\Support\Str; + + $contains = Str::of('This is my name')->contains('MY', ignoreCase: true); + + // true + #### `containsAll` {.collection-method} @@ -1713,6 +1737,14 @@ The `containsAll` method determines if the given string contains all of the valu // true +You can disable case sensitivity by setting the `ignoreCase` argument to `true`: + + use Illuminate\Support\Str; + + $containsAll = Str::of('This is my name')->containsAll(['MY', 'NAME'], ignoreCase: true); + + // true + #### `dirname` {.collection-method} From 08f56047b25bb3e7ed46da76883238e8b0fc13d4 Mon Sep 17 00:00:00 2001 From: tadhgboyle Date: Tue, 20 Aug 2024 07:48:55 -0700 Subject: [PATCH 1789/2609] Fix remember named param syntax (#9832) --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index 0935e54aca8..586f2f29033 100644 --- a/authentication.md +++ b/authentication.md @@ -362,7 +362,7 @@ To authenticate a user using their database record's primary key, you may use th 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); + Auth::loginUsingId(1, remember: true); #### Authenticate a User Once From 03cecc6287a59cfed872ee77de67e61ac1706e25 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Aug 2024 09:49:30 -0500 Subject: [PATCH 1790/2609] wip --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index 586f2f29033..8a56d1a435c 100644 --- a/authentication.md +++ b/authentication.md @@ -360,7 +360,7 @@ To authenticate a user using their database record's primary key, you may use th Auth::loginUsingId(1); -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: +You may pass a boolean value to the `remember` argument of 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); From 3a2fa3ea59532f3239ae737ae78d0ffd1922c4c8 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Tue, 20 Aug 2024 23:50:54 +0900 Subject: [PATCH 1791/2609] Rename `link` to `links` (#9831) --- eloquent-resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-resources.md b/eloquent-resources.md index 20e0122c2bd..ea01e2d81bf 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -675,7 +675,7 @@ If your intermediate table is using an accessor other than `pivot`, you may use ### Adding Meta Data -Some JSON API standards require the addition of meta data to your resource and resource collections responses. This often includes things like `links` to the resource or related resources, or meta data about the resource itself. If you need to return additional meta data about a resource, include it in your `toArray` method. For example, you might include `link` information when transforming a resource collection: +Some JSON API standards require the addition of meta data to your resource and resource collections responses. This often includes things like `links` to the resource or related resources, or meta data about the resource itself. If you need to return additional meta data about a resource, include it in your `toArray` method. For example, you might include `links` information when transforming a resource collection: /** * Transform the resource into an array. From 24a9f991e9a7aa18f32e4ed48a5b2abd6ab0b99d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 20 Aug 2024 16:32:10 -0500 Subject: [PATCH 1792/2609] expectsSearch --- console-tests.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/console-tests.md b/console-tests.md index 9b8c8d991fe..826a7a9f938 100644 --- a/console-tests.md +++ b/console-tests.md @@ -86,6 +86,36 @@ public function test_console_command(): void } ``` +If you are utilizing the `search` or `multisearch` functions provided by [Laravel Prompts](/docs/{{version}}/prompts), you may use the `expectsSearch` assertion to mock the user's input, search results, and selection: + +```php tab=Pest +test('console command', function () { + $this->artisan('example') + ->expectsSearch('What is your name?', search: 'Tay', answers: [ + 'Taylor Otwell', + 'Taylor Swift', + 'Darian Taylor' + ], answer: 'Taylor Otwell') + ->assertExitCode(0); +}); +``` + +```php tab=PHPUnit +/** + * Test a console command. + */ +public function test_console_command(): void +{ + $this->artisan('example') + ->expectsSearch('What is your name?', search: 'Tay', answers: [ + 'Taylor Otwell', + 'Taylor Swift', + 'Darian Taylor' + ], answer: 'Taylor Otwell') + ->assertExitCode(0); +} +``` + You may also assert that a console command does not generate any output using the `doesntExpectOutput` method: ```php tab=Pest From 05a382c6e7721b9a243773f3e86114bf001280f4 Mon Sep 17 00:00:00 2001 From: Peter Elmered Date: Wed, 21 Aug 2024 18:01:43 +0200 Subject: [PATCH 1793/2609] Remove canSelectAll example (#9834) It looks like this parameter was reverted and replaced with a keyboard shortcut. See the PR here: https://github.com/laravel/prompts/pull/147 --- prompts.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/prompts.md b/prompts.md index dabba4d6bea..3b088a2c46c 100644 --- a/prompts.md +++ b/prompts.md @@ -421,16 +421,6 @@ $categories = multiselect( ); ``` -You may allow the user to easily select all options via the `canSelectAll` argument: - -```php -$categories = multiselect( - label: 'What categories should be assigned?', - options: Category::pluck('name', 'id'), - canSelectAll: true -); -``` - #### Requiring a Value From 8bc2cdb5a19e7e757da97a6b4572dedb230f25b1 Mon Sep 17 00:00:00 2001 From: iainco <84080628+iainco@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:01:58 +0100 Subject: [PATCH 1794/2609] Update horizon.md (typo) (#9833) --- horizon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/horizon.md b/horizon.md index 1124a947ebf..d1d8405e339 100644 --- a/horizon.md +++ b/horizon.md @@ -230,7 +230,7 @@ You may check the current status of the Horizon process using the `horizon:statu php artisan horizon:status ``` -You may gracefully terminate the Horizon process using the `horizon:terminate` Artisan command. Any jobs that are currently being processed by will be completed and then Horizon will stop executing: +You may gracefully terminate the Horizon process using the `horizon:terminate` Artisan command. Any jobs that are currently being processed will be completed and then Horizon will stop executing: ```shell php artisan horizon:terminate From af8926466f6afe7dd8415d751c9c95cda7aa1965 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 21 Aug 2024 14:48:53 -0500 Subject: [PATCH 1795/2609] feature instances --- pennant.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pennant.md b/pennant.md index 71da2a38e88..1d8e04a2411 100644 --- a/pennant.md +++ b/pennant.md @@ -143,6 +143,14 @@ class NewApi } ``` +If you would like to manually resolve an instance of a class based feature, you may invoke the `instance` method on the `Feature` facade: + +```php +use Illuminate\Support\Facades\Feature; + +$instance = Feature::instance(NewApi::class); +``` + > [!NOTE] > Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. From 9bbce539dc2e2ca0efe0b035bf87db8e3b2ec0e4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 21 Aug 2024 16:24:34 -0500 Subject: [PATCH 1796/2609] force destroy --- eloquent.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eloquent.md b/eloquent.md index 46d75a76917..514a2d535a4 100644 --- a/eloquent.md +++ b/eloquent.md @@ -927,6 +927,10 @@ In the example above, we are retrieving the model from the database before calli Flight::destroy(collect([1, 2, 3])); +If you are utilizing [soft deleting models](#soft-deleting), you may permanently delete models via the `forceDestroy` method: + + Flight::forceDestroy(1); + > [!WARNING] > The `destroy` method loads each model individually and calls the `delete` method so that the `deleting` and `deleted` events are properly dispatched for each model. From 6441ead7d4b19a8c39576b9f8e52452f7b075b64 Mon Sep 17 00:00:00 2001 From: Einar Hansen <49709354+einar-hansen@users.noreply.github.com> Date: Wed, 21 Aug 2024 23:25:06 +0200 Subject: [PATCH 1797/2609] Add the resource method to the http client docs (#9812) --- http-client.md | 1 + 1 file changed, 1 insertion(+) diff --git a/http-client.md b/http-client.md index ca74f4d5fa9..2e49325fe7b 100644 --- a/http-client.md +++ b/http-client.md @@ -38,6 +38,7 @@ The `get` method returns an instance of `Illuminate\Http\Client\Response`, which $response->json($key = null, $default = null) : mixed; $response->object() : object; $response->collect($key = null) : Illuminate\Support\Collection; + $response->resource() : resource; $response->status() : int; $response->successful() : bool; $response->redirect(): bool; From de98ca780d45beb9772ba7febf85e6de6531b06a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 21 Aug 2024 16:35:39 -0500 Subject: [PATCH 1798/2609] transform --- prompts.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/prompts.md b/prompts.md index 3b088a2c46c..7ac4de4b0fa 100644 --- a/prompts.md +++ b/prompts.md @@ -13,6 +13,7 @@ - [Search](#search) - [Multi-search](#multisearch) - [Pause](#pause) +- [Transforming Input Before Validation](#transforming-input-before-validation) - [Forms](#forms) - [Informational Messages](#informational-messages) - [Tables](#tables) @@ -721,6 +722,23 @@ use function Laravel\Prompts\pause; pause('Press ENTER to continue.'); ``` + +## Transforming Input Before Validation + +Sometimes you may want to transform the prompt input before validation takes place. For example, you may wish to remove white space from any provided strings. To accomplish this, many of the prompt functions provide a `transform` argument, which accepts a closure: + +```php +$name = text( + label: 'What is your name?', + transform: fn (string $value) => trim($value), + validate: fn (string $value) => match (true) { + strlen($value) < 3 => 'The name must be at least 3 characters.', + strlen($value) > 255 => 'The name must not exceed 255 characters.', + default => null + } +); +``` + ## Forms From a6124510a5db5a83e9268fafdb48b6cc8e1ebbb1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 22 Aug 2024 12:35:52 +1000 Subject: [PATCH 1799/2609] Header fixes (#9836) --- middleware.md | 1 - routing.md | 1 - session.md | 2 +- strings.md | 1 - upgrade.md | 1 - 5 files changed, 1 insertion(+), 5 deletions(-) diff --git a/middleware.md b/middleware.md index 3d245a89313..9938bfe0aac 100644 --- a/middleware.md +++ b/middleware.md @@ -61,7 +61,6 @@ It's best to envision middleware as a series of "layers" HTTP requests must pass > [!NOTE] > All middleware are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a middleware's constructor. - #### Middleware and Responses diff --git a/routing.md b/routing.md index 12e4d050166..0821accd1f2 100644 --- a/routing.md +++ b/routing.md @@ -552,7 +552,6 @@ Typically, implicit model binding will not retrieve models that have been [soft return $user->email; })->withTrashed(); - #### Customizing the Key diff --git a/session.md b/session.md index c01fddf0b7a..e7ef049a5a6 100644 --- a/session.md +++ b/session.md @@ -185,7 +185,7 @@ The `pull` method will retrieve and delete an item from the session in a single $value = $request->session()->pull('key', 'default'); - + #### Incrementing and Decrementing Session Values If your session data contains an integer you wish to increment or decrement, you may use the `increment` and `decrement` methods: diff --git a/strings.md b/strings.md index ed7382cd60f..9408404fb6d 100644 --- a/strings.md +++ b/strings.md @@ -372,7 +372,6 @@ The `Str::camel` method converts the given string to `camelCase`: // 'fooBar' - #### `Str::charAt()` {.collection-method} The `Str::charAt` method returns the character at the specified index. If the index is out of bounds, `false` is returned: diff --git a/upgrade.md b/upgrade.md index d76be13755d..381f486024c 100644 --- a/upgrade.md +++ b/upgrade.md @@ -164,7 +164,6 @@ public function getAuthPasswordName() The default `User` model included with Laravel receives this method automatically since the method is included within the `Illuminate\Auth\Authenticatable` trait. - #### The `AuthenticationException` Class **Likelihood Of Impact: Very Low** From 8758bd2c244370939f1246f1d32138ec478c01e1 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 22 Aug 2024 12:40:40 +1000 Subject: [PATCH 1800/2609] [11.x] document async validate (#9835) * document async validate * Fix syntax * formatting --------- Co-authored-by: Taylor Otwell --- precognition.md | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/precognition.md b/precognition.md index 158409a74c2..a3942e15135 100644 --- a/precognition.md +++ b/precognition.md @@ -150,6 +150,20 @@ If you are validating a subset of a form's inputs with Precognition, it can be u > ``` +As we have seen, you can hook into an input's `change` event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step. + +To do this with Precognition, you should mark the fields you wish to validate as "touched" by passing their names to the `touch` method. Then, call the `validate` method with `onSuccess` or `onValidationError` callbacks: + +```html + +``` + Of course, you may also execute code in reaction to the response to the form submission. The form's `submit` function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form inputs on successful submission, or handle a failed request: ```js @@ -314,14 +328,28 @@ If you are validating a subset of a form's inputs with Precognition, it can be u + onChange={(e) => { form.setData('avatar', e.target.value); form.forgetError('avatar'); - } + }} > ``` +As we have seen, you can hook into an input's `blur` event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step. + +To do this with Precognition, you should mark the fields you wish to validate as "touched" by passing their names to the `touch` method. Then, call the `validate` method with `onSuccess` or `onValidationError` callbacks: + +```jsx + +``` + Of course, you may also execute code in reaction to the response to the form submission. The form's `submit` function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form's inputs on a successful form submission, or handle a failed request: ```js @@ -501,6 +529,20 @@ You may also determine if an input has passed or failed validation by passing th > [!WARNING] > A form input will only appear as valid or invalid once it has changed and a validation response has been received. +As we have seen, you can hook into an input's `change` event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step. + +To do this with Precognition, you should mark the fields you wish to validate as "touched" by passing their names to the `touch` method. Then, call the `validate` method with `onSuccess` or `onValidationError` callbacks: + +```html + +``` + You may determine if a form submission request is in-flight by inspecting the form's `processing` property: ```html From 1a3d112255c8d0da2cbc10ebe581deebdd172ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCller?= Date: Thu, 22 Aug 2024 14:47:18 +0200 Subject: [PATCH 1801/2609] Add Context to the facade class reference (#9837) --- facades.md | 1 + 1 file changed, 1 insertion(+) diff --git a/facades.md b/facades.md index 1c8fcbb0bd9..175c50ea358 100644 --- a/facades.md +++ b/facades.md @@ -297,6 +297,7 @@ Below you will find every facade and its underlying class. This is a useful tool | Cache (Instance) | [Illuminate\Cache\Repository](https://laravel.com/api/{{version}}/Illuminate/Cache/Repository.html) | `cache.store` | | Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/{{version}}/Illuminate/Cache/CacheManager.html) | `cache` | | Config | [Illuminate\Config\Repository](https://laravel.com/api/{{version}}/Illuminate/Config/Repository.html) | `config` | +| Context | [Illuminate\Log\Context\Repository](https://laravel.com/api/{{version}}/Illuminate/Log/Context/Repository.html) |   | | Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/{{version}}/Illuminate/Cookie/CookieJar.html) | `cookie` | | Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/{{version}}/Illuminate/Encryption/Encrypter.html) | `encrypter` | | Date | [Illuminate\Support\DateFactory](https://laravel.com/api/{{version}}/Illuminate/Support/DateFactory.html) | `date` | From b2830447ba2c1edca5dceffbbe740ec7dd3a5c3a Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 24 Aug 2024 02:22:19 +1000 Subject: [PATCH 1802/2609] Fix example (#9839) --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 1d47735cb48..401b8a63c29 100644 --- a/queries.md +++ b/queries.md @@ -979,7 +979,7 @@ Alternatively, you may use the `limit` and `offset` methods. These methods are f 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->string('role'); + $role = $request->input('role'); $users = DB::table('users') ->when($role, function (Builder $query, string $role) { From b9fa924860cb30ba5be04d6dddb62f4b6ce17d19 Mon Sep 17 00:00:00 2001 From: Faraz Samapoor Date: Sat, 24 Aug 2024 15:26:56 +0330 Subject: [PATCH 1803/2609] [11.x] Updates Artisan Docs (#9840) * Adds "migrate:install" to the Tinker Command Allow List. References: https://github.com/laravel/tinker/commit/8531d0f0e0285783f420bdd743c37f99c0f394d1 * Update artisan.md --------- Co-authored-by: Faraz Samapoor Co-authored-by: Taylor Otwell --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index ae3d061a6ff..794326a4d6a 100644 --- a/artisan.md +++ b/artisan.md @@ -86,7 +86,7 @@ php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider" #### Command Allow List -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: +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`, `migrate:install`, `up`, and `optimize` 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, From b63370cb54fd7158d004dfae2b647de937c80355 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 26 Aug 2024 20:39:05 +0800 Subject: [PATCH 1804/2609] Explicitly include `--port=9515` when running `chromedriver` on CI (#9841) --- dusk.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dusk.md b/dusk.md index 382867eba1e..0f49217b95a 100644 --- a/dusk.md +++ b/dusk.md @@ -2125,7 +2125,7 @@ To run Dusk tests on [Heroku CI](https://www.heroku.com/continuous-integration), ], "scripts": { "test-setup": "cp .env.testing .env", - "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk" + "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux --port=9515 > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk" } } } @@ -2191,7 +2191,7 @@ jobs: - name: Upgrade Chrome Driver run: php artisan dusk:chrome-driver --detect - name: Start Chrome Driver - run: ./vendor/laravel/dusk/bin/chromedriver-linux & + run: ./vendor/laravel/dusk/bin/chromedriver-linux --port=9515 & - name: Run Laravel Server run: php artisan serve --no-reload & - name: Run Dusk Tests From d0aaa1b62ab0304f0a7eb3d881eee9a01c44ea99 Mon Sep 17 00:00:00 2001 From: Greg Brock Date: Tue, 27 Aug 2024 09:01:29 -0400 Subject: [PATCH 1805/2609] Add corrected middleware class (#9851) The `ValidateSignature` middleware is actually for validating signed routes --- fortify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fortify.md b/fortify.md index 28d5b6e1ff5..d6e271a0c72 100644 --- a/fortify.md +++ b/fortify.md @@ -536,7 +536,7 @@ If the request to resend the verification link email was successful, Fortify wil ### Protecting Routes -To specify that a route or group of routes requires that the user has verified their email address, you should attach Laravel's built-in `verified` middleware to the route. The `verified` middleware alias is automatically registered by Laravel and serves as an alias for the `Illuminate\Routing\Middleware\ValidateSignature` middleware: +To specify that a route or group of routes requires that the user has verified their email address, you should attach Laravel's built-in `verified` middleware to the route. The `verified` middleware alias is automatically registered by Laravel and serves as an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` middleware: ```php Route::get('/dashboard', function () { From 7b696f268160e61c19eeed2a9c49191ae89f6bf1 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Tue, 27 Aug 2024 22:02:57 +0900 Subject: [PATCH 1806/2609] Add Livewire directory to full page refresh on file save (#9866) --- vite.md | 1 + 1 file changed, 1 insertion(+) diff --git a/vite.md b/vite.md index e04548df157..640558bbac8 100644 --- a/vite.md +++ b/vite.md @@ -499,6 +499,7 @@ export default defineConfig({ When the `refresh` option is `true`, saving files in the following directories will trigger the browser to perform a full page refresh while you are running `npm run dev`: +- `app/Livewire/**` - `app/View/Components/**` - `lang/**` - `resources/lang/**` From bb4650831ec4567c07d72f7a9949a95f90c04035 Mon Sep 17 00:00:00 2001 From: Joe Koop Date: Wed, 28 Aug 2024 11:04:39 -0500 Subject: [PATCH 1807/2609] (attempted to) fix/remove dead internal links (#9867) --- authorization.md | 2 +- controllers.md | 2 +- eloquent.md | 2 +- helpers.md | 13 ++++++------- requests.md | 2 +- verification.md | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/authorization.md b/authorization.md index d795ac76c09..ca5b4c8f470 100644 --- a/authorization.md +++ b/authorization.md @@ -607,7 +607,7 @@ As previously discussed, some policy methods like `create` do not require a mode ### 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 may be attached to a route using the `can` [middleware alias](/docs/{{version}}/middleware#middleware-alias), which is automatically registered by Laravel. Let's explore an example of using the `can` middleware to authorize that a user can update a post: +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 may be attached to a route using the `can` [middleware alias](/docs/{{version}}/middleware#middleware-aliases), which is automatically registered by Laravel. Let's explore an example of using the `can` middleware to authorize that a user can update a post: use App\Models\Post; diff --git a/controllers.md b/controllers.md index 8bd7b1b1fff..30ad8127d72 100644 --- a/controllers.md +++ b/controllers.md @@ -431,7 +431,7 @@ Singleton resources may also be nested within a standard resource: Route::singleton('photos.thumbnail', ThumbnailController::class); ``` -In this example, the `photos` resource would receive all of the [standard resource routes](#actions-handled-by-resource-controller); however, the `thumbnail` resource would be a singleton resource with the following routes: +In this example, the `photos` resource would receive all of the [standard resource routes](#actions-handled-by-resource-controllers); however, the `thumbnail` resource would be a singleton resource with the following routes:
    diff --git a/eloquent.md b/eloquent.md index 514a2d535a4..14383122503 100644 --- a/eloquent.md +++ b/eloquent.md @@ -388,7 +388,7 @@ Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction()); ## Retrieving Models -Once you have created a model and [its associated database table](/docs/{{version}}/migrations#writing-migrations), you are ready to start retrieving data from your database. You can think of each Eloquent model as a powerful [query builder](/docs/{{version}}/queries) allowing you to fluently query the database table associated with the model. The model's `all` method will retrieve all of the records from the model's associated database table: +Once you have created a model and [its associated database table](/docs/{{version}}/migrations#generating-migrations), you are ready to start retrieving data from your database. You can think of each Eloquent model as a powerful [query builder](/docs/{{version}}/queries) allowing you to fluently query the database table associated with the model. The model's `all` method will retrieve all of the records from the model's associated database table: use App\Models\Flight; diff --git a/helpers.md b/helpers.md index 6b24ab85eeb..382255b5899 100644 --- a/helpers.md +++ b/helpers.md @@ -69,7 +69,6 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::sort](#method-array-sort) [Arr::sortDesc](#method-array-sort-desc) [Arr::sortRecursive](#method-array-sort-recursive) -[Arr::sortRecursiveDesc](#method-array-sort-recursive-desc) [Arr::take](#method-array-take) [Arr::toCssClasses](#method-array-to-css-classes) [Arr::toCssStyles](#method-array-to-css-styles) @@ -1603,7 +1602,7 @@ If no path is provided, an `Illuminate\Routing\UrlGenerator` instance is returne #### `abort()` {.collection-method} -The `abort` function throws [an HTTP exception](/docs/{{version}}/errors#http-exceptions) which will be rendered by the [exception handler](/docs/{{version}}/errors#the-exception-handler): +The `abort` function throws [an HTTP exception](/docs/{{version}}/errors#http-exceptions) which will be rendered by the [exception handler](/docs/{{version}}/errors#handling-exceptions): abort(403); @@ -1919,7 +1918,7 @@ An array of contextual data may also be passed to the function: logger('User has logged in.', ['id' => $user->id]); -A [logger](/docs/{{version}}/errors#logging) instance will be returned if no value is passed to the function: +A [logger](/docs/{{version}}/logging) instance will be returned if no value is passed to the function: logger()->error('You are not allowed here.'); @@ -2031,7 +2030,7 @@ The `redirect` function returns a [redirect HTTP response](/docs/{{version}}/res #### `report()` {.collection-method} -The `report` function will report an exception using your [exception handler](/docs/{{version}}/errors#the-exception-handler): +The `report` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions): report($e); @@ -2042,7 +2041,7 @@ The `report` function also accepts a string as an argument. When a string is giv #### `report_if()` {.collection-method} -The `report_if` function will report an exception using your [exception handler](/docs/{{version}}/errors#the-exception-handler) if the given condition is `true`: +The `report_if` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if the given condition is `true`: report_if($shouldReport, $e); @@ -2051,7 +2050,7 @@ The `report_if` function will report an exception using your [exception handler] #### `report_unless()` {.collection-method} -The `report_unless` function will report an exception using your [exception handler](/docs/{{version}}/errors#the-exception-handler) if the given condition is `false`: +The `report_unless` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if the given condition is `false`: report_unless($reportingDisabled, $e); @@ -2069,7 +2068,7 @@ The `request` function returns the current [request](/docs/{{version}}/requests) #### `rescue()` {.collection-method} -The `rescue` function executes the given closure and catches any exceptions that occur during its execution. All exceptions that are caught will be sent to your [exception handler](/docs/{{version}}/errors#the-exception-handler); however, the request will continue processing: +The `rescue` function executes the given closure and catches any exceptions that occur during its execution. All exceptions that are caught will be sent to your [exception handler](/docs/{{version}}/errors#handling-exceptions); however, the request will continue processing: return rescue(function () { return $this->method(); diff --git a/requests.md b/requests.md index 673bc7c83c2..103f92df37d 100644 --- a/requests.md +++ b/requests.md @@ -306,7 +306,7 @@ When sending JSON requests to your application, you may access the JSON data via #### Retrieving Stringable Input Values -Instead of retrieving the request's input data as a primitive `string`, you may use the `string` method to retrieve the request data as an instance of [`Illuminate\Support\Stringable`](/docs/{{version}}/helpers#fluent-strings): +Instead of retrieving the request's input data as a primitive `string`, you may use the `string` method to retrieve the request data as an instance of [`Illuminate\Support\Stringable`](/docs/{{version}}/strings): $name = $request->string('name')->trim(); diff --git a/verification.md b/verification.md index d8c2cca2c8d..d6c0a752499 100644 --- a/verification.md +++ b/verification.md @@ -108,7 +108,7 @@ Sometimes a user may misplace or accidentally delete the email address verificat ### Protecting Routes -[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel includes a `verified` [middleware alias](/docs/{{version}}/middleware#middleware-alias), which is an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` middleware class. Since this alias is already automatically registered by Laravel, all you need to do is attach the `verified` middleware to a route definition. Typically, this middleware is paired with the `auth` middleware: +[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel includes a `verified` [middleware alias](/docs/{{version}}/middleware#middleware-aliases), which is an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` middleware class. Since this alias is already automatically registered by Laravel, all you need to do is attach the `verified` middleware to a route definition. Typically, this middleware is paired with the `auth` middleware: Route::get('/profile', function () { // Only verified users may access this route... From 1c647219738642c4b667c5ed7bac880e18ab67d9 Mon Sep 17 00:00:00 2001 From: Sergio Peris Date: Sun, 1 Sep 2024 01:31:21 +0200 Subject: [PATCH 1808/2609] Update filesystem.md (#9869) --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index 659db7882a7..6a4e839cd67 100644 --- a/filesystem.md +++ b/filesystem.md @@ -219,7 +219,7 @@ AWS_URL=http://localhost:9000/local ``` > [!WARNING] -> Generating temporary storage URLs via the `temporaryUrl` method is not supported when using MinIO. +> Generating temporary storage URLs via the `temporaryUrl` method may not work when using MinIO if the `endpoint` is not accessible by the client. ## Obtaining Disk Instances From eeb8b2839390f6e5c317978cdea3c5056681ebf3 Mon Sep 17 00:00:00 2001 From: Amir Date: Mon, 2 Sep 2024 18:26:47 +0330 Subject: [PATCH 1809/2609] queue: change ThrottlesExceptions decay from minutes to seconds (#9872) --- queues.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/queues.md b/queues.md index b54d1a90689..080b54adedb 100644 --- a/queues.md +++ b/queues.md @@ -610,7 +610,7 @@ For example, let's imagine a queued job that interacts with a third-party API th */ public function middleware(): array { - return [new ThrottlesExceptions(10, 5)]; + return [new ThrottlesExceptions(10, 5 * 60)]; } /** @@ -621,7 +621,7 @@ For example, let's imagine a queued job that interacts with a third-party API th return now()->addMinutes(5); } -The first constructor argument accepted by the middleware is the number of exceptions the job can throw before being throttled, while the second constructor argument is the number of minutes that should elapse before the job is attempted again once it has been throttled. In the code example above, if the job throws 10 exceptions within 5 minutes, we will wait 5 minutes before attempting the job again. +The first constructor argument accepted by the middleware is the number of exceptions the job can throw before being throttled, while the second constructor argument is the number of seconds that should elapse before the job is attempted again once it has been throttled. In the code example above, if the job throws 10 exceptions within 5 minutes, we will wait 5 minutes before attempting the job again. When a job throws an exception but the exception threshold has not yet been reached, the job will typically be retried immediately. However, you may specify the number of minutes such a job should be delayed by calling the `backoff` method when attaching the middleware to the job: @@ -634,7 +634,7 @@ When a job throws an exception but the exception threshold has not yet been reac */ public function middleware(): array { - return [(new ThrottlesExceptions(10, 5))->backoff(5)]; + return [(new ThrottlesExceptions(10, 5 * 60))->backoff(5)]; } Internally, this middleware uses Laravel's cache system to implement rate limiting, and the job's class name is utilized as the cache "key". You may override this key by calling the `by` method when attaching the middleware to your job. This may be useful if you have multiple jobs interacting with the same third-party service and you would like them to share a common throttling "bucket": @@ -648,7 +648,7 @@ Internally, this middleware uses Laravel's cache system to implement rate limiti */ public function middleware(): array { - return [(new ThrottlesExceptions(10, 10))->by('key')]; + return [(new ThrottlesExceptions(10, 10 * 60))->by('key')]; } By default, this middleware will throttle every exception. You can modify this behaviour by invoking the `when` method when attaching the middleware to your job. The exception will then only be throttled if closure provided to the `when` method returns `true`: @@ -663,7 +663,7 @@ By default, this middleware will throttle every exception. You can modify this b */ public function middleware(): array { - return [(new ThrottlesExceptions(10, 10))->when( + return [(new ThrottlesExceptions(10, 10 * 60))->when( fn (Throwable $throwable) => $throwable instanceof HttpClientException )]; } @@ -680,7 +680,7 @@ If you would like to have the throttled exceptions reported to your application' */ public function middleware(): array { - return [(new ThrottlesExceptions(10, 10))->report( + return [(new ThrottlesExceptions(10, 10 * 60))->report( fn (Throwable $throwable) => $throwable instanceof HttpClientException )]; } From c92f80d37444c1574ec525db8830bff189087fe0 Mon Sep 17 00:00:00 2001 From: Naoki Haba <59875779+NaokiHaba@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:57:29 +0900 Subject: [PATCH 1810/2609] chore: Update Pint CLI commands to use vendor/bin directory (#9873) --- pint.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pint.md b/pint.md index f7da25d53d4..5de25ca04ba 100644 --- a/pint.md +++ b/pint.md @@ -81,7 +81,7 @@ As previously mentioned, Pint does not require any configuration. However, if yo In addition, if you wish to use a `pint.json` from a specific directory, you may provide the `--config` option when invoking Pint: ```shell -pint --config vendor/my-company/coding-style/pint.json +./vendor/bin/pint --config vendor/my-company/coding-style/pint.json ``` @@ -90,7 +90,7 @@ pint --config vendor/my-company/coding-style/pint.json Presets define a set of rules that can be used to fix code style issues in your code. By default, Pint uses the `laravel` preset, which fixes issues by following the opinionated coding style of Laravel. However, you may specify a different preset by providing the `--preset` option to Pint: ```shell -pint --preset psr12 +./vendor/bin/pint --preset psr12 ``` If you wish, you may also set the preset in your project's `pint.json` file: From e1b400d993364b991891bbcaa380bd1979bc741d Mon Sep 17 00:00:00 2001 From: Tobias Grasse <834914+tobias-grasse@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:25:38 +0200 Subject: [PATCH 1811/2609] Update sail.md (#9875) The seleniarm/standalone-chromium fork was merged back into the official Selenium project and is only maintained there. The last seleniarm/standalone-chromium release is Chromium v124.0.6367.78. --- sail.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sail.md b/sail.md index bd20494facb..f14030178d8 100644 --- a/sail.md +++ b/sail.md @@ -352,11 +352,11 @@ sail dusk #### Selenium on Apple Silicon -If your local machine contains an Apple Silicon chip, your `selenium` service must use the `seleniarm/standalone-chromium` image: +If your local machine contains an Apple Silicon chip, your `selenium` service must use the `selenium/standalone-chromium` image: ```yaml selenium: - image: 'seleniarm/standalone-chromium' + image: 'selenium/standalone-chromium' extra_hosts: - 'host.docker.internal:host-gateway' volumes: From 20492202acc86e4f234fa2320cb126f19493cbc6 Mon Sep 17 00:00:00 2001 From: ytoda Date: Tue, 3 Sep 2024 22:39:47 +0900 Subject: [PATCH 1812/2609] Update eloquent-mutators.md (#9865) * Update eloquent-mutators.md Clarify UTC formatting for timestamp columns in date/datetime casting * Update eloquent-mutators.md --------- Co-authored-by: Taylor Otwell --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 3aeaeca0981..34a5ba2e7a6 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -443,7 +443,7 @@ To specify the format that should be used when actually storing a model's dates By default, the `date` and `datetime` casts will serialize dates to a UTC ISO-8601 date string (`YYYY-MM-DDTHH:MM:SS.uuuuuuZ`), regardless of the timezone specified in your application's `timezone` configuration option. You are strongly encouraged to always use this serialization format, as well as to store your application's dates in the UTC timezone by not changing your application's `timezone` configuration option from its default `UTC` value. Consistently using the UTC timezone throughout your application will provide the maximum level of interoperability with other date manipulation libraries written in PHP and JavaScript. -If a custom format is applied to the `date` or `datetime` cast, such as `datetime:Y-m-d H:i:s`, the inner timezone of the Carbon instance will be used during date serialization. Typically, this will be the timezone specified in your application's `timezone` configuration option. +If a custom format is applied to the `date` or `datetime` cast, such as `datetime:Y-m-d H:i:s`, the inner timezone of the Carbon instance will be used during date serialization. Typically, this will be the timezone specified in your application's `timezone` configuration option. However, it's important to note that `timestamp` columns such as `created_at` and `updated_at` are exempt from this behavior and are always formatted in UTC, regardless of the application's timezone setting. ### Enum Casting From 040ab151f8350470f8df5d00bfba05f56c155d12 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Sep 2024 09:34:44 -0500 Subject: [PATCH 1813/2609] wip --- socialite.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/socialite.md b/socialite.md index 54a27175346..e4689ceab92 100644 --- a/socialite.md +++ b/socialite.md @@ -15,7 +15,7 @@ ## 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 via Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, Bitbucket, and Slack. +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 via Facebook, X, LinkedIn, Google, GitHub, GitLab, Bitbucket, and Slack. > [!NOTE] > Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. @@ -39,7 +39,7 @@ When upgrading to a new major version of Socialite, it's important that you care Before using Socialite, you will need to add credentials for the OAuth providers your application utilizes. Typically, these credentials may be retrieved by creating a "developer application" within the dashboard of the service you will be authenticating with. -These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter` (OAuth 1.0), `twitter-oauth-2` (OAuth 2.0), `linkedin-openid`, `google`, `github`, `gitlab`, `bitbucket`, `slack`, or `slack-openid`, depending on the providers your application requires: +These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `x`, `linkedin-openid`, `google`, `github`, `gitlab`, `bitbucket`, `slack`, or `slack-openid`, depending on the providers your application requires: 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), @@ -189,7 +189,7 @@ Differing properties and methods may be available on this object depending on wh }); -#### Retrieving User Details From a Token (OAuth2) +#### Retrieving User Details From a Token If you already have a valid access token for a user, you can retrieve their user details using Socialite's `userFromToken` method: @@ -199,15 +199,6 @@ If you already have a valid access token for a user, you can retrieve their user If you are using Facebook Limited Login via an iOS application, Facebook will return an OIDC token instead of an access token. Like an access token, the OIDC token can be provided to the `userFromToken` method in order to retrieve user details. - -#### Retrieving User Details From a Token and Secret (OAuth1) - -If you already have a valid token and secret for a user, you can retrieve their user details using Socialite's `userFromTokenAndSecret` method: - - use Laravel\Socialite\Facades\Socialite; - - $user = Socialite::driver('twitter')->userFromTokenAndSecret($token, $secret); - #### Stateless Authentication @@ -216,6 +207,3 @@ The `stateless` method may be used to disable session state verification. This i use Laravel\Socialite\Facades\Socialite; return Socialite::driver('google')->stateless()->user(); - -> [!WARNING] -> Stateless authentication is not available for the Twitter OAuth 1.0 driver. From 06c3df075e79917d1a6afbbb587a17545f2ac7c9 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 6 Sep 2024 07:04:00 +1000 Subject: [PATCH 1814/2609] [11.x] Asset Prefetching (#9838) * Asset Prefetching (vite) * Add event docs * typo * Use initialism * formatting --------- Co-authored-by: Taylor Otwell --- vite.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/vite.md b/vite.md index 640558bbac8..16d69524989 100644 --- a/vite.md +++ b/vite.md @@ -18,6 +18,7 @@ - [Processing Static Assets With Vite](#blade-processing-static-assets) - [Refreshing on Save](#blade-refreshing-on-save) - [Aliases](#blade-aliases) +- [Asset Prefetching](#asset-prefetching) - [Custom Base URLs](#custom-base-urls) - [Environment Variables](#environment-variables) - [Disabling Vite in Tests](#disabling-vite-in-tests) @@ -405,6 +406,8 @@ createInertiaApp({ }); ``` +If you are using Vite's code splitting feature with Inertia, we recommend configuring [asset prefetching](#asset-prefetching). + > [!NOTE] > Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. @@ -562,6 +565,75 @@ Once a macro has been defined, it can be invoked within your templates. For exam Laravel Logo ``` + +## Asset Prefetching + +When building an SPA using Vite's code splitting feature, required assets are fetched on each page navigation. This behavior can lead to delayed UI rendering. If this is a problem for your frontend framework of choice, Laravel offers the ability to eagerly prefetch your application's JavaScript and CSS assets on initial page load. + +You can instruct Laravel to eagerly prefetch your assets by invoking the `Vite::prefetch` method in the `boot` method of a [service provider](/docs/{{version}}/providers): + +```php + + addEventListener('load', () => setTimeout(() => { + dispatchEvent(new Event('vite:prefetch')) + }, 3000)) + +``` + ## Custom Base URLs From 62eb15f6637ea5810edf8a9aeae0e621003cff0f Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 6 Sep 2024 08:38:07 -0500 Subject: [PATCH 1815/2609] standardize constructor examples (#9879) - always use trailing commas in signature - always use multiline arguments - correct indentation - remove empty bodies --- broadcasting.md | 5 +---- collections.md | 4 ++-- container.md | 4 ++-- events.md | 5 +---- queues.md | 10 +++++----- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 6fb7ebad010..7c42269e48a 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -672,10 +672,7 @@ Finally, you may place the authorization logic for your channel in the channel c /** * Create a new channel instance. */ - public function __construct() - { - // ... - } + public function __construct() {} /** * Authenticate the user's access to the channel. diff --git a/collections.md b/collections.md index c8c9198a7dc..58a05007a4e 100644 --- a/collections.md +++ b/collections.md @@ -1488,7 +1488,7 @@ The `mapInto()` method iterates over the collection, creating a new instance of * Create a new currency instance. */ function __construct( - public string $code + public string $code, ) {} } @@ -1842,7 +1842,7 @@ The `pipeInto` method creates a new instance of the given class and passes the c * Create a new ResourceCollection instance. */ public function __construct( - public Collection $collection, + public Collection $collection, ) {} } diff --git a/container.md b/container.md index 7a1709cad4f..89db5d5c617 100644 --- a/container.md +++ b/container.md @@ -199,7 +199,7 @@ This statement tells the container that it should inject the `RedisEventPusher` * Create a new class instance. */ public function __construct( - protected EventPusher $pusher + protected EventPusher $pusher, ) {} @@ -380,7 +380,7 @@ If you would like to have the Laravel container instance itself injected into a * Create a new class instance. */ public function __construct( - protected Container $container + protected Container $container, ) {} diff --git a/events.md b/events.md index dc14cabc4f1..ebe2934bddb 100644 --- a/events.md +++ b/events.md @@ -220,10 +220,7 @@ Next, let's take a look at the listener for our example event. Event listeners r /** * Create the event listener. */ - public function __construct() - { - // ... - } + public function __construct() {} /** * Handle the event. diff --git a/queues.md b/queues.md index 080b54adedb..a324913faf4 100644 --- a/queues.md +++ b/queues.md @@ -236,8 +236,9 @@ Or, to prevent relations from being serialized, you can call the `withoutRelatio /** * Create a new job instance. */ - public function __construct(Podcast $podcast) - { + public function __construct( + Podcast $podcast, + ) { $this->podcast = $podcast->withoutRelations(); } @@ -250,9 +251,8 @@ If you are using PHP constructor property promotion and would like to indicate t */ public function __construct( #[WithoutRelations] - public Podcast $podcast - ) { - } + public Podcast $podcast, + ) {} If a job receives a collection or array of Eloquent models instead of a single model, the models within that collection will not have their relationships restored when the job is deserialized and executed. This is to prevent excessive resource usage on jobs that deal with large numbers of models. From 0f44074a2b2d10fe6ccc0f72d57574c7c8a360f5 Mon Sep 17 00:00:00 2001 From: Pedro Borges Date: Fri, 6 Sep 2024 17:37:10 -0300 Subject: [PATCH 1816/2609] Document `Response` "stream" and "streamJson" methods (#9881) * Document response "stream" and "streamJson" methods * formatting * formatting --------- Co-authored-by: Taylor Otwell --- responses.md | 56 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/responses.md b/responses.md index 1653629ed5c..559ed9f3fab 100644 --- a/responses.md +++ b/responses.md @@ -14,6 +14,7 @@ - [JSON Responses](#json-responses) - [File Downloads](#file-downloads) - [File Responses](#file-responses) + - [Streamed Responses](#streamed-responses) - [Response Macros](#response-macros) @@ -287,6 +288,52 @@ The `download` method may be used to generate a response that forces the user's > [!WARNING] > Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. + +### File Responses + +The `file` method may be used to display a file, such as an image or PDF, directly in the user's browser instead of initiating a download. This method accepts the absolute path to the file as its first argument and an array of headers as its second argument: + + return response()->file($pathToFile); + + return response()->file($pathToFile, $headers); + + +### Streamed Responses + +By streaming data to the client as it is generated, you can significantly reduce memory usage and improve performance, especially for very large responses. Streamed responses allow the client to begin processing data before the server has finished sending it: + + function streamedContent(): Generator { + yield 'Hello, '; + yield 'World!'; + } + + Route::get('/stream', function () { + return response()->stream(function (): void { + foreach (streamedContent() as $chunk) { + echo $chunk; + ob_flush(); + flush(); + sleep(2); // Simulate delay between chunks... + } + }, 200, ['X-Accel-Buffering' => 'no']); + }); + +> [!NOTE] +> Internally, Laravel utilizes PHP's output buffering functionality. As you can see in the example above, you should use the `ob_flush` and `flush` functions to push buffered content to the client. + + +#### Streamed JSON Responses + +If you need to stream JSON data incrementally, you may utilize the `streamJson` method. This method is especially useful for large datasets that need to be sent progressively to the browser in a format that can be easily parsed by JavaScript: + + use App\Models\User; + + Route::get('/users.json', function () { + return response()->streamJson([ + 'users' => User::cursor(), + ]); + }); + #### Streamed Downloads @@ -300,15 +347,6 @@ Sometimes you may wish to turn the string response of a given operation into a d ->readme('laravel', 'laravel')['contents']; }, 'laravel-readme.md'); - -### File Responses - -The `file` method may be used to display a file, such as an image or PDF, directly in the user's browser instead of initiating a download. This method accepts the absolute path to the file as its first argument and an array of headers as its second argument: - - return response()->file($pathToFile); - - return response()->file($pathToFile, $headers); - ## Response Macros From f1c0e59dc78917eef5d5adfadc4d89448fed38f3 Mon Sep 17 00:00:00 2001 From: Kennedy Tedesco Date: Mon, 9 Sep 2024 10:58:04 -0300 Subject: [PATCH 1817/2609] Add docs for `isNotFilled()` method on Request docs (#9884) --- requests.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/requests.md b/requests.md index 103f92df37d..dd35ff68f84 100644 --- a/requests.md +++ b/requests.md @@ -405,6 +405,18 @@ If you would like to determine if a value is present on the request and is not a // ... } +If you would like to determine if a value is missing from the request or is an empty string, you may use the `isNotFilled` method: + + if ($request->isNotFilled('name')) { + // ... + } + +When given an array, the `isNotFilled` method will determine if all of the specified values are missing or empty: + + if ($request->isNotFilled(['name', 'email'])) { + // ... + } + The `anyFilled` method returns `true` if any of the specified values is not an empty string: if ($request->anyFilled(['name', 'email'])) { From 9ad603d85e801497147758044a45a63117133e50 Mon Sep 17 00:00:00 2001 From: "Dr. Adam Nielsen" <1765602+iwasherefirst2@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:16:07 +0200 Subject: [PATCH 1818/2609] Update Xdebug docs: Static IP for Linux pre-Docker 20.10 (#9887) Clarified Linux Xdebug setup. For Docker 20.10+, host.docker.internal works without manual config. For older versions, added instructions to set a static IP in docker-compose.yml. --- sail.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/sail.md b/sail.md index f14030178d8..0d9541b3107 100644 --- a/sail.md +++ b/sail.md @@ -483,18 +483,28 @@ SAIL_XDEBUG_MODE=develop,debug,coverage #### Linux Host IP Configuration -Internally, the `XDEBUG_CONFIG` environment variable is defined as `client_host=host.docker.internal` so that Xdebug will be properly configured for Mac and Windows (WSL2). If your local machine is running Linux, you should ensure that you are running Docker Engine 17.06.0+ and Compose 1.16.0+. Otherwise, you will need to manually define this environment variable as shown below. +Internally, the `XDEBUG_CONFIG` environment variable is defined as `client_host=host.docker.internal` so that Xdebug will be properly configured for Mac and Windows (WSL2). If your local machine is running Linux and you're using **Docker 20.10+**, `host.docker.internal` is available, and no manual configuration is required. -First, you should determine the correct host IP address to add to the environment variable by running the following command. Typically, the `` should be the name of the container that serves your application and often ends with `_laravel.test_1`: +For **Docker versions older than 20.10**, `host.docker.internal` is not supported on Linux, and you will need to manually define the host IP. To do this, configure a static IP for your container by defining a custom network in your `docker-compose.yml` file: -```shell -docker inspect -f {{range.NetworkSettings.Networks}}{{.Gateway}}{{end}} +```yaml +networks: + custom_network: + ipam: + config: + - subnet: 172.20.0.0/16 + +services: + laravel.test: + networks: + custom_network: + ipv4_address: 172.20.0.2 ``` -Once you have obtained the correct host IP address, you should define the `SAIL_XDEBUG_CONFIG` variable within your application's `.env` file: +Once you have set the static IP, define the SAIL_XDEBUG_CONFIG variable within your application's .env file: ```ini -SAIL_XDEBUG_CONFIG="client_host=" +SAIL_XDEBUG_CONFIG="client_host=172.20.0.2" ``` From 3a8bc7fa4eaf942a9e1793e12db1b7b530c1b063 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Sep 2024 08:17:02 -0500 Subject: [PATCH 1819/2609] wip --- sail.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sail.md b/sail.md index 0d9541b3107..7226abe3d78 100644 --- a/sail.md +++ b/sail.md @@ -483,9 +483,9 @@ SAIL_XDEBUG_MODE=develop,debug,coverage #### Linux Host IP Configuration -Internally, the `XDEBUG_CONFIG` environment variable is defined as `client_host=host.docker.internal` so that Xdebug will be properly configured for Mac and Windows (WSL2). If your local machine is running Linux and you're using **Docker 20.10+**, `host.docker.internal` is available, and no manual configuration is required. +Internally, the `XDEBUG_CONFIG` environment variable is defined as `client_host=host.docker.internal` so that Xdebug will be properly configured for Mac and Windows (WSL2). If your local machine is running Linux and you're using Docker 20.10+, `host.docker.internal` is available, and no manual configuration is required. -For **Docker versions older than 20.10**, `host.docker.internal` is not supported on Linux, and you will need to manually define the host IP. To do this, configure a static IP for your container by defining a custom network in your `docker-compose.yml` file: +For Docker versions older than 20.10, `host.docker.internal` is not supported on Linux, and you will need to manually define the host IP. To do this, configure a static IP for your container by defining a custom network in your `docker-compose.yml` file: ```yaml networks: From 681e1f1beea1aae88c75d5a6b10df2b8fc4bce8c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Sep 2024 17:00:56 -0500 Subject: [PATCH 1820/2609] Laracon 2024 (#9885) * concurrency * deferred functions * chaperone * chaperone polymorphic * attributes * custom attributes * cache flexible docs --- cache.md | 13 +++++ concurrency.md | 50 ++++++++++++++++++ container.md | 104 ++++++++++++++++++++++++++++++++++++++ documentation.md | 1 + eloquent-relationships.md | 87 +++++++++++++++++++++++++++++++ helpers.md | 37 ++++++++++++++ 6 files changed, 292 insertions(+) create mode 100644 concurrency.md diff --git a/cache.md b/cache.md index e4df87a66b0..0eced2f74d1 100644 --- a/cache.md +++ b/cache.md @@ -202,6 +202,19 @@ You may use the `rememberForever` method to retrieve an item from the cache or s return DB::table('users')->get(); }); + +#### Stale While Revalidate + +When using the `Cache::remember` method, some users may experience slow response times if the cached value has expired. For certain types of data, it can be useful to allow partially stale data to be served while the cached value is recalculated in the background, preventing some users from experiencing slow response times while cached values are calculated. This is often referred to as the "stale-while-revalidate" pattern, and the `Cache::flexible` method provides an implementation of this pattern. + +The flexible method accepts an array that specifies how long the cached value is considered “fresh” and when it becomes “stale.” The first value in the array represents the number of seconds the cache is considered fresh, while the second value defines how long it can be served as stale data before recalculation is necessary. + +If a request is made within the fresh period (before the first value), the cache is returned immediately without recalculation. If a request is made during the stale period (between the two values), the stale value is served to the user, and a [deferred function](/docs/{{version}}/helpers#deferred-functions) is registered to refresh the cached value after the response is sent to the user. If a request is made after the second value, the cache is considered expired, and the value is recalculated immediately, which may result in a slower response for the user: + + $value = Cache::flexible('users', [5, 10], function () { + return DB::table('users')->get(); + }); + #### Retrieve and Delete diff --git a/concurrency.md b/concurrency.md new file mode 100644 index 00000000000..114950d9f38 --- /dev/null +++ b/concurrency.md @@ -0,0 +1,50 @@ +# Concurrency + +- [Introduction](#introduction) +- [Running Concurrent Tasks](#running-concurrent-tasks) +- [Deferring Concurrent Tasks](#deferring-concurrent-tasks) + + +## Introduction + +> [!WARNING] +> Laravel's `Concurrency` facade is currently in beta while we gather community feedback. + +Sometimes you may need to execute several slow tasks which do not depend on one another. In many cases, significant performance improvements can be realized by executing the tasks concurrently. Laravel's `Concurrency` facade provides a simple, convenient API for executing closures concurrently. + + +#### How it Works + +Laravel achieves concurrency by serializing the given closures and dispatching them to a hidden Artisan CLI command, which unserializes the closures and invokes it within its own PHP process. After the closure has been invoked, the resulting value is serialized back to the parent process. + +The `Concurrency` facade supports three drivers: `process` (the default), `fork`, and `sync`. The `fork` driver offers improved performance compared to the default `process` driver, but it may only be used within PHP's CLI context, as PHP does not support forking during web requests. The `sync` driver is primarily useful during testing when you want to disable all concurrency and simple execute the given closure in sequence within the parent process. + + +## Running Concurrent Tasks + +To run concurrent tasks, you may invoke the `Concurrency` facade's `run` method. The `run` method accepts an array of closures which should be executed simultaneously in child PHP processes: + +```php +use Illuminate\Support\Facades\Concurrency; +use Illuminate\Support\Facades\DB; + +[$userCount, $orderCount] = Concurrency::run([ + fn () => DB::table('users')->count(), + fn () => DB::table('orders')->count(), +]); +``` + + +## Deferring Concurrent Tasks + +If you would like to execute an array of closures concurrently, but are not interested in the results returned by those closures, you should consider using the `defer` method. When the `defer` method is invoked, the given closures are not executed immediately. Instead, Laravel will execute the closures concurrently after the HTTP response has been sent to the user: + +```php +use App\Services\Metrics; +use Illuminate\Support\Facades\Concurrency; + +Concurrency::defer([ + fn () => Metrics::report('users'), + fn () => Metrics::report('orders'), +]); +``` diff --git a/container.md b/container.md index 89db5d5c617..39155e8ea0f 100644 --- a/container.md +++ b/container.md @@ -7,6 +7,7 @@ - [Binding Basics](#binding-basics) - [Binding Interfaces to Implementations](#binding-interfaces-to-implementations) - [Contextual Binding](#contextual-binding) + - [Contextual Attributes](#contextual-attributes) - [Binding Primitives](#binding-primitives) - [Binding Typed Variadics](#binding-typed-variadics) - [Tagging](#tagging) @@ -225,6 +226,109 @@ Sometimes you may have two classes that utilize the same interface, but you wish return Storage::disk('s3'); }); + +### Contextual Attributes + +Since contextual binding is often used to inject implementations of drivers or configuration values, Laravel offers a variety of contextual binding attributes that allow to inject these types of values without manually defining the contextual bindings in your service providers. + +For example, the `Storage` attribute may be used to inject a specific [storage disk](/docs/{{version}}/filesystem): + +```php +middleware('auth'); +``` + + +#### Defining Custom Attributes + +You can create your own contextual attributes by implementing the `Illuminate\Contracts\Container\ContextualAttribute` contract. The container will call your attribute's `resolve` method, which should resolve the value that should be injected into the class utilizing the attribute. In the example below, we will re-implement Laravel's built-in `Config` attribute: + + make('config')->get($attribute->key, $attribute->default); + } + } + ### Binding Primitives diff --git a/documentation.md b/documentation.md index 2ea03e6a683..b4e36d8c503 100644 --- a/documentation.md +++ b/documentation.md @@ -34,6 +34,7 @@ - [Broadcasting](/docs/{{version}}/broadcasting) - [Cache](/docs/{{version}}/cache) - [Collections](/docs/{{version}}/collections) + - [Concurrency](/docs/{{version}}/concurrency) - [Context](/docs/{{version}}/context) - [Contracts](/docs/{{version}}/contracts) - [Events](/docs/{{version}}/events) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 0c64bac641a..33692568ec3 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -194,6 +194,53 @@ Like the `hasOne` method, you may also override the foreign and local keys by pa return $this->hasMany(Comment::class, 'foreign_key', 'local_key'); + +#### Automatically Hydrating Parent Models on Children + +Even when utilizing Eloquent eager loading, "N + 1" query problems can arise if you try to access the parent model from a child model while looping through the child models: + +```php +$posts = Post::with('comments')->get(); + +foreach ($posts as $post) { + foreach ($post->comments as $comment) { + echo $comment->post->title; + } +} +``` + +In the example above, an "N + 1" query problem has been introduced because, even though comments were eager loaded for every `Post` model, Eloquent does not automatically hydrate the parent `Post` on each child `Comment` model. + +If you would like Eloquent to automatically hydrate parent models onto their children, you may invoke the `chaperone` method when defining a `hasMany` relationship: + + hasMany(Comment::class)->chaperone(); + } + } + +Or, if you would like to opt-in to automatic parent hydration at run time, you may invoke the `chaperone` model when eager loading the relationship: + +```php +use App\Models\Post; + +$posts = Post::with([ + 'comments' => fn ($comments) => $comments->chaperone(), +])->get(); +``` + ### One to Many (Inverse) / Belongs To @@ -1008,6 +1055,46 @@ You may also retrieve the parent of a polymorphic child model by accessing the n The `commentable` relation on the `Comment` model will return either a `Post` or `Video` instance, depending on which type of model is the comment's parent. + +#### Automatically Hydrating Parent Models on Children + +Even when utilizing Eloquent eager loading, "N + 1" query problems can arise if you try to access the parent model from a child model while looping through the child models: + +```php +$posts = Post::with('comments')->get(); + +foreach ($posts as $post) { + foreach ($post->comments as $comment) { + echo $comment->commentable->title; + } +} +``` + +In the example above, an "N + 1" query problem has been introduced because, even though comments were eager loaded for every `Post` model, Eloquent does not automatically hydrate the parent `Post` on each child `Comment` model. + +If you would like Eloquent to automatically hydrate parent models onto their children, you may invoke the `chaperone` method when defining a `hasMany` relationship: + + class Post extends Model + { + /** + * Get all of the post's comments. + */ + public function comments(): MorphMany + { + return $this->morphMany(Comment::class, 'commentable')->chaperone(); + } + } + +Or, if you would like to opt-in to automatic parent hydration at run time, you may invoke the `chaperone` model when eager loading the relationship: + +```php +use App\Models\Post; + +$posts = Post::with([ + 'comments' => fn ($comments) => $comments->chaperone(), +])->get(); +``` + ### One of Many (Polymorphic) diff --git a/helpers.md b/helpers.md index 382255b5899..fe06ecdfd46 100644 --- a/helpers.md +++ b/helpers.md @@ -5,6 +5,7 @@ - [Other Utilities](#other-utilities) - [Benchmarking](#benchmarking) - [Dates](#dates) + - [Deferred Functions](#deferred-functions) - [Lottery](#lottery) - [Pipeline](#pipeline) - [Sleep](#sleep) @@ -2352,6 +2353,36 @@ $now = Carbon::now(); For a thorough discussion of Carbon and its features, please consult the [official Carbon documentation](https://carbon.nesbot.com/docs/). + +### Deferred Functions + +> [!WARNING] +> Deferred functions are currently in beta while we gather community feedback. + +While Laravel's [queued jobs](/docs/{{version}}/queues) allow you to queue tasks for background processing, sometimes you may have simple tasks you would like to defer without configuring or maintaining a long-running queue worker. + +Deferred functions allow you to defer the execution of a closure until after the HTTP response has been sent to the user, keeping your application feeling fast and responsive. To defer the execution of a closure, simply pass the closure to the `defer` function: + +```php +use App\Services\Metrics; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Route; + +Route::post('/orders', function (Request $request) { + // Create order... + + defer(fn () => Metrics::reportOrder($order)); + + return $order; +}); +``` + +By default, deferred functions will only be executed if the HTTP response, Artisan command, or queued job from which `defer` is invoked completes successfully. This means that deferred functions will not be executed if a request results in a `4xx` or `5xx` HTTP response. If you would like a deferred function to always execute, you may chain the `always` method onto your deferred function: + +```php +defer(fn () => Metrics::reportOrder($order))->always(); +``` + ### Lottery @@ -2451,6 +2482,12 @@ Laravel's `Sleep` class is a light-weight wrapper around PHP's native `sleep` an The `Sleep` class offers a variety of methods that allow you to work with different units of time: + // Return a value after sleeping... + $result = Sleep::for(1)->second()->then(fn () => 1 + 1); + + // Sleep while a given value is true... + Sleep::for(1)->second()->while(fn () => shouldKeepSleeping()); + // Pause execution for 90 seconds... Sleep::for(1.5)->minutes(); From c394da46ffa88cd14e25ece6d374ce3057b457a9 Mon Sep 17 00:00:00 2001 From: Kennedy Tedesco Date: Wed, 11 Sep 2024 19:07:41 -0300 Subject: [PATCH 1821/2609] [11.x] Add Skipping Jobs section (#9883) * [11.x] Add Skipping Jobs section * formatting --------- Co-authored-by: Taylor Otwell --- queues.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/queues.md b/queues.md index a324913faf4..ea99a89c0f8 100644 --- a/queues.md +++ b/queues.md @@ -12,6 +12,7 @@ - [Rate Limiting](#rate-limiting) - [Preventing Job Overlaps](#preventing-job-overlaps) - [Throttling Exceptions](#throttling-exceptions) + - [Skipping Jobs](#skipping-jobs) - [Dispatching Jobs](#dispatching-jobs) - [Delayed Dispatching](#delayed-dispatching) - [Synchronous Dispatching](#synchronous-dispatching) @@ -688,6 +689,39 @@ If you would like to have the throttled exceptions reported to your application' > [!NOTE] > If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. + +### Skipping Jobs + +The `Skip` middleware allows you to specify that a job should be skipped / deleted without needing to modify the job's logic. The `Skip::when` method will delete the job if the given condition evaluates to `true`, while the `Skip::unless` method will delete the job if the condition evaluates to `false`: + + use Illuminate\Queue\Middleware\Skip; + + /** + * Get the middleware the job should pass through. + */ + public function middleware(): array + { + return [ + Skip::when($someCondition), + ]; + } + +You can also pass a `Closure` to the `when` and `unless` methods for more complex conditional evaluation: + + use Illuminate\Queue\Middleware\Skip; + + /** + * Get the middleware the job should pass through. + */ + public function middleware(): array + { + return [ + Skip::when(function (): bool { + return $this->shouldSkip(); + }), + ]; + } + ## Dispatching Jobs From 4ee51634d1f738b7f3ebffa4955bdae40b0f2470 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Sep 2024 17:11:03 -0500 Subject: [PATCH 1822/2609] wip --- eloquent-collections.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/eloquent-collections.md b/eloquent-collections.md index 5808b9c5868..bd1080352c1 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -67,6 +67,7 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe [diff](#method-diff) [except](#method-except) [find](#method-find) +[findOrFail](#method-find-or-fail) [fresh](#method-fresh) [intersect](#method-intersect) [load](#method-load) @@ -125,6 +126,15 @@ The `find` method returns the model that has a primary key matching the given ke $user = $users->find(1); + +#### `findOrFail($key)` {.collection-method} + +The `findOrFail` method returns the model that has a primary key matching the given key or throws an `Illuminate\Database\Eloquent\ModelNotFoundException` exception if no matching model can be found in the collection: + + $users = User::all(); + + $user = $users->findOrFail(1); + #### `fresh($with = [])` {.collection-method} From 0a27093a90b0db8cb6ef00ff576b7f8af786326f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Sep 2024 17:12:46 -0500 Subject: [PATCH 1823/2609] wip --- prompts.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/prompts.md b/prompts.md index 7ac4de4b0fa..5f8f49aeb1f 100644 --- a/prompts.md +++ b/prompts.md @@ -19,6 +19,7 @@ - [Tables](#tables) - [Spin](#spin) - [Progress Bar](#progress) +- [Clearing the Terminal](#clear) - [Terminal Considerations](#terminal-considerations) - [Unsupported Environments and Fallbacks](#fallbacks) @@ -888,6 +889,17 @@ foreach ($users as $user) { $progress->finish(); ``` + +## Clearing the Terminal + +The `clear` function may be used to clear the user's terminal: + +``` +use function Laravel\Prompts\clear; + +clear(); +``` + ## Terminal Considerations From 57ac7ac1a316e7c6c6efed4c4894921a65f406e6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Sep 2024 17:13:48 -0500 Subject: [PATCH 1824/2609] wip --- container.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/container.md b/container.md index 39155e8ea0f..575bd93461a 100644 --- a/container.md +++ b/container.md @@ -252,7 +252,7 @@ class PhotoController extends Controller } ``` -In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `DB`, and `Log` attributes: +In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `DB`, `Log`, and [`Tag`](#tagging) attributes: ```php Date: Wed, 11 Sep 2024 17:17:29 -0500 Subject: [PATCH 1825/2609] wip --- helpers.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/helpers.md b/helpers.md index fe06ecdfd46..8a7dc84a67e 100644 --- a/helpers.md +++ b/helpers.md @@ -199,6 +199,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [value](#method-value) [view](#method-view) [with](#method-with) +[when](#method-when)
    @@ -2304,6 +2305,23 @@ The `with` function returns the value it is given. If a closure is passed as the // 5 + +#### `when()` {.collection-method} + +The `when` function returns the value it is given if the given condition evaluates to `true`. Otherwise, `null` is returned. If a closure is passed as the second argument to the function, the closure will be executed and its returned value will be returned: + + $value = when(true, 'Hello World'); + + $value = when(true, fn () => 'Hello World'); + +The `when` function is primarily useful for conditionally rendering HTML attributes: + +```blade +
    + ... +
    +``` + ## Other Utilities From 1dd01f3e13d27a8669a1b8832b86427d8e649c06 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Sep 2024 17:17:47 -0500 Subject: [PATCH 1826/2609] wip --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 8a7dc84a67e..f293cd61e54 100644 --- a/helpers.md +++ b/helpers.md @@ -2308,7 +2308,7 @@ The `with` function returns the value it is given. If a closure is passed as the #### `when()` {.collection-method} -The `when` function returns the value it is given if the given condition evaluates to `true`. Otherwise, `null` is returned. If a closure is passed as the second argument to the function, the closure will be executed and its returned value will be returned: +The `when` function returns the value it is given if a given condition evaluates to `true`. Otherwise, `null` is returned. If a closure is passed as the second argument to the function, the closure will be executed and its returned value will be returned: $value = when(true, 'Hello World'); From cfa4896d43b4fa1730d58e9e87d78c9adba046e4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Sep 2024 17:26:42 -0500 Subject: [PATCH 1827/2609] wip --- queries.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/queries.md b/queries.md index 401b8a63c29..369cd83d647 100644 --- a/queries.md +++ b/queries.md @@ -95,6 +95,10 @@ If you just need to retrieve a single row from a database table, you may use the return $user->email; +If you would like to retrieve a single row from the database table, but throw a `Illuminate\Database\RecordNotFoundException` is no matching row is found, you may use the `firstOrFail` method. If the `RecordNotFoundException` is not caught, a 404 HTTP response is automatically sent back to the client: + + $user = DB::table('users')->where('name', 'John')->firstOrFail(); + 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'); From 02dc4b6b61bc8ad607cc9b4ee9d4469f25fd8d00 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Sep 2024 17:27:00 -0500 Subject: [PATCH 1828/2609] wip --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 369cd83d647..4c01fe44a19 100644 --- a/queries.md +++ b/queries.md @@ -95,7 +95,7 @@ If you just need to retrieve a single row from a database table, you may use the return $user->email; -If you would like to retrieve a single row from the database table, but throw a `Illuminate\Database\RecordNotFoundException` is no matching row is found, you may use the `firstOrFail` method. If the `RecordNotFoundException` is not caught, a 404 HTTP response is automatically sent back to the client: +If you would like to retrieve a single row from a database table, but throw an `Illuminate\Database\RecordNotFoundException` if no matching row is found, you may use the `firstOrFail` method. If the `RecordNotFoundException` is not caught, a 404 HTTP response is automatically sent back to the client: $user = DB::table('users')->where('name', 'John')->firstOrFail(); From 510dd2ef02a1efee44174d64c97ea79cf4532d49 Mon Sep 17 00:00:00 2001 From: Ronald Beilsma Date: Thu, 12 Sep 2024 17:36:39 +0300 Subject: [PATCH 1829/2609] [11.x] Fix fluent methods order (alphabetical) (#9891) * [11.x] Fix fluent methods order (alphabetical) * Also fix order in method list --- strings.md | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/strings.md b/strings.md index 9408404fb6d..e81aa12ba24 100644 --- a/strings.md +++ b/strings.md @@ -143,8 +143,8 @@ Laravel includes a variety of functions for manipulating string values. Many of [containsAll](#method-fluent-str-contains-all) [dirname](#method-fluent-str-dirname) [endsWith](#method-fluent-str-ends-with) -[excerpt](#method-fluent-str-excerpt) [exactly](#method-fluent-str-exactly) +[excerpt](#method-fluent-str-excerpt) [explode](#method-fluent-str-explode) [finish](#method-fluent-str-finish) [headline](#method-fluent-str-headline) @@ -1763,32 +1763,6 @@ If necessary, you may specify how many directory levels you wish to trim from th // '/foo' - -#### `excerpt` {.collection-method} - -The `excerpt` method extracts an excerpt from the string that matches the first instance of a phrase within that string: - - use Illuminate\Support\Str; - - $excerpt = Str::of('This is my name')->excerpt('my', [ - 'radius' => 3 - ]); - - // '...is my na...' - -The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. - -In addition, you may use the `omission` option to change the string that will be prepended and appended to the truncated string: - - use Illuminate\Support\Str; - - $excerpt = Str::of('This is my name')->excerpt('name', [ - 'radius' => 3, - 'omission' => '(...) ' - ]); - - // '(...) my name' - #### `endsWith` {.collection-method} @@ -1823,6 +1797,32 @@ The `exactly` method determines if the given string is an exact match with anoth // true + +#### `excerpt` {.collection-method} + +The `excerpt` method extracts an excerpt from the string that matches the first instance of a phrase within that string: + + use Illuminate\Support\Str; + + $excerpt = Str::of('This is my name')->excerpt('my', [ + 'radius' => 3 + ]); + + // '...is my na...' + +The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. + +In addition, you may use the `omission` option to change the string that will be prepended and appended to the truncated string: + + use Illuminate\Support\Str; + + $excerpt = Str::of('This is my name')->excerpt('name', [ + 'radius' => 3, + 'omission' => '(...) ' + ]); + + // '(...) my name' + #### `explode` {.collection-method} From d62fe02f06606bc5696999e03f42008ce950801c Mon Sep 17 00:00:00 2001 From: Ronald Beilsma Date: Thu, 12 Sep 2024 17:41:25 +0300 Subject: [PATCH 1830/2609] [11.x] Document Str::deduplicate method (#9890) * [11.x] Document Str::deduplicate method * Fix order (alphabetical) * formatting --------- Co-authored-by: Taylor Otwell --- strings.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/strings.md b/strings.md index e81aa12ba24..e56975e3fe6 100644 --- a/strings.md +++ b/strings.md @@ -47,6 +47,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::chopEnd](#method-str-chop-end) [Str::contains](#method-str-contains) [Str::containsAll](#method-str-contains-all) +[Str::deduplicate](#method-deduplicate) [Str::endsWith](#method-ends-with) [Str::excerpt](#method-excerpt) [Str::finish](#method-str-finish) @@ -141,6 +142,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [chopEnd](#method-fluent-str-chop-end) [contains](#method-fluent-str-contains) [containsAll](#method-fluent-str-contains-all) +[deduplicate](#method-fluent-str-deduplicate) [dirname](#method-fluent-str-dirname) [endsWith](#method-fluent-str-ends-with) [exactly](#method-fluent-str-exactly) @@ -466,6 +468,25 @@ You may disable case sensitivity by setting the `ignoreCase` argument to `true`: // true + +#### `Str::deduplicate()` {.collection-method} + +The `Str::deduplicate` method replaces consecutive instances of a character with a single instance of that character in the given string. By default, the method deduplicates spaces: + + use Illuminate\Support\Str; + + $result = Str::deduplicate('The Laravel Framework'); + + // The Laravel Framework + +You may specify a different character to deduplicate by passing it in as the second argument to the method: + + use Illuminate\Support\Str; + + $result = Str::deduplicate('The---Laravel---Framework', '-'); + + // The-Laravel-Framework + #### `Str::endsWith()` {.collection-method} @@ -1744,6 +1765,25 @@ You can disable case sensitivity by setting the `ignoreCase` argument to `true`: // true + +#### `deduplicate` {.collection-method} + +The `deduplicate` method replaces consecutive instances of a character with a single instance of that character in the given string. By default, the method deduplicates spaces: + + use Illuminate\Support\Str; + + $result = Str::of('The Laravel Framework')->deduplicate(); + + // The Laravel Framework + +You may specify a different character to deduplicate by passing it in as the second argument to the method: + + use Illuminate\Support\Str; + + $result = Str::of('The---Laravel---Framework')->deduplicate('-'); + + // The-Laravel-Framework + #### `dirname` {.collection-method} From ce6cf0e289dff0b42a9c2a1e2206200498d50018 Mon Sep 17 00:00:00 2001 From: Braunson Yager Date: Thu, 12 Sep 2024 14:19:11 -0400 Subject: [PATCH 1831/2609] [11.x] Update scout.md about connecting to Typesense in Laravel Sail (#9889) * Update scout.md Added note under Typesense section about setting the TYPESENSE_HOST to match the Docker container for Typesense allowing Scout to properly connect. * Update scout.md --------- Co-authored-by: Taylor Otwell --- scout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scout.md b/scout.md index ba64255c8aa..65af70930a7 100644 --- a/scout.md +++ b/scout.md @@ -147,7 +147,7 @@ TYPESENSE_API_KEY=masterKey TYPESENSE_HOST=localhost ``` -If needed, you may also specify your installation's port, path, and protocol: +If you are using [Laravel Sail](/docs/{{version}}/sail), you may need to adjust the `TYPESENSE_HOST` environment variable to match the Docker container name. You may also optionally specify your installation's port, path, and protocol: ```env TYPESENSE_PORT=8108 From 4fb89ead0f2fb36c7deda672befa00d4be008c44 Mon Sep 17 00:00:00 2001 From: Benji Bilheimer Date: Fri, 13 Sep 2024 18:02:09 +0200 Subject: [PATCH 1832/2609] Add documentation for retrieving input values as integers to requests.md (#9894) * Add documentation for retrieving input values as integers to requests.md * Update requests.md --------- Co-authored-by: Taylor Otwell --- requests.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/requests.md b/requests.md index dd35ff68f84..bfcc95d2467 100644 --- a/requests.md +++ b/requests.md @@ -310,6 +310,13 @@ Instead of retrieving the request's input data as a primitive `string`, you may $name = $request->string('name')->trim(); + +#### Retrieving Integer Input Values + +To retrieve input values as integers, you may use the `integer` method. This method will attempt to cast the input value to an integer. If the input is not present or the cast fails, it will return the default value you specify. This is particularly useful for pagination or other numeric inputs: + + $perPage = $request->integer('per_page'); + #### Retrieving Boolean Input Values From ce657e91c960ef28cfa9da317021c9d4650e945c Mon Sep 17 00:00:00 2001 From: Giancarlo Di Massa Date: Fri, 13 Sep 2024 18:06:21 +0200 Subject: [PATCH 1833/2609] Update concurrency.md (#9893) * Update concurrency.md - Add requirements of spatie/fork - Show example on how to switch drivers - Explain that the defer method will never run the tasks in the PHP CLI's context * Update concurrency.md --------- Co-authored-by: Taylor Otwell --- concurrency.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/concurrency.md b/concurrency.md index 114950d9f38..d849f6049a1 100644 --- a/concurrency.md +++ b/concurrency.md @@ -17,7 +17,15 @@ Sometimes you may need to execute several slow tasks which do not depend on one Laravel achieves concurrency by serializing the given closures and dispatching them to a hidden Artisan CLI command, which unserializes the closures and invokes it within its own PHP process. After the closure has been invoked, the resulting value is serialized back to the parent process. -The `Concurrency` facade supports three drivers: `process` (the default), `fork`, and `sync`. The `fork` driver offers improved performance compared to the default `process` driver, but it may only be used within PHP's CLI context, as PHP does not support forking during web requests. The `sync` driver is primarily useful during testing when you want to disable all concurrency and simple execute the given closure in sequence within the parent process. +The `Concurrency` facade supports three drivers: `process` (the default), `fork`, and `sync`. + +The `fork` driver offers improved performance compared to the default `process` driver, but it may only be used within PHP's CLI context, as PHP does not support forking during web requests. Before using the `fork` driver, you need to install the `spatie/fork` package: + +```bash +composer require spatie/fork +``` + +The `sync` driver is primarily useful during testing when you want to disable all concurrency and simply execute the given closures in sequence within the parent process. ## Running Concurrent Tasks @@ -34,6 +42,18 @@ use Illuminate\Support\Facades\DB; ]); ``` +To use a specific driver, you may use the `driver` method: + +```php +$results = Concurrency::driver('fork')->run(...); +``` + +Or, to change the default concurrency driver, you should publish the `concurrency` configuration file via the `config:publish` Artisan command and update the `default` option within the file: + +```bash +php artisan config:publish concurrency +``` + ## Deferring Concurrent Tasks From afbf2ca8be70031941591131a9a56571a5a5a8a4 Mon Sep 17 00:00:00 2001 From: Kennedy Tedesco Date: Fri, 13 Sep 2024 13:15:57 -0300 Subject: [PATCH 1834/2609] [11.x] Add dispatching without delay section (#9888) * [11.x] Add dispatching without delay section * formatting * word * formatting --------- Co-authored-by: Taylor Otwell --- queues.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/queues.md b/queues.md index ea99a89c0f8..4c4926add0c 100644 --- a/queues.md +++ b/queues.md @@ -795,6 +795,10 @@ If you would like to specify that a job should not be immediately available for } } +In some cases, jobs may have a default delay configured. If you need to bypass this delay and dispatch a job for immediate processing, you may use the `withoutDelay` method: + + ProcessPodcast::dispatch($podcast)->withoutDelay(); + > [!WARNING] > The Amazon SQS queue service has a maximum delay time of 15 minutes. From 821326045cb8527b3aa79dddbd31eceeebcf8bd6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 15 Sep 2024 15:51:45 -0500 Subject: [PATCH 1835/2609] wip --- helpers.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/helpers.md b/helpers.md index f293cd61e54..0bbc8f7e8c2 100644 --- a/helpers.md +++ b/helpers.md @@ -2401,6 +2401,19 @@ By default, deferred functions will only be executed if the HTTP response, Artis defer(fn () => Metrics::reportOrder($order))->always(); ``` + +#### Deferred Function Compatibility + +If you upgraded to Laravel 11.x from a Laravel 10.x application and your application's skeleton still contains an `app/Http/Kernel.php` file, you should add the `InvokeDeferredCallbacks` middleware to the beginning of the kernel's `$middleware` property: + +```php +protected $middleware = [ + \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, + \App\Http\Middleware\TrustProxies::class, + // ... +]; +``` + ### Lottery From 0a357c22b2c218b70d281caa8e912af17edc630d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 15 Sep 2024 15:52:42 -0500 Subject: [PATCH 1836/2609] wip --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 0bbc8f7e8c2..b3c9a604b6d 100644 --- a/helpers.md +++ b/helpers.md @@ -2408,7 +2408,7 @@ If you upgraded to Laravel 11.x from a Laravel 10.x application and your applica ```php protected $middleware = [ - \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, + \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, // [tl! add] \App\Http\Middleware\TrustProxies::class, // ... ]; From 81330e871dd01be123c39846b8f4a46c023c4476 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 15 Sep 2024 15:58:01 -0500 Subject: [PATCH 1837/2609] wip --- filesystem.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index 6a4e839cd67..0f3c04f78e7 100644 --- a/filesystem.md +++ b/filesystem.md @@ -312,7 +312,7 @@ If you would like to modify the host for URLs generated using the `Storage` faca ### Temporary URLs -Using the `temporaryUrl` method, you may create temporary URLs to files stored using the `s3` driver. This method accepts a path and a `DateTime` instance specifying when the URL should expire: +Using the `temporaryUrl` method, you may create temporary URLs to files stored using the `local` and `s3` drivers. This method accepts a path and a `DateTime` instance specifying when the URL should expire: use Illuminate\Support\Facades\Storage; @@ -320,6 +320,23 @@ Using the `temporaryUrl` method, you may create temporary URLs to files stored u 'file.jpg', now()->addMinutes(5) ); + +#### Enabling Local Temporary URLs + +If you started developing your application before support for temporary URLs was introduced to the `local` driver, you may need to enable local temporary URLs. To do so, add the `serve` option to your `local` disk's configuration array within the `config/filesystems.php` configuration file: + +```php +'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'serve' => true, // [tl! add] + 'throw' => false, +], +``` + + +#### S3 Request Parameters + If you need to specify additional [S3 request parameters](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html#RESTObjectGET-requests), you may pass the array of request parameters as the third argument to the `temporaryUrl` method: $url = Storage::temporaryUrl( @@ -331,6 +348,9 @@ If you need to specify additional [S3 request parameters](https://docs.aws.amazo ] ); + +#### Customizing Temporary URLs + If you need to customize how temporary URLs are created for a specific storage disk, you can use the `buildTemporaryUrlsUsing` method. For example, this can be useful if you have a controller that allows you to download files stored via a disk that doesn't typically support temporary URLs. Usually, this method should be called from the `boot` method of a service provider: Date: Mon, 16 Sep 2024 15:12:38 +0100 Subject: [PATCH 1838/2609] Update when helper example for rendering HTML attributes. (#9898) Josh Cirre DM'd me that he was having an issue with using when() with livewire, and noted that the docs example shows it using `{{` blade tags, where it should be using `{!!` to prevent output escaping. quick fix! --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index b3c9a604b6d..884d9f9ccdb 100644 --- a/helpers.md +++ b/helpers.md @@ -2317,7 +2317,7 @@ The `when` function returns the value it is given if a given condition evaluates The `when` function is primarily useful for conditionally rendering HTML attributes: ```blade -
    +
    ...
    ``` From 45c8abc0c7f914cbd223f207ee44ab417deb8065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rodr=C3=ADguez?= Date: Mon, 16 Sep 2024 16:17:37 +0200 Subject: [PATCH 1839/2609] [11.x] Fix `ThrottlesExceptions` description and adjust `retryUntil()` time limit (#9899) * Fixes incorrect behavior description of the Limiter and adjusts the time limit value to reduce confusion * Improoved redaction for the 30-minute time limit --------- Co-authored-by: Jose Rodriguez --- queues.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index 4c4926add0c..cda1ccedfe8 100644 --- a/queues.md +++ b/queues.md @@ -619,10 +619,10 @@ For example, let's imagine a queued job that interacts with a third-party API th */ public function retryUntil(): DateTime { - return now()->addMinutes(5); + return now()->addMinutes(30); } -The first constructor argument accepted by the middleware is the number of exceptions the job can throw before being throttled, while the second constructor argument is the number of seconds that should elapse before the job is attempted again once it has been throttled. In the code example above, if the job throws 10 exceptions within 5 minutes, we will wait 5 minutes before attempting the job again. +The first constructor argument accepted by the middleware is the number of exceptions the job can throw before being throttled, while the second constructor argument is the number of seconds that should elapse before the job is attempted again once it has been throttled. In the code example above, if the job throws 10 consecutive exceptions, we will wait 5 minutes before attempting the job again, constrained by the 30-minute time limit. When a job throws an exception but the exception threshold has not yet been reached, the job will typically be retried immediately. However, you may specify the number of minutes such a job should be delayed by calling the `backoff` method when attaching the middleware to the job: From aac0a3fa319d0ac84d546957c4ca30ca0ec4c2df Mon Sep 17 00:00:00 2001 From: Hasan Can Boga Date: Mon, 16 Sep 2024 17:30:26 +0300 Subject: [PATCH 1840/2609] Add hint about ConcurrencyServiceProvider for old Laravel structure (#9897) * Add hint about ConcurrencyServiceProvider for older Laravel versions * wip --------- Co-authored-by: Hasancan Boga Co-authored-by: Taylor Otwell --- concurrency.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/concurrency.md b/concurrency.md index d849f6049a1..a5f93276548 100644 --- a/concurrency.md +++ b/concurrency.md @@ -12,6 +12,29 @@ Sometimes you may need to execute several slow tasks which do not depend on one another. In many cases, significant performance improvements can be realized by executing the tasks concurrently. Laravel's `Concurrency` facade provides a simple, convenient API for executing closures concurrently. + +#### Concurrency Compatibility + +If you upgraded to Laravel 11.x from a Laravel 10.x application, you may need to add the `ConcurrencyServiceProvider` to the `providers` array in your application's `config/app.php` configuration file: + +```php +'providers' => ServiceProvider::defaultProviders()->merge([ + /* + * Package Service Providers... + */ + Illuminate\Concurrency\ConcurrencyServiceProvider::class, // [tl! add] + + /* + * Application Service Providers... + */ + App\Providers\AppServiceProvider::class, + App\Providers\AuthServiceProvider::class, + // App\Providers\BroadcastServiceProvider::class, + App\Providers\EventServiceProvider::class, + App\Providers\RouteServiceProvider::class, +])->toArray(), +``` + #### How it Works From 6e7ec33a34c32dd3416823e5960e2c339320c074 Mon Sep 17 00:00:00 2001 From: Peter C <63091190+petoc@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:16:29 +0200 Subject: [PATCH 1841/2609] passport.md - Purging Tokens, updated namespace (#9900) --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 8e06f93ff57..96738eae41b 100644 --- a/passport.md +++ b/passport.md @@ -518,7 +518,7 @@ php artisan passport:purge --expired You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your application's `routes/console.php` file to automatically prune your tokens on a schedule: - use Laravel\Support\Facades\Schedule; + use Illuminate\Support\Facades\Schedule; Schedule::command('passport:purge')->hourly(); From ebbf48e1899a68df4f067292a0e7b0ea2ffba143 Mon Sep 17 00:00:00 2001 From: Wes Hooper Date: Wed, 18 Sep 2024 15:23:10 +0100 Subject: [PATCH 1842/2609] Document fluent string wrap() (#9904) --- strings.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/strings.md b/strings.md index e56975e3fe6..396f45c4c57 100644 --- a/strings.md +++ b/strings.md @@ -228,6 +228,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [whenTest](#method-fluent-str-when-test) [wordCount](#method-fluent-str-word-count) [words](#method-fluent-str-words) +[wrap](#method-fluent-str-wrap)
    @@ -3051,3 +3052,18 @@ The `words` method limits the number of words in a string. If necessary, you may $string = Str::of('Perfectly balanced, as all things should be.')->words(3, ' >>>'); // Perfectly balanced, as >>> + + +#### `wrap` {.collection-method} + +The `wrap` method wraps the given string with an additional string or pair of strings: + + use Illuminate\Support\Str; + + Str::of('Laravel')->wrap('"'); + + // "Laravel" + + Str::is('is')->wrap(before: 'This ', after: ' Laravel!'); + + // This is Laravel! From 358fd26b3a81359b6e5f54d263f50083e82f525b Mon Sep 17 00:00:00 2001 From: Ronny Badilla Date: Wed, 18 Sep 2024 08:41:12 -0600 Subject: [PATCH 1843/2609] [11.x] `helpers.md` - Cancelling deferred functions (#9902) * mention defer forget method * formatting * remove word * wording --------- Co-authored-by: Taylor Otwell --- helpers.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/helpers.md b/helpers.md index 884d9f9ccdb..9a75390696e 100644 --- a/helpers.md +++ b/helpers.md @@ -2401,6 +2401,17 @@ By default, deferred functions will only be executed if the HTTP response, Artis defer(fn () => Metrics::reportOrder($order))->always(); ``` + +#### Cancelling Deferred Functions + +If you need to cancel a deferred function before it is executed, you can use the `forget` method to cancel the function by its name. To name a deferred function, provide a second argument to the `defer` function: + +```php +defer(fn () => Metrics::report(), 'reportMetrics'); + +defer()->forget('reportMetrics'); +``` + #### Deferred Function Compatibility From a71a1ac32316192e8c489268c2fbc61f6bc6f3e7 Mon Sep 17 00:00:00 2001 From: Renato Cardoso Date: Thu, 19 Sep 2024 08:19:06 -0300 Subject: [PATCH 1844/2609] [11.x] Add middleware alias registration example for EnsureUserHasRole middleware (#9906) * [11.x] Add middleware alias registration example for EnsureUserHasRole middleware This commit adds information about registering a middleware alias for the `EnsureUserHasRole` middleware example in the "Middleware Parameters" section of the documentation. The current documentation provides an example of the `EnsureUserHasRole` middleware and how to use it with parameters, but it doesn't mention the necessary step of registering the middleware alias. This addition will help users avoid confusion when implementing the middleware in their applications. Changes made: 1. Added a code snippet demonstrating how to register the `EnsureUserHasRole` middleware alias in the `bootstrap/app.php` file. 2. Placed the new information immediately after the middleware class example and before the route definition examples. * Update middleware.md * Update middleware.md --------- Co-authored-by: Taylor Otwell --- middleware.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/middleware.md b/middleware.md index 9938bfe0aac..b9e3d03b21f 100644 --- a/middleware.md +++ b/middleware.md @@ -394,15 +394,17 @@ Additional middleware parameters will be passed to the middleware after the `$ne Middleware parameters may be specified when defining the route by separating the middleware name and parameters with a `:`: + use App\Http\Middleware\EnsureUserHasRole; + Route::put('/post/{id}', function (string $id) { // ... - })->middleware('role:editor'); + })->middleware(EnsureUserHasRole::class.':editor'); Multiple parameters may be delimited by commas: Route::put('/post/{id}', function (string $id) { // ... - })->middleware('role:editor,publisher'); + })->middleware(EnsureUserHasRole::class.':editor,publisher'); ## Terminable Middleware From 05e97a83ed36b745afd6abd7c307d6689143a580 Mon Sep 17 00:00:00 2001 From: Sarah Gilberg Date: Thu, 19 Sep 2024 07:31:03 -0400 Subject: [PATCH 1845/2609] Document breaking change in password rehashing if custom password field (#9901) * Document breaking change in password rehashing if custom password field Also added missing likelihood-of-impact, which I'm calling "low" since most folks probably use the default 'password' field name. * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/upgrade.md b/upgrade.md index 381f486024c..209df7bd94c 100644 --- a/upgrade.md +++ b/upgrade.md @@ -126,9 +126,15 @@ However, we do **not recommend** that Laravel 10 applications upgrading to Larav #### Password Rehashing -Laravel 11 will automatically rehash your user's passwords during authentication if your hashing algorithm's "work factor" has been updated since the password was last hashed. +**Likelihood Of Impact: Low** + +Laravel 11 will automatically rehash your user's passwords during authentication if your hashing algorithm's "work factor" has been updated since the password was last hashed. + +Typically, this should not disrupt your application; however, if your `User` model's "password" field has a name other than `password`, you should specify the field's name via the model's `authPasswordName` property: + + protected $authPasswordName = 'custom_password_field'; -Typically, this should not disrupt your application; however, you may disable this behavior by adding the `rehash_on_login` option to your application's `config/hashing.php` configuration file: +Alternatively, you may disable password rehashing by adding the `rehash_on_login` option to your application's `config/hashing.php` configuration file: 'rehash_on_login' => false, From ab75377e9b8fff24a3f4715895a82087513c5fcf Mon Sep 17 00:00:00 2001 From: "Pankaj M. Aagjal" Date: Fri, 20 Sep 2024 14:51:35 +0200 Subject: [PATCH 1846/2609] fix: active version mentioned in contributions.md (#9907) --- contributions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributions.md b/contributions.md index e390461c54d..5e96e643fb8 100644 --- a/contributions.md +++ b/contributions.md @@ -80,7 +80,7 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest version that supports bug fixes (currently `10.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `11.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. **Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `11.x`). From 38fa9c370f19c1bfad16eb1b9e83884f94cf526a Mon Sep 17 00:00:00 2001 From: Seweryn Czabanowski <68080844+PaperTurtle@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:03:09 +0200 Subject: [PATCH 1847/2609] Add Laravel Breeze link (#9912) --- contributions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributions.md b/contributions.md index 5e96e643fb8..ce9668a2c47 100644 --- a/contributions.md +++ b/contributions.md @@ -28,6 +28,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Application](https://github.com/laravel/laravel) - [Laravel Art](https://github.com/laravel/art) +- [Laravel Breeze](https://github.com/laravel/breeze) - [Laravel Documentation](https://github.com/laravel/docs) - [Laravel Dusk](https://github.com/laravel/dusk) - [Laravel Cashier Stripe](https://github.com/laravel/cashier) From b744d060e795703c95a56576c63011f68101020e Mon Sep 17 00:00:00 2001 From: Sajad Torkamani Date: Sun, 22 Sep 2024 16:05:29 +0100 Subject: [PATCH 1848/2609] fix links to Mailgun docs (#9910) --- mail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail.md b/mail.md index df469d22326..2798dad7bfc 100644 --- a/mail.md +++ b/mail.md @@ -689,7 +689,7 @@ Some third-party email providers such as Mailgun and Postmark support message "t ); } -If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) and [metadata](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). +If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/docs/mailgun/user-manual/tracking-messages/#tagging) and [metadata](https://documentation.mailgun.com/docs/mailgun/user-manual/tracking-messages/#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). If your application is using Amazon SES to send emails, you should use the `metadata` method to attach [SES "tags"](https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html) to the message. From d41ad218afaa2cf009e8c47411f3405ef208fe42 Mon Sep 17 00:00:00 2001 From: gisu nasrollahi <113020788+gisuNasr@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:43:04 +0330 Subject: [PATCH 1849/2609] noActionOnDelete() added to migrations.md (#9908) * add noActionOnDelete() to migrations.md * Update migrations.md --------- Co-authored-by: Taylor Otwell --- migrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/migrations.md b/migrations.md index f86f2cab482..a22bc3e5c25 100644 --- a/migrations.md +++ b/migrations.md @@ -1194,6 +1194,7 @@ An alternative, expressive syntax is also provided for these actions: | `$table->cascadeOnDelete();` | Deletes should cascade. | | `$table->restrictOnDelete();` | Deletes should be restricted. | | `$table->nullOnDelete();` | Deletes should set the foreign key value to null. | +| `$table->noActionOnDelete();` | Prevents deletes if child records exist. |
    From dc8015a44281e30a45bd98de4a5830b56d002392 Mon Sep 17 00:00:00 2001 From: inmanturbo <47095624+inmanturbo@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:33:53 -0400 Subject: [PATCH 1850/2609] fix incompatible return type (#9909) * fix incompatible return type * Include full doc block like in the factory example * Update eloquent-factories.md * Update eloquent-factories.md --------- Co-authored-by: Taylor Otwell --- eloquent-factories.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/eloquent-factories.md b/eloquent-factories.md index 2c852a7f7ba..9baefc19b37 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -95,13 +95,12 @@ Once you have defined your factories, you may use the static `factory` method pr The `HasFactory` trait's `factory` method will use conventions to determine the proper factory for the model the trait is assigned to. Specifically, the method will look for a factory in the `Database\Factories` namespace that has a class name matching the model name and is suffixed with `Factory`. If these conventions do not apply to your particular application or factory, you may overwrite the `newFactory` method on your model to return an instance of the model's corresponding factory directly: - use Illuminate\Database\Eloquent\Factories\Factory; use Database\Factories\Administration\FlightFactory; /** * Create a new factory instance for the model. */ - protected static function newFactory(): Factory + protected static function newFactory() { return FlightFactory::new(); } From f54c269ce77ea3fff2f36f325a8d82b3a42bc23c Mon Sep 17 00:00:00 2001 From: Mohammed Omer Date: Tue, 24 Sep 2024 18:41:26 +0400 Subject: [PATCH 1851/2609] [11.x] Clarify event subscriber registration (#9913) * Clarify event subscriber registration * Update events.md * Update events.md --------- Co-authored-by: Taylor Otwell --- events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.md b/events.md index ebe2934bddb..93c7b8cd69d 100644 --- a/events.md +++ b/events.md @@ -631,7 +631,7 @@ If your event listener methods are defined within the subscriber itself, you may ### Registering Event Subscribers -After writing the subscriber, you are ready to register it with the event dispatcher. You may register subscribers using the `subscribe` method of the `Event` facade. Typically, this should be done within the `boot` method of your application's `AppServiceProvider`: +After writing the subscriber, Laravel will automatically register handler methods within the subscriber if they follow Laravel's [event discovery conventions](#event-discovery). Otherwise, you may manually register your subscriber using the `subscribe` method of the `Event` facade. Typically, this should be done within the `boot` method of your application's `AppServiceProvider`: Date: Wed, 25 Sep 2024 12:02:32 -0400 Subject: [PATCH 1852/2609] DynamoDB Cache TTL (#9916) * DynamoDB Cache TTL * formatting --------- Co-authored-by: Taylor Otwell --- cache.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cache.md b/cache.md index 0eced2f74d1..56bc99d5a10 100644 --- a/cache.md +++ b/cache.md @@ -90,6 +90,8 @@ Before using the [DynamoDB](https://aws.amazon.com/dynamodb) cache driver, you m This table should also have a string partition key with a name that corresponds to the value of the `stores.dynamodb.attributes.key` configuration item within your application's `cache` configuration file. By default, the partition key should be named `key`. +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: ```shell From 1ba0d0395b54440fb2d706b676aeb1828d4731fc Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 26 Sep 2024 21:56:27 +1000 Subject: [PATCH 1853/2609] [11.x] Document namespaced function (#9915) * Document namespaced function * reference namespaced function in prose --- helpers.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/helpers.md b/helpers.md index 9a75390696e..097f1ff83ea 100644 --- a/helpers.md +++ b/helpers.md @@ -2379,12 +2379,13 @@ For a thorough discussion of Carbon and its features, please consult the [offici While Laravel's [queued jobs](/docs/{{version}}/queues) allow you to queue tasks for background processing, sometimes you may have simple tasks you would like to defer without configuring or maintaining a long-running queue worker. -Deferred functions allow you to defer the execution of a closure until after the HTTP response has been sent to the user, keeping your application feeling fast and responsive. To defer the execution of a closure, simply pass the closure to the `defer` function: +Deferred functions allow you to defer the execution of a closure until after the HTTP response has been sent to the user, keeping your application feeling fast and responsive. To defer the execution of a closure, simply pass the closure to the `Illuminate\Support\defer` function: ```php use App\Services\Metrics; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; +use function Illuminate\Support\defer; Route::post('/orders', function (Request $request) { // Create order... @@ -2395,7 +2396,7 @@ Route::post('/orders', function (Request $request) { }); ``` -By default, deferred functions will only be executed if the HTTP response, Artisan command, or queued job from which `defer` is invoked completes successfully. This means that deferred functions will not be executed if a request results in a `4xx` or `5xx` HTTP response. If you would like a deferred function to always execute, you may chain the `always` method onto your deferred function: +By default, deferred functions will only be executed if the HTTP response, Artisan command, or queued job from which `Illuminate\Support\defer` is invoked completes successfully. This means that deferred functions will not be executed if a request results in a `4xx` or `5xx` HTTP response. If you would like a deferred function to always execute, you may chain the `always` method onto your deferred function: ```php defer(fn () => Metrics::reportOrder($order))->always(); @@ -2404,7 +2405,7 @@ defer(fn () => Metrics::reportOrder($order))->always(); #### Cancelling Deferred Functions -If you need to cancel a deferred function before it is executed, you can use the `forget` method to cancel the function by its name. To name a deferred function, provide a second argument to the `defer` function: +If you need to cancel a deferred function before it is executed, you can use the `forget` method to cancel the function by its name. To name a deferred function, provide a second argument to the `Illuminate\Support\defer` function: ```php defer(fn () => Metrics::report(), 'reportMetrics'); From 2d2cfae706c9367ebf19e13175dc1de2711cad7a Mon Sep 17 00:00:00 2001 From: Arco Voets <142106975+ArcoVoets@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:24:05 +0200 Subject: [PATCH 1854/2609] Update pulse.md (#9919) Fix: User requests are about URLs, so ignore ignores URLs and not jobs --- pulse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pulse.md b/pulse.md index 4020822192b..4cabaa86b5a 100644 --- a/pulse.md +++ b/pulse.md @@ -426,7 +426,7 @@ You may optionally adjust the [sample rate](#sampling) and ignored job patterns. The `UserRequests` recorder captures information about the users making requests to your application for display on the [Application Usage](#application-usage-card) card. -You may optionally adjust the [sample rate](#sampling) and ignored job patterns. +You may optionally adjust the [sample rate](#sampling) and ignored URL patterns. ### Filtering From f04003eccc61f9f05e291305d66f73cd10dbb1da Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 1 Oct 2024 16:06:44 -0500 Subject: [PATCH 1855/2609] wip --- pennant.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index 1d8e04a2411..c0783ae1b3f 100644 --- a/pennant.md +++ b/pennant.md @@ -364,7 +364,7 @@ $user->features()->unless('new-api', ### Blade Directive -To make checking features in Blade a seamless experience, Pennant offers a `@feature` directive: +To make checking features in Blade a seamless experience, Pennant offers the `@feature` and `@featureany` directive: ```blade @feature('site-redesign') @@ -372,6 +372,10 @@ To make checking features in Blade a seamless experience, Pennant offers a `@fea @else @endfeature + +@featureany(['site-redesign', 'beta']) + +@endfeatureany ``` From f891b178c8ceb9b7aa6771785a9805adac1e7e51 Mon Sep 17 00:00:00 2001 From: KentarouTakeda Date: Wed, 2 Oct 2024 23:40:05 +0900 Subject: [PATCH 1856/2609] fix: releases.md -Unify presence/absence of comma (#9924) * fix: releases.md -Unify presence/absence of comma * Update releases.md --------- Co-authored-by: Taylor Otwell --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 692cbfb16f3..894f97c5123 100644 --- a/releases.md +++ b/releases.md @@ -28,7 +28,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 - 8.3 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | -| 12 | 8.2 - 8.3 | Q1 2025 | Q3, 2026 | Q1, 2027 | +| 12 | 8.2 - 8.3 | Q1 2025 | Q3 2026 | Q1 2027 |
    From 70abbf2ea5625c6f6d6aab6c88d2dd19d6b0c623 Mon Sep 17 00:00:00 2001 From: Lijahide <49410375+Lijahide@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:43:17 -0400 Subject: [PATCH 1857/2609] changed "one-to-many" header (#9922) added " / Has Many" to the "One to Many" header and corresponding TOC line. --- eloquent-relationships.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 33692568ec3..cbaf8bbfbb0 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Defining Relationships](#defining-relationships) - [One to One](#one-to-one) - - [One to Many](#one-to-many) + - [One to Many / Has Many](#one-to-many) - [One to Many (Inverse) / Belongs To](#one-to-many-inverse) - [Has One of Many](#has-one-of-many) - [Has One Through](#has-one-through) @@ -148,7 +148,7 @@ If the parent model does not use `id` as its primary key, or you wish to find th } -### One to Many +### One to Many / Has Many A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models. For example, a blog post may have an infinite number of comments. Like all other Eloquent relationships, one-to-many relationships are defined by defining a method on your Eloquent model: From b967f72817e992d526405d0712b606aa164b9efb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 2 Oct 2024 09:43:45 -0500 Subject: [PATCH 1858/2609] wip --- eloquent-relationships.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index cbaf8bbfbb0..37f88cbf926 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -2,7 +2,7 @@ - [Introduction](#introduction) - [Defining Relationships](#defining-relationships) - - [One to One](#one-to-one) + - [One to One / Has One](#one-to-one) - [One to Many / Has Many](#one-to-many) - [One to Many (Inverse) / Belongs To](#one-to-many-inverse) - [Has One of Many](#has-one-of-many) @@ -68,7 +68,7 @@ Eloquent relationships are defined as methods on your Eloquent model classes. Si But, before diving too deep into using relationships, let's learn how to define each type of relationship supported by Eloquent. -### One to One +### One to One / Has One A one-to-one relationship is a very basic type of database relationship. For example, a `User` model might be associated with one `Phone` model. To define this relationship, we will place a `phone` method on the `User` model. The `phone` method should call the `hasOne` method and return its result. The `hasOne` method is available to your model via the model's `Illuminate\Database\Eloquent\Model` base class: From 935b090bdbe1b8616240382087ac19f49bd8b95f Mon Sep 17 00:00:00 2001 From: andzandz Date: Wed, 2 Oct 2024 15:55:39 +0100 Subject: [PATCH 1859/2609] Add note about logically grouping "or" queries for chunkById and lazyById (#9921) * Update queries.md * Update eloquent.md * Update eloquent.md * Update queries.md --------- Co-authored-by: Taylor Otwell --- eloquent.md | 13 +++++++++++++ queries.md | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/eloquent.md b/eloquent.md index 14383122503..5739d808792 100644 --- a/eloquent.md +++ b/eloquent.md @@ -482,6 +482,19 @@ Flight::where('departed', true) }, $column = 'id'); ``` +Since the `chunkById` and `lazyById` methods add their own "where" conditions to the query being executed, you should typically [logically group](/docs/{{version}}/queries#logical-grouping) your own conditions within a closure: + +```php +Flight::where(function ($query) { + $query->where('delayed', true)->orWhere('cancelled', true); +})->chunkById(200, function (Collection $flights) { + $flights->each->update([ + 'departed' => false, + 'cancelled' => true + ]); +}, column: 'id'); +``` + ### Chunking Using Lazy Collections diff --git a/queries.md b/queries.md index 4c01fe44a19..a77466e732a 100644 --- a/queries.md +++ b/queries.md @@ -161,6 +161,20 @@ If you are updating database records while chunking results, your chunk results } }); +Since the `chunkById` and `lazyById` methods add their own "where" conditions to the query being executed, you should typically [logically group](#logical-grouping) your own conditions within a closure: + +```php +DB::table('users')->where(function ($query) { + $query->where('credits', 1)->orWhere('credits', 2); +})->chunkById(100, function (Collection $users) { + foreach ($users as $user) { + DB::table('users') + ->where('id', $user->id) + ->update(['credits' => 3]); + } +}); +``` + > [!WARNING] > When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results. From 45969e396c5ec52aca9a558a43e9f902759e3d41 Mon Sep 17 00:00:00 2001 From: Matthew Frieswyk Date: Wed, 2 Oct 2024 12:37:16 -0400 Subject: [PATCH 1860/2609] Document horizon:supervisor-status (#9926) * Document horizon:supervisor-status * Update horizon.md --------- Co-authored-by: Taylor Otwell --- horizon.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/horizon.md b/horizon.md index d1d8405e339..fc2a090c627 100644 --- a/horizon.md +++ b/horizon.md @@ -230,6 +230,12 @@ You may check the current status of the Horizon process using the `horizon:statu php artisan horizon:status ``` +You may check the current status of a specific Horizon [supervisor](#supervisors) using the `horizon:supervisor-status` Artisan command: + +```shell +php artisan horizon:supervisor-status supervisor-1 +``` + You may gracefully terminate the Horizon process using the `horizon:terminate` Artisan command. Any jobs that are currently being processed will be completed and then Horizon will stop executing: ```shell From ead3a5b4b11c267f20ee1065eb3f9aa98fc986e1 Mon Sep 17 00:00:00 2001 From: Jonathan Goode Date: Thu, 3 Oct 2024 18:30:47 +0100 Subject: [PATCH 1861/2609] Wording (#9929) --- blade.md | 2 +- cache.md | 2 +- controllers.md | 2 +- eloquent.md | 2 +- encryption.md | 4 ++-- packages.md | 2 +- queries.md | 4 ++-- queues.md | 2 +- routing.md | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/blade.md b/blade.md index d1a15aab66b..fb9193d6250 100644 --- a/blade.md +++ b/blade.md @@ -1435,7 +1435,7 @@ Because the `color` prop was only passed into the parent (``), it won't ``` > [!WARNING] -> The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. +> The `@aware` directive cannot access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component cannot be accessed by the `@aware` directive. ### Anonymous Component Paths diff --git a/cache.md b/cache.md index 56bc99d5a10..e80e3a0a0f7 100644 --- a/cache.md +++ b/cache.md @@ -327,7 +327,7 @@ The `get` method also accepts a closure. After the closure is executed, Laravel // Lock acquired for 10 seconds and automatically released... }); -If the lock is not available at the moment you request it, you may instruct Laravel to wait for a specified number of seconds. If the lock can not be acquired within the specified time limit, an `Illuminate\Contracts\Cache\LockTimeoutException` will be thrown: +If the lock is not available at the moment you request it, you may instruct Laravel to wait for a specified number of seconds. If the lock cannot be acquired within the specified time limit, an `Illuminate\Contracts\Cache\LockTimeoutException` will be thrown: use Illuminate\Contracts\Cache\LockTimeoutException; diff --git a/controllers.md b/controllers.md index 30ad8127d72..c2c22c7bffa 100644 --- a/controllers.md +++ b/controllers.md @@ -198,7 +198,7 @@ You may even register many resource controllers at once by passing an array to t #### Customizing Missing Model Behavior -Typically, a 404 HTTP response will be generated if an implicitly bound resource model is not found. However, you may customize this behavior by calling the `missing` method when defining your resource route. The `missing` method accepts a closure that will be invoked if an implicitly bound model can not be found for any of the resource's routes: +Typically, a 404 HTTP response will be generated if an implicitly bound resource model is not found. However, you may customize this behavior by calling the `missing` method when defining your resource route. The `missing` method accepts a closure that will be invoked if an implicitly bound model cannot be found for any of the resource's routes: use App\Http\Controllers\PhotoController; use Illuminate\Http\Request; diff --git a/eloquent.md b/eloquent.md index 5739d808792..776029bc98b 100644 --- a/eloquent.md +++ b/eloquent.md @@ -631,7 +631,7 @@ If the `ModelNotFoundException` is not caught, a 404 HTTP response is automatica ### Retrieving or Creating Models -The `firstOrCreate` method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the attributes resulting from merging the first array argument with the optional second array argument: +The `firstOrCreate` method will attempt to locate a database record using the given column / value pairs. If the model cannot be found in the database, a record will be inserted with the attributes resulting from merging the first array argument with the optional second array argument: The `firstOrNew` method, like `firstOrCreate`, will attempt to locate a record in the database matching the given attributes. However, if a model is not found, a new model instance will be returned. Note that the model returned by `firstOrNew` has not yet been persisted to the database. You will need to manually call the `save` method to persist it: diff --git a/encryption.md b/encryption.md index 02a07927207..49beb8cd179 100644 --- a/encryption.md +++ b/encryption.md @@ -8,7 +8,7 @@ ## Introduction -Laravel's encryption services provide a simple, convenient interface for encrypting and decrypting text via OpenSSL using AES-256 and AES-128 encryption. All of Laravel's encrypted values are signed using a message authentication code (MAC) so that their underlying value can not be modified or tampered with once encrypted. +Laravel's encryption services provide a simple, convenient interface for encrypting and decrypting text via OpenSSL using AES-256 and AES-128 encryption. All of Laravel's encrypted values are signed using a message authentication code (MAC) so that their underlying value cannot be modified or tampered with once encrypted. ## Configuration @@ -65,7 +65,7 @@ You may encrypt a value using the `encryptString` method provided by the `Crypt` #### Decrypting a Value -You may decrypt values using the `decryptString` method provided by the `Crypt` facade. If the value can not be properly decrypted, such as when the message authentication code is invalid, an `Illuminate\Contracts\Encryption\DecryptException` will be thrown: +You may decrypt values using the `decryptString` method provided by the `Crypt` facade. If the value cannot be properly decrypted, such as when the message authentication code is invalid, an `Illuminate\Contracts\Encryption\DecryptException` will be thrown: use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Facades\Crypt; diff --git a/packages.md b/packages.md index 4999c4e343d..e43ef7a0bde 100644 --- a/packages.md +++ b/packages.md @@ -107,7 +107,7 @@ Now, when users of your package execute Laravel's `vendor:publish` command, your $value = config('courier.option'); > [!WARNING] -> You should not define closures in your configuration files. They can not be serialized correctly when users execute the `config:cache` Artisan command. +> You should not define closures in your configuration files. They cannot be serialized correctly when users execute the `config:cache` Artisan command. #### Default Package Configuration diff --git a/queries.md b/queries.md index a77466e732a..d437ab81f40 100644 --- a/queries.md +++ b/queries.md @@ -276,7 +276,7 @@ Sometimes you may need to insert an arbitrary string into a query. To create a r ### Raw Methods -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.** +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 cannot guarantee that any query using raw expressions is protected against SQL injection vulnerabilities.** #### `selectRaw` @@ -1096,7 +1096,7 @@ In addition to inserting records into the database, the query builder can also u 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 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 cannot be found, a new record will be inserted with the merged attributes of both arguments: DB::table('users') ->updateOrInsert( diff --git a/queues.md b/queues.md index cda1ccedfe8..b6dfffd8c1c 100644 --- a/queues.md +++ b/queues.md @@ -1649,7 +1649,7 @@ If you defined your DynamoDB table with a `ttl` attribute, you may define config ## 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 content is cryptographically signed so that 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 cannot be modified in transit: $podcast = App\Podcast::find(1); diff --git a/routing.md b/routing.md index 0821accd1f2..b77e6f67fd0 100644 --- a/routing.md +++ b/routing.md @@ -613,7 +613,7 @@ Similarly, you may explicitly instruct Laravel to not scope bindings by invoking #### Customizing Missing Model Behavior -Typically, a 404 HTTP response will be generated if an implicitly bound model is not found. However, you may customize this behavior by calling the `missing` method when defining your route. The `missing` method accepts a closure that will be invoked if an implicitly bound model can not be found: +Typically, a 404 HTTP response will be generated if an implicitly bound model is not found. However, you may customize this behavior by calling the `missing` method when defining your route. The `missing` method accepts a closure that will be invoked if an implicitly bound model cannot be found: use App\Http\Controllers\LocationsController; use Illuminate\Http\Request; From 77db50ec005aa0e6391511eb81ff3a0e21d7e504 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Fri, 4 Oct 2024 03:33:25 +1000 Subject: [PATCH 1862/2609] [11.x] Prompts: Warn about inadvertently returning associative arrays after filtering (#9928) * Prompts: Warn about inadvertently returning associative arrays after filtering * Update prompts.md --------- Co-authored-by: Taylor Otwell --- prompts.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/prompts.md b/prompts.md index 5f8f49aeb1f..441f5c6a1b6 100644 --- a/prompts.md +++ b/prompts.md @@ -571,6 +571,20 @@ $id = search( The closure will receive the text that has been typed by the user so far and must return an array of options. If you return an associative array then the selected option's key will be returned, otherwise its value will be returned instead. +When filtering an array where you intend to return the value, you should use the `array_values` function or the `values` Collection method to ensure the array doesn't become associative: + +```php +$names = collect(['Taylor', 'Abigail']); + +$selected = search( + label: 'Search for the user that should receive the mail', + options: fn (string $value) => $names + ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true)) + ->values() + ->all(), +); +``` + You may also include placeholder text and an informational hint: ```php @@ -637,6 +651,20 @@ $ids = multisearch( The closure will receive the text that has been typed by the user so far and must return an array of options. If you return an associative array then the selected options' keys will be returned; otherwise, their values will be returned instead. +When filtering an array where you intend to return the value, you should use the `array_values` function or the `values` Collection method to ensure the array doesn't become associative: + +```php +$names = collect(['Taylor', 'Abigail']); + +$selected = multisearch( + label: 'Search for the users that should receive the mail', + options: fn (string $value) => $names + ->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true)) + ->values() + ->all(), +); +``` + You may also include placeholder text and an informational hint: ```php From 5f7df11b007c4c775ca1870a1008b47b1cfc9494 Mon Sep 17 00:00:00 2001 From: Michael Stephen Date: Mon, 7 Oct 2024 21:02:47 +0700 Subject: [PATCH 1863/2609] added missing "whereMorphedTo" on Eloquent relationship page (#9931) * added missing "whereMorphedTo" on Eloquent relationship page from [\#53041](https://github.com/laravel/framework/pull/53041), it turns out there's Polymorphic equivalent to `whereBelongsTo()` that is not mentioned anywhere in the docs. * Updated description Changed the wording to follow general Laravel documentation wording and updated the example to match. * Update eloquent-relationships.md --------- Co-authored-by: Taylor Otwell --- eloquent-relationships.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 37f88cbf926..caea2bb0645 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1502,6 +1502,12 @@ You may occasionally need to add query constraints based on the "type" of the re } )->get(); +Sometimes you may want to query for the children of a "morph to" relationship's parent. You may accomplish this using the `whereMorphedTo` and `whereNotMorphedTo` methods, which will automatically determine the proper morph type mapping for the given model. These methods accept the name of the `morphTo` relationship as their first argument and the related parent model as their second argument: + + $comments = Comment::whereMorphedTo('commentable', $post) + ->orWhereMorphedTo('commentable', $video) + ->get(); + #### Querying All Related Models From 44f2375021852c0b1f0c9b6d3316436d3fc590d3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Oct 2024 11:34:08 -0500 Subject: [PATCH 1864/2609] document php nwe --- installation.md | 60 +++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/installation.md b/installation.md index 227e2ee1c99..6009d6b1ac4 100644 --- a/installation.md +++ b/installation.md @@ -2,7 +2,9 @@ - [Meet Laravel](#meet-laravel) - [Why Laravel?](#why-laravel) -- [Creating a Laravel Project](#creating-a-laravel-project) +- [Creating a Laravel Application](#creating-a-laravel-project) + - [Installing PHP and the Laravel Installer](#installing-php) + - [Creating an Application](#creating-an-application) - [Initial Configuration](#initial-configuration) - [Environment Based Configuration](#environment-based-configuration) - [Databases and Migrations](#databases-and-migrations) @@ -54,25 +56,39 @@ Need extreme scaling? Platforms like [Laravel Vapor](https://vapor.laravel.com) Laravel combines the best packages in the PHP ecosystem to offer the most robust and developer friendly framework available. In addition, thousands of talented developers from around the world have [contributed to the framework](https://github.com/laravel/framework). Who knows, maybe you'll even become a Laravel contributor. -## Creating a Laravel Project +## Creating a Laravel Application -Before creating your first Laravel project, make sure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS or Windows, PHP, Composer, Node and NPM can be installed in minutes via [Laravel Herd](#local-installation-using-herd). + +### Installing PHP and the Laravel Installer -After you have installed PHP and Composer, you may create a new Laravel project via Composer's `create-project` command: +Before creating your first Laravel application, make sure that your local machine has [PHP](https://php.net) and [Composer](https://getcomposer.org) installed. In addition, you should install [Node and NPM](https://nodejs.org). -```nothing -composer create-project laravel/laravel example-app +If you don't have PHP and Composer installed on your local machine, the following commands will install PHP, Composer, and the Laravel installer on macOS, Windows, or Linux: + +```shell tab=macOS +/bin/bash -c "$(curl -fsSL https://php.new/install/mac)" ``` -Or, you may create new Laravel projects by globally installing [the Laravel installer](https://github.com/laravel/installer) via Composer. The Laravel installer allows you to select your preferred testing framework, database, and starter kit when creating new applications: +```shell tab=Windows Powershell +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows')) +``` -```nothing -composer global require laravel/installer +```shell tab=Linux +/bin/bash -c "$(curl -fsSL https://php.new/install/linux)" +``` +After running one of the commands above, you should restart your terminal session. To update PHP, Composer, and the Laravel installer after installing them via `php.new`, you can re-run the command in your terminal. + + +### Creating an Application + +After you have installed PHP, Composer, and the Laravel installer, you may create a new Laravel application. The Laravel installer allows you to select your preferred testing framework, database, and starter kit when creating new applications: + +```nothing laravel new example-app ``` -Once the project has been created, start Laravel's local development server using Laravel Artisan's `serve` command: +Once the application has been created, start Laravel's local development server using Laravel Artisan's `serve` command: ```nothing cd example-app @@ -107,7 +123,7 @@ Your `.env` file should not be committed to your application's source control, s Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with a SQLite database. -During the creation of the project, Laravel created a `database/database.sqlite` file for you, and ran the necessary migrations to create the application's database tables. +During the creation of the application, Laravel created a `database/database.sqlite` file for you, and ran the necessary migrations to create the application's database tables. If you prefer to use another database driver such as MySQL or PostgreSQL, you can update your `.env` configuration file to use the appropriate database. For example, if you wish to use MySQL, update your `.env` configuration file's `DB_*` variables like so: @@ -151,7 +167,7 @@ If you develop on macOS, you can download the Herd installer from the [Herd webs Herd for macOS uses [dnsmasq](https://en.wikipedia.org/wiki/Dnsmasq) to support "parked" directories. Any Laravel application in a parked directory will automatically be served by Herd. By default, Herd creates a parked directory at `~/Herd` and you can access any Laravel application in this directory on the `.test` domain using its directory name. -After installing Herd, the fastest way to create a new Laravel project is using the Laravel CLI, which is bundled with Herd: +After installing Herd, the fastest way to create a new Laravel application is using the Laravel CLI, which is bundled with Herd: ```nothing cd ~/Herd @@ -173,7 +189,7 @@ The Herd UI is accessible by left-clicking on Herd's system tray icon. A right-c During installation, Herd creates a "parked" directory in your home directory at `%USERPROFILE%\Herd`. Any Laravel application in a parked directory will automatically be served by Herd, and you can access any Laravel application in this directory on the `.test` domain using its directory name. -After installing Herd, the fastest way to create a new Laravel project is using the Laravel CLI, which is bundled with Herd. To get started, open Powershell and run the following commands: +After installing Herd, the fastest way to create a new Laravel application is using the Laravel CLI, which is bundled with Herd. To get started, open Powershell and run the following commands: ```nothing cd ~\Herd @@ -187,7 +203,7 @@ You can learn more about Herd by checking out the [Herd documentation for Window ## Docker Installation Using Sail -We want it to be as easy as possible to get started with Laravel regardless of your preferred operating system. So, there are a variety of options for developing and running a Laravel project on your local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel project using [Docker](https://www.docker.com). +We want it to be as easy as possible to get started with Laravel regardless of your preferred operating system. So, there are a variety of options for developing and running a Laravel application on your local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel application using [Docker](https://www.docker.com). Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local machine's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your local machine. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). @@ -199,7 +215,7 @@ Laravel Sail is a light-weight command-line interface for interacting with Larav ### Sail on macOS -If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: +If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is already installed, you can use a simple terminal command to create a new Laravel application. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: ```shell curl -s "https://laravel.build/example-app" | bash @@ -209,7 +225,7 @@ Of course, you can change "example-app" in this URL to anything you like - just Sail installation may take several minutes while Sail's application containers are built on your local machine. -After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: +After the application has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```shell cd example-app @@ -236,7 +252,7 @@ Before we create a new Laravel application on your Windows machine, make sure to > [!NOTE] > After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). -Next, you are ready to create your first Laravel project. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: +Next, you are ready to create your first Laravel application. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel application. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: ```shell curl -s https://laravel.build/example-app | bash @@ -246,7 +262,7 @@ Of course, you can change "example-app" in this URL to anything you like - just Sail installation may take several minutes while Sail's application containers are built on your local machine. -After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: +After the application has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```shell cd example-app @@ -269,12 +285,12 @@ Finally, you can access the application in your web browser at: http://localhost Of course, you will need to be able to modify the Laravel application files that were created within your WSL2 installation. To accomplish this, we recommend using Microsoft's [Visual Studio Code](https://code.visualstudio.com) editor and their first-party extension for [Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack). -Once these tools are installed, you may open any Laravel project by executing the `code .` command from your application's root directory using Windows Terminal. +Once these tools are installed, you may open any Laravel application by executing the `code .` command from your application's root directory using Windows Terminal. ### Sail on Linux -If you're developing on Linux and [Docker Compose](https://docs.docker.com/compose/install/) is already installed, you can use a simple terminal command to create a new Laravel project. +If you're developing on Linux and [Docker Compose](https://docs.docker.com/compose/install/) is already installed, you can use a simple terminal command to create a new Laravel application. First, if you are using Docker Desktop for Linux, you should execute the following command. If you are not using Docker Desktop for Linux, you may skip this step: @@ -292,7 +308,7 @@ Of course, you can change "example-app" in this URL to anything you like - just Sail installation may take several minutes while Sail's application containers are built on your local machine. -After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: +After the application has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```shell cd example-app @@ -338,7 +354,7 @@ In addition, the community maintained [Laravel Idea](https://laravel-idea.com/) ## Next Steps -Now that you have created your Laravel project, you may be wondering what to learn next. First, we strongly recommend becoming familiar with how Laravel works by reading the following documentation: +Now that you have created your Laravel application, you may be wondering what to learn next. First, we strongly recommend becoming familiar with how Laravel works by reading the following documentation:
    From 0a9edb1107b6110c9aa109f8d5b3104ea4ea3a91 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Oct 2024 11:37:10 -0500 Subject: [PATCH 1865/2609] wip --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 6009d6b1ac4..eed208cb75d 100644 --- a/installation.md +++ b/installation.md @@ -82,7 +82,7 @@ After running one of the commands above, you should restart your terminal sessio ### Creating an Application -After you have installed PHP, Composer, and the Laravel installer, you may create a new Laravel application. The Laravel installer allows you to select your preferred testing framework, database, and starter kit when creating new applications: +After you have installed PHP, Composer, and the Laravel installer, you're ready to create a new Laravel application. The Laravel installer will prompt you to select your preferred testing framework, database, and starter kit: ```nothing laravel new example-app From 8e3d7df4f888db955cb6409758433d90fa595c4b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Oct 2024 11:37:31 -0500 Subject: [PATCH 1866/2609] wip --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index eed208cb75d..617378affb0 100644 --- a/installation.md +++ b/installation.md @@ -88,7 +88,7 @@ After you have installed PHP, Composer, and the Laravel installer, you're ready laravel new example-app ``` -Once the application has been created, start Laravel's local development server using Laravel Artisan's `serve` command: +Once the application has been created, start Laravel's local development server using the `serve` command: ```nothing cd example-app From 49c9b6b7207485055c5236935423fc1b6d7e07eb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Oct 2024 11:39:18 -0500 Subject: [PATCH 1867/2609] wip --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 617378affb0..223da81060c 100644 --- a/installation.md +++ b/installation.md @@ -61,7 +61,7 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ### Installing PHP and the Laravel Installer -Before creating your first Laravel application, make sure that your local machine has [PHP](https://php.net) and [Composer](https://getcomposer.org) installed. In addition, you should install [Node and NPM](https://nodejs.org). +Before creating your first Laravel application, make sure that your local machine has [PHP](https://php.net) and [Composer](https://getcomposer.org) installed. In addition, you should install [Node and NPM](https://nodejs.org) so that you can compile your application's frontend assets. If you don't have PHP and Composer installed on your local machine, the following commands will install PHP, Composer, and the Laravel installer on macOS, Windows, or Linux: From 49e3020bb599d19eba0f9583c0d4da2b2bceca4d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 8 Oct 2024 12:05:47 -0500 Subject: [PATCH 1868/2609] wip --- installation.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 223da81060c..ab6508af262 100644 --- a/installation.md +++ b/installation.md @@ -69,7 +69,7 @@ If you don't have PHP and Composer installed on your local machine, the followin /bin/bash -c "$(curl -fsSL https://php.new/install/mac)" ``` -```shell tab=Windows Powershell +```shell tab=Windows PowerShell Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows')) ``` @@ -79,6 +79,9 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage After running one of the commands above, you should restart your terminal session. To update PHP, Composer, and the Laravel installer after installing them via `php.new`, you can re-run the command in your terminal. +> [!NOTE] +> For a fully-featured, graphical PHP installation and management experience, check out [Laravel Herd](#local-installation-using-herd). + ### Creating an Application From f6318a64440b85473c46b2b4789412f7a5202564 Mon Sep 17 00:00:00 2001 From: Sarah Gilberg Date: Tue, 8 Oct 2024 14:35:23 -0400 Subject: [PATCH 1869/2609] Document solution for multiple rate limits on same segment (#9933) * Document solution for multiple rate limits on same segment * formatting --------- Co-authored-by: Taylor Otwell --- routing.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/routing.md b/routing.md index b77e6f67fd0..f0fdbdd1900 100644 --- a/routing.md +++ b/routing.md @@ -829,6 +829,15 @@ If needed, you may return an array of rate limits for a given rate limiter confi ]; }); +If you're assigning multiple rate limits segmented by identical `by` values, you should ensure that each `by` value is unique. The easiest way to achieve this is to prefix the values given to the `by` method: + + RateLimiter::for('uploads', function (Request $request) { + return [ + Limit::perMinute(10)->by('minute:'.$request->user()->id), + Limit::perDay(1000)->by('day:'.$request->user()->id), + ]; + }); + ### Attaching Rate Limiters to Routes From 08fbb4d3aa9a28ea0aaf294bbb1a465046067286 Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Wed, 9 Oct 2024 23:26:38 +0900 Subject: [PATCH 1870/2609] Add link to Carbon 3.x official migration guide (#9934) * Add link to Carbon 3.x official migration guide * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index 209df7bd94c..0c1008e39db 100644 --- a/upgrade.md +++ b/upgrade.md @@ -421,7 +421,7 @@ public function scalar($query, $bindings = [], $useReadPdo = true); **Likelihood Of Impact: Medium** -Laravel 11 supports both Carbon 2 and Carbon 3. Carbon is a date manipulation library utilized extensively by Laravel and packages throughout the ecosystem. If you upgrade to Carbon 3, be aware that `diffIn*` methods now return floating-point numbers and may return negative values to indicate time direction, which is a significant change from Carbon 2. Review Carbon's [change log](https://github.com/briannesbitt/Carbon/releases/tag/3.0.0) for detailed information on how to handle these and other changes. +Laravel 11 supports both Carbon 2 and Carbon 3. Carbon is a date manipulation library utilized extensively by Laravel and packages throughout the ecosystem. If you upgrade to Carbon 3, be aware that `diffIn*` methods now return floating-point numbers and may return negative values to indicate time direction, which is a significant change from Carbon 2. Review Carbon's [change log](https://github.com/briannesbitt/Carbon/releases/tag/3.0.0) and [documentation](https://carbon.nesbot.com/docs/#api-carbon-3) for detailed information on how to handle these and other changes. ### Mail From 2f56a79462e97db0fa5072c92d0bb1f1197eff2f Mon Sep 17 00:00:00 2001 From: Ryan Holton Date: Wed, 9 Oct 2024 20:35:54 +0100 Subject: [PATCH 1871/2609] feat: document Str::doesntContain() (#9937) --- strings.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/strings.md b/strings.md index 396f45c4c57..7a75023e369 100644 --- a/strings.md +++ b/strings.md @@ -47,6 +47,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::chopEnd](#method-str-chop-end) [Str::contains](#method-str-contains) [Str::containsAll](#method-str-contains-all) +[Str::doesntContain](#method-str-doesnt-contain) [Str::deduplicate](#method-deduplicate) [Str::endsWith](#method-ends-with) [Str::excerpt](#method-excerpt) @@ -468,6 +469,33 @@ You may disable case sensitivity by setting the `ignoreCase` argument to `true`: $containsAll = Str::containsAll('This is my name', ['MY', 'NAME'], ignoreCase: true); // true + + +#### `Str::doesntContain()` {.collection-method} + +The `Str::doesntContain` method determines if the given string doesn't contain the given value. By default this method is case sensitive: + + use Illuminate\Support\Str; + + $doesntContain = Str::doesntContain('This is name', 'my'); + + // true + +You may also pass an array of values to determine if the given string doesn't contain any of the values in the array: + + use Illuminate\Support\Str; + + $doesntContain = Str::doesntContain('This is name', ['my', 'foo']); + + // true + +You may disable case sensitivity by setting the `ignoreCase` argument to `true`: + + use Illuminate\Support\Str; + + $doesntContain = Str::doesntContain('This is name', 'MY', ignoreCase: true); + + // true #### `Str::deduplicate()` {.collection-method} From f6715ca87507f0e5f8dc2ed862e0d47743359504 Mon Sep 17 00:00:00 2001 From: Giga <36007921+gigabites19@users.noreply.github.com> Date: Wed, 9 Oct 2024 23:37:07 +0400 Subject: [PATCH 1872/2609] fix inaccurate return type (#9935) --- vite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.md b/vite.md index 16d69524989..69bf5b6f5c4 100644 --- a/vite.md +++ b/vite.md @@ -399,7 +399,7 @@ import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; createInertiaApp({ resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')), setup({ el, App, props, plugin }) { - return createApp({ render: () => h(App, props) }) + createApp({ render: () => h(App, props) }) .use(plugin) .mount(el) }, From c720e78408598e7534eef4d0bb79ab58922b46bb Mon Sep 17 00:00:00 2001 From: Ryan Holton Date: Wed, 9 Oct 2024 20:38:23 +0100 Subject: [PATCH 1873/2609] [11.x] feat: document new Number::withCurrency and Number::useCurrency (#9936) * feat: document new currency methods * feat: document new currency methods --- helpers.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/helpers.md b/helpers.md index 097f1ff83ea..ac604263946 100644 --- a/helpers.md +++ b/helpers.md @@ -103,6 +103,8 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Number::trim](#method-number-trim) [Number::useLocale](#method-number-use-locale) [Number::withLocale](#method-number-with-locale) +[Number::useCurrency](#method-number-use-currency) +[Number::withCurrency](#method-number-with-currency)
    @@ -1425,6 +1427,32 @@ The `Number::withLocale` method executes the given closure using the specified l return Number::format(1500); }); + +#### `Number::useCurrency()` {.collection-method} + +The `Number::useCurrency` method sets the default number currency globally, which affects how the currency is formatted by subsequent invocations to the `Number` class's methods: + + use Illuminate\Support\Number; + + /** + * Bootstrap any application services. + */ + public function boot(): void + { + Number::useCurrency('GBP'); + } + + +#### `Number::withCurrency()` {.collection-method} + +The `Number::withCurrency` method executes the given closure using the specified currency and then restores the original currency after the callback has executed: + + use Illuminate\Support\Number; + + $number = Number::withCurrency('GBP', function () { + // ... + }); + ## Paths From e3cf0747f0c17032d9dcc324ac3bd5b47315e4dd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 9 Oct 2024 15:00:33 -0500 Subject: [PATCH 1874/2609] wip --- queries.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/queries.md b/queries.md index d437ab81f40..c5f93a92afa 100644 --- a/queries.md +++ b/queries.md @@ -479,6 +479,9 @@ You may also pass an array of conditions to the `where` function. Each element o > [!WARNING] > 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. +> [!WARNING] +> MySQL and MariaDB automatically typecast strings to integers in string-number comparisons. In this process, non-numeric strings are converted to `0`, which can lead to unexpected results. For example, if your table has a `secret` column with a value of `aaa` and you run `User::where('secret', 0)`, that row will be returned. To avoid this, ensure all values are typecast to their appropriate types before using them in queries. + ### Or Where Clauses From 687b781084e92ce5ee56197125c071b0d592fda4 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 10 Oct 2024 20:05:42 -0500 Subject: [PATCH 1875/2609] consistent styling for multi-line attributes (#9941) we had a mix of multiline attribute styling, some with the first attribute on the first line and some on the second line. this converts them all to be the same. --- blade.md | 48 ++++++++++++++++++++++++++++++------------------ validation.md | 6 ++++-- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/blade.md b/blade.md index fb9193d6250..e124c9cfb2a 100644 --- a/blade.md +++ b/blade.md @@ -482,10 +482,12 @@ Likewise, the `@style` directive may be used to conditionally add inline CSS sty For convenience, you may use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will echo `checked` if the provided condition evaluates to `true`: ```blade -active)) /> +active)) +/> ``` Likewise, the `@selected` directive may be used to indicate if a given select option should be "selected": @@ -509,19 +511,23 @@ Additionally, the `@disabled` directive may be used to indicate if a given eleme Moreover, the `@readonly` directive may be used to indicate if a given element should be "readonly": ```blade -isNotAdmin()) /> +isNotAdmin()) +/> ``` In addition, the `@required` directive may be used to indicate if a given element should be "required": ```blade -isAdmin()) /> +isAdmin()) +/> ``` @@ -1641,9 +1647,11 @@ The `@error` directive may be used to quickly check if [validation error message - + class="@error('title') is-invalid @enderror" +/> @error('title')
    {{ $message }}
    @@ -1657,9 +1665,11 @@ Since the `@error` directive compiles to an "if" statement, you may use the `@el - + class="@error('email') is-invalid @else is-valid @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: @@ -1669,9 +1679,11 @@ You may pass [the name of a specific error bag](/docs/{{version}}/validation#nam - + class="@error('email', 'login') is-invalid @enderror" +/> @error('email', 'login')
    {{ $message }}
    diff --git a/validation.md b/validation.md index b77486a7bdf..011d9f6d98d 100644 --- a/validation.md +++ b/validation.md @@ -220,10 +220,12 @@ You may use the `@error` [Blade](/docs/{{version}}/blade) directive to quickly d - + class="@error('title') is-invalid @enderror" +/> @error('title')
    {{ $message }}
    From 718322dbbf948e16789b05be3e8580683bdf4f64 Mon Sep 17 00:00:00 2001 From: Alex Miles Date: Fri, 11 Oct 2024 02:07:23 +0100 Subject: [PATCH 1876/2609] [11.x] Reinstate link to Laravel installer package (#9940) * Reinstate link to Laravel installer package * Update installation.md --------- Co-authored-by: Taylor Otwell --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index ab6508af262..eb184447445 100644 --- a/installation.md +++ b/installation.md @@ -61,7 +61,7 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ### Installing PHP and the Laravel Installer -Before creating your first Laravel application, make sure that your local machine has [PHP](https://php.net) and [Composer](https://getcomposer.org) installed. In addition, you should install [Node and NPM](https://nodejs.org) so that you can compile your application's frontend assets. +Before creating your first Laravel application, make sure that your local machine has [PHP](https://php.net), [Composer](https://getcomposer.org), and [the Laravel installer](https://github.com/laravel/installer) installed. In addition, you should install [Node and NPM](https://nodejs.org) so that you can compile your application's frontend assets. If you don't have PHP and Composer installed on your local machine, the following commands will install PHP, Composer, and the Laravel installer on macOS, Windows, or Linux: From 413397108dceec51289541a1b899a0783646d84a Mon Sep 17 00:00:00 2001 From: Andy Hinkle Date: Fri, 11 Oct 2024 08:57:40 -0500 Subject: [PATCH 1877/2609] [11.x] Optional param for Confirmed rule (#9943) * Add documentation for optional param of confirmed rule * wording --- validation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/validation.md b/validation.md index 011d9f6d98d..43d71477dab 100644 --- a/validation.md +++ b/validation.md @@ -1100,6 +1100,8 @@ The field under validation must be able to be cast as a boolean. Accepted 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. +You may also pass a custom confirmation field name. For example, `confirmed:repeat_username` will expect the field `repeat_username` to match the field under validation. + #### contains:_foo_,_bar_,... From 85faadde4f004298773a1e0e8f0d53ae47430c77 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 11 Oct 2024 09:27:58 -0500 Subject: [PATCH 1878/2609] update the list of global middleware (#9944) document the newly added global middleware https://github.com/laravel/framework/blob/11.x/src/Illuminate/Foundation/Configuration/Middleware.php#L411 --- middleware.md | 1 + 1 file changed, 1 insertion(+) diff --git a/middleware.md b/middleware.md index b9e3d03b21f..95db7bb8b7d 100644 --- a/middleware.md +++ b/middleware.md @@ -129,6 +129,7 @@ If you would like to manage Laravel's global middleware stack manually, you may ->withMiddleware(function (Middleware $middleware) { $middleware->use([ + \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, // \Illuminate\Http\Middleware\TrustHosts::class, \Illuminate\Http\Middleware\TrustProxies::class, \Illuminate\Http\Middleware\HandleCors::class, From 339648899df6c796dc63dd3b614986af451ea493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 15 Oct 2024 15:41:37 +0200 Subject: [PATCH 1879/2609] Explain how to get structured logs when using FrankenPHP through Octane (#9953) * Explain how to get structured logs when using FrankenPHP through Octane * Update octane.md --------- Co-authored-by: Taylor Otwell --- octane.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/octane.md b/octane.md index 999360a1ead..8259e9a3985 100644 --- a/octane.md +++ b/octane.md @@ -135,6 +135,8 @@ services: - .:/app ``` +If the `--log-level` option is explicitly passed to the `php artisan octane:start` command, Octane will use FrankenPHP's native logger and, unless configured differently, will produce structured JSON logs. + You may consult [the official FrankenPHP documentation](https://frankenphp.dev/docs/docker/) for more information on running FrankenPHP with Docker. From 2da9260ac1dc62a9c282a075da036ab997f870e2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Oct 2024 09:32:29 -0500 Subject: [PATCH 1880/2609] document collected by --- eloquent-collections.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/eloquent-collections.md b/eloquent-collections.md index bd1080352c1..c5b9b5c0b55 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -242,7 +242,23 @@ The `unique` method returns all of the unique models in the collection. Any mode ## Custom Collections -If you would like to use a custom `Collection` object when interacting with a given model, you may define a `newCollection` method on your model: +If you would like to use a custom `Collection` object when interacting with a given model, you may add the `CollectedBy` attribute to your model: + + Date: Tue, 15 Oct 2024 09:33:02 -0500 Subject: [PATCH 1881/2609] wip --- eloquent-collections.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eloquent-collections.md b/eloquent-collections.md index c5b9b5c0b55..860a1f7ea8e 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -282,4 +282,6 @@ Alternatively, you may define a `newCollection` method on your model: } } -Once you have defined a `newCollection` method, you will receive an instance of your custom collection anytime Eloquent would normally return an `Illuminate\Database\Eloquent\Collection` instance. If you would like to use a custom collection for every model in your application, you should define the `newCollection` method on a base model class that is extended by all of your application's models. +Once you have defined a `newCollection` method or added the `CollectedBy` attribute to your model, you will receive an instance of your custom collection anytime Eloquent would normally return an `Illuminate\Database\Eloquent\Collection` instance. + +If you would like to use a custom collection for every model in your application, you should define the `newCollection` method on a base model class that is extended by all of your application's models. From 8205b55a6e20c0ceecc9fa606f03b7a58c32095a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Oct 2024 09:34:04 -0500 Subject: [PATCH 1882/2609] wip --- container.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.md b/container.md index 575bd93461a..5fc122ed354 100644 --- a/container.md +++ b/container.md @@ -252,7 +252,7 @@ class PhotoController extends Controller } ``` -In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `DB`, `Log`, and [`Tag`](#tagging) attributes: +In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `DB`, `Log`, `RouteParameter`, and [`Tag`](#tagging) attributes: ```php Date: Tue, 15 Oct 2024 09:35:35 -0500 Subject: [PATCH 1883/2609] composer run dev --- installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/installation.md b/installation.md index eb184447445..ee92d33252f 100644 --- a/installation.md +++ b/installation.md @@ -91,15 +91,15 @@ After you have installed PHP, Composer, and the Laravel installer, you're ready laravel new example-app ``` -Once the application has been created, start Laravel's local development server using the `serve` command: +Once the application has been created, you can start Laravel's local development server, queue worker, and Vite development server using the `dev` Composer script: ```nothing cd example-app -php artisan serve +composer run dev ``` -Once you have started the Artisan development server, your application will be accessible in your web browser at [http://localhost:8000](http://localhost:8000). Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). +Once you have started the development server, your application will be accessible in your web browser at [http://localhost:8000](http://localhost:8000). Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). > [!NOTE] > If you would like a head start when developing your Laravel application, 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. From 6edbc838af936fca1c98832a55e78325d6214097 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 15 Oct 2024 09:39:47 -0500 Subject: [PATCH 1884/2609] wip --- packages.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages.md b/packages.md index e43ef7a0bde..0ee98e13d8b 100644 --- a/packages.md +++ b/packages.md @@ -13,6 +13,7 @@ - [View Components](#view-components) - ["About" Artisan Command](#about-artisan-command) - [Commands](#commands) + - [Optimize Commands](#optimize-commands) - [Public Assets](#public-assets) - [Publishing File Groups](#publishing-file-groups) @@ -339,6 +340,24 @@ To register your package's Artisan commands with Laravel, you may use the `comma } } + +### Optimize Commands + +Laravel's [`optimize` command](/docs/{{version}}/deployment#optimization) caches the application's configuration, events, routes, and views. Using the `optimizes` method, you may register your package's own Artisan commands that should be invoked when the `optimize` and `optimize:clear` commands are executed: + + /** + * Bootstrap any package services. + */ + public function boot(): void + { + if ($this->app->runningInConsole()) { + $this->optimizes( + optimize: 'package:optimize', + clear: 'package:clear-optimizations', + ); + } + } + ## Public Assets From 3e699863690de91ec3ea7b002cf52f0e80e62442 Mon Sep 17 00:00:00 2001 From: Andy Hinkle Date: Tue, 15 Oct 2024 10:08:32 -0500 Subject: [PATCH 1885/2609] [11.x] Migration `nullOnUpdate` (#9954) * Add docs for null on update * Update migrations.md --------- Co-authored-by: Taylor Otwell --- migrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/migrations.md b/migrations.md index a22bc3e5c25..d4613404ade 100644 --- a/migrations.md +++ b/migrations.md @@ -1190,6 +1190,7 @@ An alternative, expressive syntax is also provided for these actions: | ----------------------------- | ------------------------------------------------- | | `$table->cascadeOnUpdate();` | Updates should cascade. | | `$table->restrictOnUpdate();` | Updates should be restricted. | +| `$table->nullOnUpdate();` | Updates should set the foreign key value to null. | | `$table->noActionOnUpdate();` | No action on updates. | | `$table->cascadeOnDelete();` | Deletes should cascade. | | `$table->restrictOnDelete();` | Deletes should be restricted. | From 8f1a3d074d960178fc00bc91760324336e898822 Mon Sep 17 00:00:00 2001 From: Andy Hinkle Date: Tue, 15 Oct 2024 10:24:35 -0500 Subject: [PATCH 1886/2609] [11.x] Add Number Helpers: `defaultLocale`, `defaultCurrency` (#9955) * Add Number Default Locale * Add docs for default currency * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/helpers.md b/helpers.md index ac604263946..6986624beb9 100644 --- a/helpers.md +++ b/helpers.md @@ -93,6 +93,8 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Number::abbreviate](#method-number-abbreviate) [Number::clamp](#method-number-clamp) [Number::currency](#method-number-currency) +[Number::defaultCurrency](#method-default-currency) +[Number::defaultLocale](#method-default-locale) [Number::fileSize](#method-number-file-size) [Number::forHumans](#method-number-for-humans) [Number::format](#method-number-format) @@ -1231,6 +1233,28 @@ The `Number::currency` method returns the currency representation of the given v // 1.000,00 € + +#### `Number::defaultCurrency()` {.collection-method} + +The `Number::defaultCurrency` method returns the default currency being used by the `Number` class: + + use Illuminate\Support\Number; + + $currency = Number::defaultCurrency(); + + // USD + + +#### `Number::defaultLocale()` {.collection-method} + +The `Number::defaultLocale` method returns the default locale being used by the `Number` class: + + use Illuminate\Support\Number; + + $locale = Number::defaultLocale(); + + // en + #### `Number::fileSize()` {.collection-method} From d3df0454dd72245b80aa8801a770ceb2146cc2f9 Mon Sep 17 00:00:00 2001 From: Dmytro Morozov Date: Wed, 16 Oct 2024 23:43:33 +0300 Subject: [PATCH 1887/2609] [11.x] Get back old installation/application creation methods (#9958) * Get back old installation/application creation methods * Update installation.md * Update installation.md --------- Co-authored-by: Taylor Otwell --- installation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/installation.md b/installation.md index ee92d33252f..1c704185bb4 100644 --- a/installation.md +++ b/installation.md @@ -79,6 +79,12 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage After running one of the commands above, you should restart your terminal session. To update PHP, Composer, and the Laravel installer after installing them via `php.new`, you can re-run the command in your terminal. +In you already have PHP and Composer installed, you may install the Laravel installer via Composer: + +```shell +composer global require laravel/installer +``` + > [!NOTE] > For a fully-featured, graphical PHP installation and management experience, check out [Laravel Herd](#local-installation-using-herd). From 49b1b9f790ce1def7b3e94f254d21c0a90dfae98 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Wed, 16 Oct 2024 22:50:27 +0200 Subject: [PATCH 1888/2609] [11.x] Add support for union types in event listeners in Laravel 11 (#9957) * [11.x] Add support for union types in event listeners in Laravel 11 It's something I simply discovered. It simplifies some of my listeners, so I thought it would be helpful for other developers to know that this is possible. By using union types in Laravel event listeners, you can handle multiple related events in a single listener. This reduces redundancy and makes it easier to manage similar events in one place. The example in the documentation shows how to use union types in the `handle` method, leveraging the `instanceof` operator to differentiate and handle each event accordingly. * Update events.md * Update events.md --------- Co-authored-by: Taylor Otwell --- events.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/events.md b/events.md index 93c7b8cd69d..9fffbe352ac 100644 --- a/events.md +++ b/events.md @@ -68,6 +68,16 @@ By default, Laravel will automatically find and register your event listeners by } } +You may listen to multiple events using PHP's union types: + + /** + * Handle the given event. + */ + public function handle(PodcastProcessed|PodcastPublished $event): void + { + // ... + } + If you plan to store your listeners in a different directory or within multiple directories, you may instruct Laravel to scan those directories using the `withEvents` method in your application's `bootstrap/app.php` file: ->withEvents(discover: [ From da497fd5e0d1b0fd9f0715bcc50b7524abaec7bd Mon Sep 17 00:00:00 2001 From: Okorare Tega Date: Fri, 18 Oct 2024 14:50:40 +0100 Subject: [PATCH 1889/2609] Update typo error in installation.md (#9961) --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 1c704185bb4..eae384c4b8d 100644 --- a/installation.md +++ b/installation.md @@ -79,7 +79,7 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage After running one of the commands above, you should restart your terminal session. To update PHP, Composer, and the Laravel installer after installing them via `php.new`, you can re-run the command in your terminal. -In you already have PHP and Composer installed, you may install the Laravel installer via Composer: +If you already have PHP and Composer installed, you may install the Laravel installer via Composer: ```shell composer global require laravel/installer From 34ce2334289e77fb46bbf05f7089852d39217e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:54:10 +0200 Subject: [PATCH 1890/2609] [11.x] Fix code block language (#9973) * Change code block language from "shell" to "ini" for environment variables * That's obviously a shell command oO * The correct language is "ini", not "env" * Another one --- mail.md | 2 +- sail.md | 2 +- scout.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mail.md b/mail.md index 2798dad7bfc..af1fe496b8e 100644 --- a/mail.md +++ b/mail.md @@ -195,7 +195,7 @@ 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 MAIL_MAILER=mailersend MAIL_FROM_ADDRESS=app@yourdomain.com MAIL_FROM_NAME="App Name" diff --git a/sail.md b/sail.md index 7226abe3d78..6ba060a301f 100644 --- a/sail.md +++ b/sail.md @@ -177,7 +177,7 @@ sail php script.php Composer commands may be executed using the `composer` command. Laravel Sail's application container includes a Composer installation: -```nothing +```shell sail composer require laravel/sanctum ``` diff --git a/scout.md b/scout.md index 65af70930a7..4812de51f48 100644 --- a/scout.md +++ b/scout.md @@ -141,7 +141,7 @@ 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 SCOUT_DRIVER=typesense TYPESENSE_API_KEY=masterKey TYPESENSE_HOST=localhost @@ -149,7 +149,7 @@ TYPESENSE_HOST=localhost If you are using [Laravel Sail](/docs/{{version}}/sail), you may need to adjust the `TYPESENSE_HOST` environment variable to match the Docker container name. You may also optionally specify your installation's port, path, and protocol: -```env +```ini TYPESENSE_PORT=8108 TYPESENSE_PATH= TYPESENSE_PROTOCOL=http From e54ac7fb565a0be4661bf77535d9c52b1f5856e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:57:40 +0200 Subject: [PATCH 1891/2609] Fix heading levels on structure page (#9969) --- structure.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/structure.md b/structure.md index 50c181be912..669241f1fdd 100644 --- a/structure.md +++ b/structure.md @@ -39,37 +39,37 @@ The default Laravel application structure is intended to provide a great startin ## The Root Directory -#### The App Directory +### The App Directory The `app` directory contains the core code of your application. We'll explore this directory in more detail soon; however, almost all of the classes in your application will be in this directory. -#### The Bootstrap Directory +### 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 Config Directory +### The Config Directory The `config` directory, as the name implies, contains all of your application's configuration files. It's a great idea to read through all of these files and familiarize yourself with all of the options available to you. -#### The Database Directory +### The Database Directory The `database` directory contains your database migrations, model factories, and seeds. If you wish, you may also use this directory to hold an SQLite database. -#### The Public Directory +### The Public Directory The `public` directory contains the `index.php` file, which is the entry point for all requests entering your application and configures autoloading. This directory also houses your assets such as images, JavaScript, and CSS. -#### The Resources Directory +### The Resources Directory The `resources` directory contains your [views](/docs/{{version}}/views) as well as your raw, un-compiled assets such as CSS or JavaScript. -#### The Routes Directory +### The Routes Directory The `routes` directory contains all of the route definitions for your application. By default, two route files are included with Laravel: `web.php` and `console.php`. @@ -84,19 +84,19 @@ The `api.php` file contains routes that are intended to be stateless, so request 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 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` Artisan command. -#### The Tests Directory +### The Tests Directory The `tests` directory contains your automated tests. Example [Pest](https://pestphp.com) or [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 `/vendor/bin/pest` or `/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 +### The Vendor Directory The `vendor` directory contains your [Composer](https://getcomposer.org) dependencies. @@ -113,68 +113,68 @@ Both the `Console` and `Http` directories are further explained in their respect > Many of the classes in the `app` directory can be generated by Artisan via commands. To review the available commands, run the `php artisan list make` command in your terminal. -#### The Broadcasting Directory +### The Broadcasting Directory The `Broadcasting` directory contains all of the broadcast channel classes for your application. These classes are generated using the `make:channel` command. This directory does not exist by default, but will be created for you when you create your first channel. To learn more about channels, check out the documentation on [event broadcasting](/docs/{{version}}/broadcasting). -#### The Console Directory +### The Console Directory The `Console` directory contains all of the custom Artisan commands for your application. These commands may be generated using the `make:command` command. -#### The Events Directory +### The Events Directory This directory does not exist by default, but will be created for you by the `event:generate` and `make:event` Artisan commands. The `Events` directory houses [event classes](/docs/{{version}}/events). Events may be used to alert other parts of your application that a given action has occurred, providing a great deal of flexibility and decoupling. -#### The Exceptions Directory +### The Exceptions Directory The `Exceptions` directory contains all of the custom exceptions for your application. These exceptions may be generated using the `make:exception` command. -#### The Http Directory +### The Http Directory The `Http` directory contains your controllers, middleware, and form requests. Almost all of the logic to handle requests entering your application will be placed in this directory. -#### The Jobs Directory +### The Jobs Directory This directory does not exist by default, but will be created for you if you execute the `make:job` Artisan command. The `Jobs` directory houses the [queueable jobs](/docs/{{version}}/queues) for your application. Jobs may be queued by your application or run synchronously within the current request lifecycle. Jobs that run synchronously during the current request are sometimes referred to as "commands" since they are an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern). -#### The Listeners Directory +### The Listeners Directory This directory does not exist by default, but will be created for you if you execute the `event:generate` or `make:listener` Artisan commands. The `Listeners` directory contains the classes that handle your [events](/docs/{{version}}/events). Event listeners receive an event instance and perform logic in response to the event being fired. For example, a `UserRegistered` event might be handled by a `SendWelcomeEmail` listener. -#### The Mail Directory +### 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](/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 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 +### 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](/docs/{{version}}/notifications) that are sent by your application, such as simple notifications about events that happen within your application. Laravel's notification feature abstracts sending notifications over a variety of drivers such as email, Slack, SMS, or stored in a database. -#### The Policies Directory +### 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](/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 +### The Providers Directory The `Providers` directory contains all of the [service providers](/docs/{{version}}/providers) for your application. Service providers bootstrap your application by binding services in the service container, registering events, or performing any other tasks to prepare your application for incoming requests. In a fresh Laravel application, this directory will already contain the `AppServiceProvider`. You are free to add your own providers to this directory as needed. -#### The Rules Directory +### The Rules Directory This directory does not exist by default, but will be created for you if you execute the `make:rule` Artisan command. The `Rules` directory contains the custom validation rule objects for your application. Rules are used to encapsulate complicated validation logic in a simple object. For more information, check out the [validation documentation](/docs/{{version}}/validation). From 77569f35d47752c3ae19c88f8c6cb47366b89905 Mon Sep 17 00:00:00 2001 From: Jorge Lapa <2780099+heyjorgedev@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:00:02 +0100 Subject: [PATCH 1892/2609] Update typo error in eloquent.md (#9964) --- eloquent.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent.md b/eloquent.md index 776029bc98b..9e7084cc881 100644 --- a/eloquent.md +++ b/eloquent.md @@ -479,7 +479,7 @@ If you are filtering the results of the `chunk` method based on a column that yo Flight::where('departed', true) ->chunkById(200, function (Collection $flights) { $flights->each->update(['departed' => false]); - }, $column = 'id'); + }, column: 'id'); ``` Since the `chunkById` and `lazyById` methods add their own "where" conditions to the query being executed, you should typically [logically group](/docs/{{version}}/queries#logical-grouping) your own conditions within a closure: @@ -512,7 +512,7 @@ If you are filtering the results of the `lazy` method based on a column that you ```php Flight::where('departed', true) - ->lazyById(200, $column = 'id') + ->lazyById(200, column: 'id') ->each->update(['departed' => false]); ``` From 16b8805d9c475af2f01351913316fcd835a81ad3 Mon Sep 17 00:00:00 2001 From: David Gasperoni Date: Mon, 21 Oct 2024 16:00:57 +0200 Subject: [PATCH 1893/2609] Update Axios's URL (#9963) --- broadcasting.md | 2 +- passport.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 7c42269e48a..1c0ef6aae8d 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -721,7 +721,7 @@ However, remember that we also broadcast the task's creation. If your JavaScript #### Configuration -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 an `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/axios/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request as an `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 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: diff --git a/passport.md b/passport.md index 96738eae41b..9aaa57cb908 100644 --- a/passport.md +++ b/passport.md @@ -438,7 +438,7 @@ This `/oauth/token` route will return a JSON response containing `access_token`, #### JSON API -Passport also includes a JSON API for managing authorized access tokens. You may pair this with your own frontend to offer your users a dashboard for managing access tokens. For convenience, we'll use [Axios](https://github.com/mzabriskie/axios) to demonstrate making HTTP requests to the endpoints. The JSON API is guarded by the `web` and `auth` middleware; therefore, it may only be called from your own application. +Passport also includes a JSON API for managing authorized access tokens. You may pair this with your own frontend to offer your users a dashboard for managing access tokens. For convenience, we'll use [Axios](https://github.com/axios/axios) to demonstrate making HTTP requests to the endpoints. The JSON API is guarded by the `web` and `auth` middleware; therefore, it may only be called from your own application. #### `GET /oauth/tokens` @@ -867,7 +867,7 @@ Once you have created a personal access client, you may issue tokens for a given #### JSON API -Passport also includes a JSON API for managing personal access tokens. You may pair this with your own frontend to offer your users a dashboard for managing personal access tokens. Below, we'll review all of the API endpoints for managing personal access tokens. For convenience, we'll use [Axios](https://github.com/mzabriskie/axios) to demonstrate making HTTP requests to the endpoints. +Passport also includes a JSON API for managing personal access tokens. You may pair this with your own frontend to offer your users a dashboard for managing personal access tokens. Below, we'll review all of the API endpoints for managing personal access tokens. For convenience, we'll use [Axios](https://github.com/axios/axios) to demonstrate making HTTP requests to the endpoints. The JSON API is guarded by the `web` and `auth` middleware; therefore, it may only be called from your own application. It is not able to be called from an external source. From d636b0efcb462b894e8f18dd73f1b72f37a74881 Mon Sep 17 00:00:00 2001 From: Nelson Isioma Date: Mon, 21 Oct 2024 20:43:42 +0100 Subject: [PATCH 1894/2609] docs: updating authorization.md (#9975) * Update authorization.md * Update authorization.md --------- Co-authored-by: Taylor Otwell --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index ca5b4c8f470..ec79b888e2f 100644 --- a/authorization.md +++ b/authorization.md @@ -241,7 +241,7 @@ You may use the `after` method to define a closure to be executed after all othe } }); -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. +Values returned by `after` closures will not override the result of the authorization check unless the gate or policy returned `null`. ### Inline Authorization From 4f4c78e4a04ac1c09c8092016ff2434c9c0532a0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 22 Oct 2024 09:26:06 -0500 Subject: [PATCH 1895/2609] wip --- processes.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/processes.md b/processes.md index 0ad4de84126..46051b96b84 100644 --- a/processes.md +++ b/processes.md @@ -291,6 +291,16 @@ $process = Process::start('bash import.sh', function (string $type, string $outp $result = $process->wait(); ``` +Instead of waiting until the process has finished, you may use the `waitUntil` method to stop waiting based on the output of the process. Laravel will stop waiting for the process to finish when the closure given to the `waitUntil` method returns `true`: + +```php +$process = Process::start('bash import.sh'); + +$process->waitUntil(function (string $type, string $output) { + return $output === 'Ready...'; +}); +``` + ## Concurrent Processes From 426c6287831d95a7472e085cfd53ea11d93f0a78 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 23 Oct 2024 08:01:55 -0500 Subject: [PATCH 1896/2609] add clarification about `forget` method return value (#9982) the wording here is just a little unclear. the first sentence seems to suggest that the `forget()` method does not return anything, even though it does. you are free to assign the result to a new variable even though you don't need to. --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index 58a05007a4e..a963e8bd22e 100644 --- a/collections.md +++ b/collections.md @@ -1083,7 +1083,7 @@ The `forget` method removes an item from the collection by its key: // [] > [!WARNING] -> Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. +> Unlike most other collection methods, `forget` does not return a new modified collection; it modifies and returns the collection it is called on. #### `forPage()` {.collection-method} From c155276ccdc164d27663b7a2f210f388531db377 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 24 Oct 2024 09:31:08 -0500 Subject: [PATCH 1897/2609] [11.x] new allowed filename for anonymous index components (#9984) * new allowed filename for anonymous index components we can now use either `index.blade.php` or `{directory}.blade.php` for our anonymous index components. https://github.com/laravel/framework/pull/52669 * Update blade.md --------- Co-authored-by: Taylor Otwell --- blade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade.md b/blade.md index e124c9cfb2a..cbfd7373477 100644 --- a/blade.md +++ b/blade.md @@ -1374,10 +1374,10 @@ This directory structure allows you to render the accordion component and its it However, in order to render the accordion component via `x-accordion`, we were forced to place the "index" accordion component template in the `resources/views/components` directory instead of nesting it within the `accordion` directory with the other accordion related templates. -Thankfully, Blade allows you to place an `index.blade.php` file within a component's template directory. When an `index.blade.php` template exists for the component, it will be rendered as the "root" node of the component. So, we can continue to use the same Blade syntax given in the example above; however, we will adjust our directory structure like so: +Thankfully, Blade allows you to place a file matching the component's directory name within the component's directory itself. When this template exists, it can be rendered as the "root" element of the component even though it is nested within a directory. So, we can continue to use the same Blade syntax given in the example above; however, we will adjust our directory structure like so: ```none -/resources/views/components/accordion/index.blade.php +/resources/views/components/accordion/accordion.blade.php /resources/views/components/accordion/item.blade.php ``` From 724c31ccd3edce6b6dfe5e0dd2a594a47217f078 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 24 Oct 2024 09:38:19 -0500 Subject: [PATCH 1898/2609] [11.x] document "index" class components (#9985) * document "index" class components https://github.com/laravel/framework/pull/52669 * formatting --------- Co-authored-by: Taylor Otwell --- blade.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/blade.md b/blade.md index cbfd7373477..7b08cc4825c 100644 --- a/blade.md +++ b/blade.md @@ -18,6 +18,7 @@ - [Comments](#comments) - [Components](#components) - [Rendering Components](#rendering-components) + - [Index Components](#index-components) - [Passing Data to Components](#passing-data-to-components) - [Component Attributes](#component-attributes) - [Reserved Keywords](#reserved-keywords) @@ -756,6 +757,26 @@ If you would like to conditionally render your component, you may define a `shou return Str::length($this->message) > 0; } + +### Index Components + +Sometimes components are part of a component group and you may wish to group the related components within a single directory. For example, imagine a "card" component with the following class structure: + +```none +App\Views\Components\Card\Card +App\Views\Components\Card\Header +App\Views\Components\Card\Body +``` + +Since the root `Card` component is nested within a `Card` directory, you might expect that you would need to render the component via ``. However, when a component's file name matches the name of the component's directory, Laravel automatically assumes that component is the "root" component and allows you to render the component without repeating the directory name: + +```blade + + ... + ... + +``` + ### Passing Data to Components From c608c51d885501d71575dd692e33949e0eb7fe47 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 29 Oct 2024 08:56:21 -0500 Subject: [PATCH 1899/2609] [11.x] consistently use "an" when preceding "SQL" or "SQLite" (#9998) * consistently use "an" when preceding "SQL" or "SQLite" my preference would be to go the other direction, but this at least makes it consistent across the docs * remove "unique" SQL injection warning I don't believe this warning is actually valid anymore. Best I can tell, using the default `DatabasePresenceVerifier`, we are executing an aggregate "count" query, which use Prepared Statements with variable binding. the note was added 5 years ago [here](https://github.com/laravel/docs/commit/8c63ad24fdb6750e0372e4ee9170ac2b119c377f) the query is built [here](https://github.com/laravel/docs/commit/8c63ad24fdb6750e0372e4ee9170ac2b119c377f) for the example of updating a user and ensuring the email remains unique, the generated query is ```sql select count(*) as aggregate from `users` where `email` = ? and `id` <> ? and `users`.`deleted_at` is null ``` we could also go the route of not removing this completely, but softening the language to something like "*may* be vulnerable..." in case we're worried about people using alternate "Presence Verifiers". * oops, revert. wrong branch --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index eae384c4b8d..dfff142a526 100644 --- a/installation.md +++ b/installation.md @@ -130,7 +130,7 @@ Your `.env` file should not be committed to your application's source control, s ### Databases and Migrations -Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with a SQLite database. +Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with an SQLite database. During the creation of the application, Laravel created a `database/database.sqlite` file for you, and ran the necessary migrations to create the application's database tables. From 2a4226eadcd991901858e699f9c0ae62c13f90e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BC=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=BA=D0=BE=D1=88?= =?UTF-8?q?=D0=BA=D0=B8=D0=BD?= <126749386+humblera1@users.noreply.github.com> Date: Tue, 29 Oct 2024 21:21:09 +0700 Subject: [PATCH 1900/2609] Changed the name of the method in the description to the appropriate one (#9996) --- eloquent-relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index caea2bb0645..f4ed21b32d8 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1072,7 +1072,7 @@ foreach ($posts as $post) { In the example above, an "N + 1" query problem has been introduced because, even though comments were eager loaded for every `Post` model, Eloquent does not automatically hydrate the parent `Post` on each child `Comment` model. -If you would like Eloquent to automatically hydrate parent models onto their children, you may invoke the `chaperone` method when defining a `hasMany` relationship: +If you would like Eloquent to automatically hydrate parent models onto their children, you may invoke the `chaperone` method when defining a `morphMany` relationship: class Post extends Model { From e81073dfdf1af87568007014abe72aaa235c61b1 Mon Sep 17 00:00:00 2001 From: Perry van der Meer <11609290+PerryvanderMeer@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:32:42 +0100 Subject: [PATCH 1901/2609] Docs for `$this->assertDatabaseEmpty('table')` (#9995) * Update database-testing.md * Update database-testing.md Co-authored-by: Josh Manders --------- Co-authored-by: Josh Manders --- database-testing.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/database-testing.md b/database-testing.md index f4eb1acdba9..4545a81268e 100644 --- a/database-testing.md +++ b/database-testing.md @@ -196,6 +196,13 @@ Assert that a table in the database contains the given number of records: $this->assertDatabaseCount('users', 5); + +#### assertDatabaseEmpty + +Assert that a table in the database contains no records: + + $this->assertDatabaseEmpty('users'); + #### assertDatabaseHas From 47f34bd8ee4cbec55ad5c3b3e2dddb7a3a563628 Mon Sep 17 00:00:00 2001 From: Akhilesh Jha Date: Tue, 29 Oct 2024 20:03:17 +0530 Subject: [PATCH 1902/2609] Update installation.md (#9994) This got wasted my 10 min as when I ran "composer run dev" as I am new to laravel. this --> "npm install && npm run build" --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index dfff142a526..c890af7f45b 100644 --- a/installation.md +++ b/installation.md @@ -101,7 +101,7 @@ Once the application has been created, you can start Laravel's local development ```nothing cd example-app - +npm install && npm run build composer run dev ``` From 0fb74b53ead24933cdc2024aa979675b45e6e821 Mon Sep 17 00:00:00 2001 From: Pushpak Chhajed Date: Tue, 29 Oct 2024 20:08:52 +0530 Subject: [PATCH 1903/2609] Update Octane configuration to use dynamic port for supervisor PHP command instead of Port 80 (#9993) --- octane.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octane.md b/octane.md index 8259e9a3985..1a4d891abaf 100644 --- a/octane.md +++ b/octane.md @@ -78,7 +78,7 @@ Finally, add a `SUPERVISOR_PHP_COMMAND` environment variable to the `laravel.tes services: laravel.test: environment: - SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port=80" # [tl! add] + SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port='${APP_PORT:-80}'" # [tl! add] XDG_CONFIG_HOME: /var/www/html/config # [tl! add] XDG_DATA_HOME: /var/www/html/data # [tl! add] ``` @@ -170,7 +170,7 @@ Then, add a `SUPERVISOR_PHP_COMMAND` environment variable to the `laravel.test` services: laravel.test: environment: - SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=80" # [tl! add] + SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port='${APP_PORT:-80}'" # [tl! add] ``` Finally, ensure the `rr` binary is executable and build your Sail images: @@ -215,7 +215,7 @@ To get started, add a `SUPERVISOR_PHP_COMMAND` environment variable to the `lara services: laravel.test: environment: - SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --port=80" # [tl! add] + SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --port='${APP_PORT:-80}'" # [tl! add] ``` Finally, build your Sail images: From 1747ca57ca53190d7266b575c8aba86305ba17f1 Mon Sep 17 00:00:00 2001 From: Ahmadreza Bashari <61243238+ahmadreza1383@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:09:49 +0330 Subject: [PATCH 1904/2609] Add scopedIf method for container (#9992) * Add scopedIf method for container * Update container.md --------- Co-authored-by: ahmadreza Co-authored-by: Taylor Otwell --- container.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/container.md b/container.md index 5fc122ed354..aafba3e1d65 100644 --- a/container.md +++ b/container.md @@ -170,6 +170,12 @@ The `scoped` method binds a class or interface into the container that should on return new Transistor($app->make(PodcastParser::class)); }); +You may use the `scopedIf` method to register a scoped container binding only if a binding has not already been registered for the given type: + + $this->app->scopedIf(Transistor::class, function (Application $app) { + return new Transistor($app->make(PodcastParser::class)); + }); + #### Binding Instances From 690175ad7106a7b8ce6e6e8420872ca932622e84 Mon Sep 17 00:00:00 2001 From: Ahmadreza Bashari <61243238+ahmadreza1383@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:40:28 +0330 Subject: [PATCH 1905/2609] Add rebinding method to container docs (#9990) * Add rebinding to Container docs * document rebinding --------- Co-authored-by: ahmadreza Co-authored-by: Taylor Otwell --- container.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/container.md b/container.md index aafba3e1d65..61b90d81f2a 100644 --- a/container.md +++ b/container.md @@ -17,6 +17,7 @@ - [Automatic Injection](#automatic-injection) - [Method Invocation and Injection](#method-invocation-and-injection) - [Container Events](#container-events) + - [Rebinding](#rebinding) - [PSR-11](#psr-11) @@ -584,6 +585,28 @@ The service container fires an event each time it resolves an object. You may li As you can see, the object being resolved will be passed to the callback, allowing you to set any additional properties on the object before it is given to its consumer. + +### Rebinding + +The `rebinding` method allows you to listen for when a service is re-bound to the container, meaning it is registered again or overridden after its initial binding. This can be useful when you need to update dependencies or modify behavior each time a specific binding is updated: + + use App\Contracts\PodcastPublisher; + use App\Services\SpotifyPublisher; + use App\Services\TransistorPublisher; + use Illuminate\Contracts\Foundation\Application; + + $this->app->bind(PodcastPublisher::class, SpotifyPublisher::class); + + $this->app->rebinding( + PodcastPublisher::class, + function (Application $app, PodcastPublisher $newInstance) { + // + }, + ); + + // New binding will trigger rebinding closure... + $this->app->bind(PodcastPublisher::class, TransistorPublisher::class); + ## PSR-11 From 364b634ccee1fff2794ce0e11f8223bd0c10604b Mon Sep 17 00:00:00 2001 From: Pradeep Singh Date: Tue, 29 Oct 2024 20:44:08 +0530 Subject: [PATCH 1906/2609] Slack notification prerequisites (#9988) * Update notifications.md * Update notifications.md --------- Co-authored-by: Taylor Otwell --- notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifications.md b/notifications.md index 995d404cb2f..a6dea95c10c 100644 --- a/notifications.md +++ b/notifications.md @@ -1126,7 +1126,7 @@ composer require laravel/slack-notification-channel Additionally, you must create a [Slack App](https://api.slack.com/apps?new_app=1) for your Slack workspace. -If you only need to send notifications to the same Slack workspace that the App is created in, you should ensure that your App has the `chat:write`, `chat:write.public`, and `chat:write.customize` scopes. These scopes can be added from the "OAuth & Permissions" App management tab within Slack. +If you only need to send notifications to the same Slack workspace that the App is created in, you should ensure that your App has the `chat:write`, `chat:write.public`, and `chat:write.customize` scopes. If you want to send messages as your Slack App, you should ensure that your App also has the `chat:write:bot` scope. These scopes can be added from the "OAuth & Permissions" App management tab within Slack. 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: From 1ee73420bebd77bf85f8451ab6028e32da210702 Mon Sep 17 00:00:00 2001 From: Sean Poynter-Smith Date: Tue, 29 Oct 2024 17:28:44 +0000 Subject: [PATCH 1907/2609] Fix typo (#10001) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 7d61ab789ec..cb25ab22080 100644 --- a/queues.md +++ b/queues.md @@ -878,7 +878,7 @@ When chaining jobs, you may use the `catch` method to specify a closure that sho > Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. -### Customizing The Queue a Connection +### Customizing The Queue and Connection #### Dispatching to a Particular Queue From e589c65973ef8668df016fc27f4f37e07d8abf26 Mon Sep 17 00:00:00 2001 From: Prince John Santillan <60916966+princejohnsantillan@users.noreply.github.com> Date: Wed, 30 Oct 2024 01:48:22 +0800 Subject: [PATCH 1908/2609] Add Using Slack's Block Kit Builder Template section (#9987) * Add Using Slack's Block Kit Builder Template section * formatting --------- Co-authored-by: Taylor Otwell --- notifications.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/notifications.md b/notifications.md index a6dea95c10c..17235144293 100644 --- a/notifications.md +++ b/notifications.md @@ -1174,6 +1174,44 @@ If a notification supports being sent as a Slack message, you should define a `t }); } + +#### Using Slack's Block Kit Builder Template + +Instead of using the fluent message builder methods to construct your Block Kit message, you may provide the raw JSON payload generated by Slack's Block Kit Builder to the `usingBlockKitTemplate` method: + + use Illuminate\Notifications\Slack\SlackMessage; + use Illuminate\Support\Str; + + /** + * Get the Slack representation of the notification. + */ + public function toSlack(object $notifiable): SlackMessage + { + $template = <<usingBlockKitTemplate($template); + } + ### Slack Interactivity From 1f6303f816ef026fb5bed346c7a0952b86160800 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 30 Oct 2024 09:06:16 -0500 Subject: [PATCH 1909/2609] add suggestion for using Bun (#10005) Bun is a Node/NPM alternative that can handle dependency management and frontent asset compilation, with better performance, and simpler installation/upgrading. --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index c890af7f45b..1277baa288a 100644 --- a/installation.md +++ b/installation.md @@ -61,7 +61,7 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ### Installing PHP and the Laravel Installer -Before creating your first Laravel application, make sure that your local machine has [PHP](https://php.net), [Composer](https://getcomposer.org), and [the Laravel installer](https://github.com/laravel/installer) installed. In addition, you should install [Node and NPM](https://nodejs.org) so that you can compile your application's frontend assets. +Before creating your first Laravel application, make sure that your local machine has [PHP](https://php.net), [Composer](https://getcomposer.org), and [the Laravel installer](https://github.com/laravel/installer) installed. In addition, you should install either [Node and NPM](https://nodejs.org) or [Bun](https://bun.sh/) so that you can compile your application's frontend assets. If you don't have PHP and Composer installed on your local machine, the following commands will install PHP, Composer, and the Laravel installer on macOS, Windows, or Linux: From 37ccd45736efc6fd562ea40645c9b9580c770181 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 31 Oct 2024 02:57:07 +1100 Subject: [PATCH 1910/2609] [11.x] without defer testing docs (#10006) * without defer * formatting --------- Co-authored-by: Taylor Otwell --- helpers.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/helpers.md b/helpers.md index 6986624beb9..24988b30230 100644 --- a/helpers.md +++ b/helpers.md @@ -2478,6 +2478,53 @@ protected $middleware = [ ]; ``` + +#### Disabling Deferred Functions in Tests + +When writing tests, it may be useful to disable deferred functions. You may call `withoutDefer` in your test to instruct Laravel to invoke all deferred functions immediately: + +```php tab=Pest +test('without defer', function () { + $this->withoutDefer(); + + // ... +}); +``` + +```php tab=PHPUnit +use Tests\TestCase; + +class ExampleTest extends TestCase +{ + public function test_without_defer(): void + { + $this->withoutDefer(); + + // ... + } +} +``` + +If you would like to disable deferred functions for all tests within a test case, you may call the `withoutDefer` method from the `setUp` method on your base `TestCase` class: + +```php +withoutDefer(); + }// [tl! add:end] +} +``` + ### Lottery From 1e8496c232c2e3f17179c7ad8d751f9c795ce16a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 30 Oct 2024 10:57:33 -0500 Subject: [PATCH 1911/2609] wip --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 24988b30230..bde5d85235d 100644 --- a/helpers.md +++ b/helpers.md @@ -2478,7 +2478,7 @@ protected $middleware = [ ]; ``` - + #### Disabling Deferred Functions in Tests When writing tests, it may be useful to disable deferred functions. You may call `withoutDefer` in your test to instruct Laravel to invoke all deferred functions immediately: From dde9f07b782ba5a3ff93e34471bf2b1a581b0a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilsen=20Hern=C3=A1ndez?= <13445515+wilsenhc@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:56:05 -0400 Subject: [PATCH 1912/2609] Add new vector method to migrations (#10007) add dimensions missing parameter to vector revert whitespace changes --- migrations.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/migrations.md b/migrations.md index d4613404ade..a0be8bed27a 100644 --- a/migrations.md +++ b/migrations.md @@ -458,6 +458,7 @@ The schema builder blueprint offers a variety of methods that correspond to the [uuidMorphs](#column-method-uuidMorphs) [ulid](#column-method-ulid) [uuid](#column-method-uuid) +[vector](#column-method-vector) [year](#column-method-year) @@ -918,6 +919,13 @@ The `uuid` method creates a `UUID` equivalent column: $table->uuid('id'); + +#### `vector()` {.collection-method} + +The `vector` method creates a `vector` equivalent column: + + $table->vector('embedding', dimensions: 100); + #### `year()` {.collection-method} From 88b6f0d99c9f89ca38266d9fa0c3b4ea3df3c85e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 31 Oct 2024 22:16:33 +0100 Subject: [PATCH 1913/2609] Add documentation for MongoDB support (#9945) * Add mentions to MongoDB in the docs * formatting * Update session.md --------- Co-authored-by: Taylor Otwell --- authentication.md | 4 +- cache.md | 7 ++++ database.md | 2 + documentation.md | 1 + mongodb.md | 99 +++++++++++++++++++++++++++++++++++++++++++++++ queues.md | 1 + session.md | 7 ++-- 7 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 mongodb.md diff --git a/authentication.md b/authentication.md index 8a56d1a435c..a16e00ac148 100644 --- a/authentication.md +++ b/authentication.md @@ -53,7 +53,9 @@ Want to get started fast? Install a [Laravel application starter kit](/docs/{{ve ### 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 provider 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. If your application is using MongoDB, check out MongoDB's official [Laravel user authentication documentation](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/user-authentication/) . 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. diff --git a/cache.md b/cache.md index e80e3a0a0f7..289641e6cfd 100644 --- a/cache.md +++ b/cache.md @@ -111,6 +111,13 @@ In addition, you should ensure that values are provided for the DynamoDB cache s ], ``` + +#### MongoDB + +If you are using MongoDB, a `mongodb` cache driver is provided by the official `mongodb/laravel-mongodb` package and can be configured using a `mongodb` database connection. MongoDB supports TTL indexes, which can be used to automatically clear expired cache items. + +For more information on configuring MongoDB, please refer to the MongoDB [Cache and Locks documentation](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/cache/). + ## Cache Usage diff --git a/database.md b/database.md index 0390f489696..829eca93572 100644 --- a/database.md +++ b/database.md @@ -27,6 +27,8 @@ Almost every modern web application interacts with a database. Laravel makes int +Additionally, MongoDB is supported via the `mongodb/laravel-mongodb` package, which is officially maintained by MongoDB. Check out the [Laravel MongoDB](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/) documentation for more information. + ### Configuration diff --git a/documentation.md b/documentation.md index b4e36d8c503..c0eecca09eb 100644 --- a/documentation.md +++ b/documentation.md @@ -64,6 +64,7 @@ - [Migrations](/docs/{{version}}/migrations) - [Seeding](/docs/{{version}}/seeding) - [Redis](/docs/{{version}}/redis) + - [MongoDB](/docs/{{version}}/mongodb) - ## Eloquent ORM - [Getting Started](/docs/{{version}}/eloquent) - [Relationships](/docs/{{version}}/eloquent-relationships) diff --git a/mongodb.md b/mongodb.md new file mode 100644 index 00000000000..af992cd0d24 --- /dev/null +++ b/mongodb.md @@ -0,0 +1,99 @@ +# MongoDB + +- [Introduction](#introduction) +- [Installation](#installation) + - [MongoDB Driver](#mongodb-driver) + - [Starting a MongoDB Server](#starting-a-mongodb-server) + - [Install the Laravel MongoDB Package](#install-the-laravel-mongodb-package) +- [Configuration](#configuration) +- [Features](#features) + + +## Introduction + +[MongoDB](https://www.mongodb.com/resources/products/fundamentals/why-use-mongodb) is one of the most popular NoSQL document-oriented database, used for its high write load (useful for analytics or IoT) and high availability (easy to set replica sets with automatic failover). It can also shard the database easily for horizontal scalability and has a powerful query language for doing aggregation, text search or geospatial queries. + +Instead of storing data in tables of rows or columns like SQL databases, each record in a MongoDB database is a document described in BSON, a binary representation of the data. Applications can then retrieve this information in a JSON format. It supports a wide variety of data types, including documents, arrays, embedded documents, and binary data. + +Before using MongoDB with Laravel, we recommend installing and using the `mongodb/laravel-mongodb` package via Composer. The `laravel-mongodb` package is officially maintained by MongoDB, and while MongoDB is natively supported by PHP through the MongoDB driver, the [Laravel MongoDB](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/) package provides a richer integration with Eloquent and other Laravel features: + +```shell +composer require mongodb/laravel-mongodb +``` + + +## Installation + + +### MongoDB Driver + +To connect to a MongoDB database, the `mongodb` PHP extension is required. If you are developing locally using [Laravel Herd](https://herd.laravel.com) or installed PHP via `php.new`, you already have this extension installed on your system. However, if you need to install the extension manually, you may do so via PECL: + +```shell +pecl install mongodb +``` + +For more information on installing the MongoDB PHP extension, check out the [MongoDB PHP extension installation instructions](https://www.php.net/manual/en/mongodb.installation.php). + + +### Starting a MongoDB Server + +The MongoDB Community Server can be used to run MongoDB locally and is available for installation on Windows, macOS, Linux, or as a Docker container. To learn how to install MongoDB, please refer to the [official MongoDB Community installation guide](https://docs.mongodb.com/manual/administration/install-community/). + +The connection string for the MongoDB server can be set in your `.env` file: + +```ini +MONGODB_URI="mongodb://localhost:27017" +MONGODB_DATABASE="laravel_app" +``` + +For hosting MongoDB in the cloud, consider using [MongoDB Atlas](https://www.mongodb.com/cloud/atlas). +To access a MongoDB Atlas cluster locally from your application, you will need to [add your own IP address in the cluster's network settings](https://www.mongodb.com/docs/atlas/security/add-ip-address-to-list/) to the project's IP Access List. + +The connection string for MongoDB Atlas can also be set in your `.env` file: + +```ini +MONGODB_URI="mongodb+srv://:@.mongodb.net/?retryWrites=true&w=majority" +MONGODB_DATABASE="laravel_app" +``` + + +### Install the Laravel MongoDB Package + +Finally, use Composer to install the Laravel MongoDB package: + +```shell +composer require mongodb/laravel-mongodb +``` + +> [!NOTE] +> This installation of the package will fail if the `mongodb` PHP extension is not installed. The PHP configuration can differ between the CLI and the web server, so ensure the extension is enabled in both configurations. + + +## Configuration + +You may configure your MongoDB connection via your application's `config/database.php` configuration file. Within this file, add a `mongodb` connection that utilizes the `mongodb` driver: + +```php +'connections' => [ + 'mongodb' => [ + 'driver' => 'mongodb', + 'dsn' => env('MONGODB_URI', 'mongodb://localhost:27017'), + 'database' => env('MONGODB_DATABASE', 'laravel_app'), + ], +], +``` + + +## Features + +Once your configuration is complete, you can use the `mongodb` package and database connection in your application to leverage a variety of powerful features: + +- [Using Eloquent](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/eloquent-models/), models can be stored in MongoDB collections. In addition to the standard Eloquent features, the Laravel MongoDB package provides additional features such as embedded relationships. The package also provides direct access to the MongoDB driver, which can be used to execute operations such as raw queries and aggregation pipelines. +- [Write complex queries](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/query-builder/) using the query builder. +- The `mongodb` [cache driver](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/cache/) is optimized to use MongoDB features such as TTL indexes to automatically clear expired cache entries. +- [Dispatch and process queued jobs](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/queues/) with the `mongodb` queue driver. +- [Storing files in GridFS](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/gridfs/), via the [GridFS Adapter for Flysystem](https://flysystem.thephpleague.com/docs/adapter/gridfs/). +- Most third party packages using a database connection or Eloquent can be used with MongoDB. + +To continue learning how to use MongoDB and Laravel, refer to MongoDB's [Quick Start guide](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/quick-start/). diff --git a/queues.md b/queues.md index b6dfffd8c1c..f90404984b2 100644 --- a/queues.md +++ b/queues.md @@ -151,6 +151,7 @@ The following dependencies are needed for the listed queue drivers. These depend - Amazon SQS: `aws/aws-sdk-php ~3.0` - Beanstalkd: `pda/pheanstalk ~5.0` - Redis: `predis/predis ~2.0` or phpredis PHP extension +- [MongoDB](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/queues/): `mongodb/laravel-mongodb` diff --git a/session.md b/session.md index e7ef049a5a6..7daa7d74481 100644 --- a/session.md +++ b/session.md @@ -245,7 +245,7 @@ If you need to regenerate the session ID and remove all data from the session in ## Session Blocking > [!WARNING] -> To utilize session blocking, your application must be using a cache driver that supports [atomic locks](/docs/{{version}}/cache#atomic-locks). Currently, those cache drivers include the `memcached`, `dynamodb`, `redis`, `database`, `file`, and `array` drivers. In addition, you may not use the `cookie` session driver. +> To utilize session blocking, your application must be using a cache driver that supports [atomic locks](/docs/{{version}}/cache#atomic-locks). Currently, those cache drivers include the `memcached`, `dynamodb`, `redis`, `mongodb` (included in the official `mongodb/laravel-mongodb` package), `database`, `file`, and `array` drivers. In addition, you may not use the `cookie` session driver. By default, Laravel allows requests using the same session to execute concurrently. So, for example, if you use a JavaScript HTTP library to make two HTTP requests to your application, they will both execute at the same time. For many applications, this is not a problem; however, session data loss can occur in a small subset of applications that make concurrent requests to two different application endpoints which both write data to the session. @@ -291,10 +291,9 @@ If none of the existing session drivers fit your application's needs, Laravel ma public function gc($lifetime) {} } -> [!NOTE] -> Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `MongoSessionHandler`. +Since Laravel does not include a default directory to house your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `MongoSessionHandler`. -Since the purpose of these methods is not readily understandable, let's quickly cover what each of the methods do: +Since the purpose of these methods is not readily understandable, here is an overview of the purpose of each method:
    From 1ae07b0e1cb7539b7c9c0f4a69eabbb93d50a3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEuris?= Date: Fri, 1 Nov 2024 15:40:37 +0200 Subject: [PATCH 1914/2609] Warn about automatically registered event on the upgrade guide (#10009) * Update upgrade.md * Update upgrade.md * Update upgrade.md --------- Co-authored-by: Taylor Otwell --- upgrade.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/upgrade.md b/upgrade.md index 0c1008e39db..85d842ce552 100644 --- a/upgrade.md +++ b/upgrade.md @@ -182,6 +182,20 @@ if ($e instanceof AuthenticationException) { } ``` + +#### Email Verification Notification on Registration + +**Likelihood Of Impact: Very Low** + +The `SendEmailVerificationNotification` listener is now automatically registered for the `Registered` event if it is not already registered by your application's `EventServiceProvider`. If your application's `EventServiceProvider` does not register this listener and you do not want Laravel to automatically register it for you, you should define an empty `configureEmailVerification` method in your application's `EventServiceProvider`: + +```php +protected function configureEmailVerification() +{ + // ... +} +``` + ### Cache From cc06dbe464daa76c06d96e5530110dfd36284248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 5 Nov 2024 01:47:48 +0100 Subject: [PATCH 1915/2609] Add documentation about version constraint to validation docs (#10012) * Add documentation about version constraint to validation docs * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation.md b/validation.md index a1aaa7b3681..907fa095699 100644 --- a/validation.md +++ b/validation.md @@ -1887,6 +1887,12 @@ The field under validation must be a valid [Universally Unique Lexicographically The field under validation must be a valid RFC 4122 (version 1, 3, 4, or 5) universally unique identifier (UUID). +You may also validate that the given UUID matches a UUID specification by version: + +```php +'uuid' => 'uuid:4' +``` + ## Conditionally Adding Rules From d973103c480300debe835bf11748435e5af58bfb Mon Sep 17 00:00:00 2001 From: Luis De la Cruz <98129640+cruzmediaorg@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:55:58 +0100 Subject: [PATCH 1916/2609] Adds type hint to array to match trait structure (#10018) --- eloquent.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eloquent.md b/eloquent.md index 9e7084cc881..3cdc7f0b0cd 100644 --- a/eloquent.md +++ b/eloquent.md @@ -843,7 +843,7 @@ So, to get started, you should define which model attributes you want to make ma /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = ['name']; } @@ -864,7 +864,7 @@ When assigning JSON columns, each column's mass assignable key must be specified /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'options->enabled', @@ -878,7 +878,7 @@ If you would like to make all of your attributes mass assignable, you may define /** * The attributes that aren't mass assignable. * - * @var array + * @var array|bool */ protected $guarded = []; From 73a37c89e6d04bc6941cd9d623b2150710556b25 Mon Sep 17 00:00:00 2001 From: Simone Folador Date: Mon, 11 Nov 2024 21:33:59 +0100 Subject: [PATCH 1917/2609] Update installation.md (#10023) * Update installation.md The proposed PowerShell command does not work if the PowerShell is not run as Administrator. If that's the case, the PowerShell just disappears without any error on both Windows 10 and Windows 11. By running the PowerShell as an administrator, the command is executed with success. * Update installation.md --------- Co-authored-by: Taylor Otwell --- installation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/installation.md b/installation.md index 1277baa288a..bf624e5d66f 100644 --- a/installation.md +++ b/installation.md @@ -70,6 +70,7 @@ If you don't have PHP and Composer installed on your local machine, the followin ``` ```shell tab=Windows PowerShell +# Run as administrator... Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows')) ``` From 8c527b5b6fa144cf21b1deae3058b4ba5b47cb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Rumi=C5=84ski?= Date: Tue, 12 Nov 2024 15:56:27 +0100 Subject: [PATCH 1918/2609] docs: update PHP CS Fixer spelling/link (#10025) --- pint.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pint.md b/pint.md index 50d6719a97a..71954ede7ed 100644 --- a/pint.md +++ b/pint.md @@ -11,7 +11,7 @@ ## Introduction -[Laravel Pint](https://github.com/laravel/pint) is an opinionated PHP code style fixer for minimalists. Pint is built on top of PHP-CS-Fixer and makes it simple to ensure that your code style stays clean and consistent. +[Laravel Pint](https://github.com/laravel/pint) is an opinionated PHP code style fixer for minimalists. Pint is built on top of [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) and makes it simple to ensure that your code style stays clean and consistent. Pint is automatically installed with all new Laravel applications so you may start using it immediately. By default, Pint does not require any configuration and will fix code style issues in your code by following the opinionated coding style of Laravel. @@ -116,7 +116,7 @@ However, if you wish, you may enable or disable specific rules in your `pint.jso } ``` -Pint is built on top of [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer). Therefore, you may use any of its rules to fix code style issues in your project: [PHP-CS-Fixer Configurator](https://mlocati.github.io/php-cs-fixer-configurator). +Pint is built on top of [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer). Therefore, you may use any of its rules to fix code style issues in your project: [PHP CS Fixer Configurator](https://mlocati.github.io/php-cs-fixer-configurator). ### Excluding Files / Folders From 7102a2df101517a6388a4280485eafda00d0f28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caio=20Galv=C3=A3o?= Date: Tue, 12 Nov 2024 11:59:28 -0300 Subject: [PATCH 1919/2609] docs(logging): fix default retention days for daily channel to 14 (#10024) --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index 3d33582d290..251a52d663b 100644 --- a/logging.md +++ b/logging.md @@ -93,7 +93,7 @@ Additionally, the retention policy for the `daily` channel can be configured via | Name | Description | Default | | ------ | ----------------------------------------------------------- | ------- | -| `days` | The number of days that daily log files should be retained. | `7` | +| `days` | The number of days that daily log files should be retained. | `14` |
    From f3f131ae61df7679e53454c3647d0fe63bbaeac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 12 Nov 2024 21:00:07 +0100 Subject: [PATCH 1920/2609] [11.x] Add MongoDB to Sail doc (#10020) * Add MongoDB to Sail doc * Update sail.md --------- Co-authored-by: Taylor Otwell --- sail.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sail.md b/sail.md index 6ba060a301f..f1bfd8c54b0 100644 --- a/sail.md +++ b/sail.md @@ -13,6 +13,7 @@ - [Executing Node / NPM Commands](#executing-node-npm-commands) - [Interacting With Databases](#interacting-with-sail-databases) - [MySQL](#mysql) + - [MongoDB](#mongodb) - [Redis](#redis) - [Meilisearch](#meilisearch) - [Typesense](#typesense) @@ -239,6 +240,23 @@ Once you have started your containers, you may connect to the MySQL instance wit 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 and the access credentials correspond to the values of your `DB_USERNAME` and `DB_PASSWORD` environment variables. Or, you may connect as the `root` user, which also utilizes the value of your `DB_PASSWORD` environment variable as its password. + +### MongoDB + +If you chose to install the [MongoDB](https://www.mongodb.com/) service when installing Sail, your application's `docker-compose.yml` file contains an entry for a [MongoDB Atlas Local](https://www.mongodb.com/docs/atlas/cli/current/atlas-cli-local-cloud/) container which provides the MongoDB document database with Atlas features like [Search Indexes](https://www.mongodb.com/docs/atlas/atlas-search/). 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 MongoDB instance within your application by setting your `MONGODB_URI` environment variable within your application's `.env` file to `mongodb://mongodb:27017`. Authentication is disabled by default, but you can set the `MONGODB_USERNAME` and `MONGODB_PASSWORD` environment variables to enable authentication before starting the `mongodb` container. Then, add the credentials to the connection string: + +```ini +MONGODB_USERNAME=user +MONGODB_PASSWORD=laravel +MONGODB_URI=mongodb://${MONGODB_USERNAME}:${MONGODB_PASSWORD}@mongodb:27017 +``` + +For seamless integration of MongoDB with your application, you can install the [official package maintained by MongoDB](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/). + +To connect to your application's MongoDB database from your local machine, you may use a graphical interface such as [Compass](https://www.mongodb.com/products/tools/compass). By default, the MongoDB database is accessible at `localhost` port `27017`. + ### Redis From 137c84001cb546324e63327cb0c08ca67d3cd5fc Mon Sep 17 00:00:00 2001 From: David Heremans Date: Tue, 12 Nov 2024 21:09:06 +0100 Subject: [PATCH 1921/2609] Introduction support popping items from stackable context item (#10014) * Introduction support popping items from stackable context item Documentation update for changes introduced in https://github.com/laravel/framework/pull/53403 * Update context.md * Update context.md * Update context.md --------- Co-authored-by: Taylor Otwell --- context.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/context.md b/context.md index a524d47508b..99eeaf19fad 100644 --- a/context.md +++ b/context.md @@ -229,6 +229,18 @@ The `pull` method may be used to retrieve information from the context and immed $value = Context::pull('key'); ``` +If context data is stored in a [stack](#stacks), you may pop items from the stack using the `pop` method: + +```php +Context::push('breadcrumbs', 'first_value', 'second_value'); + +Context::pop('breadcrumbs') +// second_value + +Context::get('breadcrumbs'); +// ['first_value'] +``` + If you would like to retrieve all of the information stored in the context, you may invoke the `all` method: ```php @@ -305,6 +317,7 @@ Context::addHiddenIf(/* ... */); Context::pushHidden(/* ... */); Context::getHidden(/* ... */); Context::pullHidden(/* ... */); +Context::popHidden(/* ... */); Context::onlyHidden(/* ... */); Context::allHidden(/* ... */); Context::hasHidden(/* ... */); From 3ed0888fdb29edbb04d290707a2819f739dc5b7b Mon Sep 17 00:00:00 2001 From: Davey Shafik Date: Thu, 14 Nov 2024 07:30:19 -0800 Subject: [PATCH 1922/2609] Remove unnecessary warning about Route::fallback() being last (#10026) --- routing.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/routing.md b/routing.md index f0fdbdd1900..3fda01168da 100644 --- a/routing.md +++ b/routing.md @@ -737,9 +737,6 @@ Using the `Route::fallback` method, you may define a route that will be executed // ... }); -> [!WARNING] -> The fallback route should always be the last route registered by your application. - ## Rate Limiting From fe38158e2fe3b32feb9f9ec238d0161373162f9c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 16 Nov 2024 01:51:20 +1100 Subject: [PATCH 1923/2609] [11.x] Document defining Pennant features externally (#10027) * Document defining features externally * Update pennant.md --------- Co-authored-by: Taylor Otwell --- pennant.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index c0783ae1b3f..fd9c757fc49 100644 --- a/pennant.md +++ b/pennant.md @@ -28,6 +28,7 @@ - [Adding Custom Pennant Drivers](#adding-custom-pennant-drivers) - [Implementing the Driver](#implementing-the-driver) - [Registering the Driver](#registering-the-driver) + - [Defining Features Externally](#defining-features-externally) - [Events](#events) @@ -1022,7 +1023,7 @@ class RedisFeatureDriver implements Driver Now, we just need to implement each of these methods using a Redis connection. For an example of how to implement each of these methods, take a look at the `Laravel\Pennant\Drivers\DatabaseDriver` in the [Pennant source code](https://github.com/laravel/pennant/blob/1.x/src/Drivers/DatabaseDriver.php) -> [!NOTE] +> [!NOTE] > Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `RedisFeatureDriver`. @@ -1075,6 +1076,32 @@ Once the driver has been registered, you may use the `redis` driver in your appl ], + +### Defining Features Externally + +If your driver is a wrapper around a third-party feature flag platform, you will likely define features on the platform rather than using Pennant's `Feature::define` method. If that is the case, your custom driver should also implement the `Laravel\Pennant\Contracts\DefinesFeaturesExternally` interface: + +```php + ## Events From 63ffde91d51bd2be64036899966496461423f6b1 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 18 Nov 2024 08:18:01 -0600 Subject: [PATCH 1924/2609] update docblock for Model `$hidden` (#10030) syncs up both the comment and the var declaration with `laravel/laravel` https://github.com/laravel/laravel/pull/6495 --- eloquent-serialization.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-serialization.md b/eloquent-serialization.md index 30821074124..45f4df9cce7 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -84,9 +84,9 @@ Sometimes you may wish to limit the attributes, such as passwords, that are incl class User extends Model { /** - * The attributes that should be hidden for arrays. + * The attributes that should be hidden for serialization. * - * @var array + * @var array */ protected $hidden = ['password']; } From 3dfff31349118d0a1a3e413336baf19b02ced97e Mon Sep 17 00:00:00 2001 From: jordyvanderhaegen Date: Tue, 19 Nov 2024 20:44:21 +0100 Subject: [PATCH 1925/2609] docs: replace deprecated PHP CS rules (#10033) --- pint.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pint.md b/pint.md index 5de25ca04ba..d4741dc41d3 100644 --- a/pint.md +++ b/pint.md @@ -115,10 +115,10 @@ However, if you wish, you may enable or disable specific rules in your `pint.jso "preset": "laravel", "rules": { "simplified_null_return": true, - "braces": false, - "new_with_braces": { - "anonymous_class": false, - "named_class": false + "array_indentation": false, + "new_with_parentheses": { + "anonymous_class": true, + "named_class": true } } } From 3e3ecda52ed7aac946f9338e9e9a848d58f565a1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 19 Nov 2024 13:57:35 -0600 Subject: [PATCH 1926/2609] wip --- requests.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/requests.md b/requests.md index bfcc95d2467..4b4cc9e412d 100644 --- a/requests.md +++ b/requests.md @@ -346,6 +346,12 @@ Input values that correspond to [PHP enums](https://www.php.net/manual/en/langua $status = $request->enum('status', Status::class); +If the input value is an array of values that correspond to a PHP enum, you may use the `enums` method to retrieve the array of values as enum instances: + + use App\Enums\Product; + + $products = $request->enums('products', Product::class); + #### Retrieving Input via Dynamic Properties From c0b77dd72e27ab1f37a4ba9284fffaad1aa999c8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 19 Nov 2024 14:10:39 -0600 Subject: [PATCH 1927/2609] wip --- http-client.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/http-client.md b/http-client.md index 2e49325fe7b..f613fe09032 100644 --- a/http-client.md +++ b/http-client.md @@ -530,6 +530,15 @@ If you would like to specify a fallback URL pattern that will stub all unmatched '*' => Http::response('Hello World', 200, ['Headers']), ]); + +#### Faking Connection Exceptions + +Sometimes you may need to test your application's behavior if the HTTP client encounters an `Illuminate\Http\Client\ConnectionException` when attempting to make a request. You can instruct the HTTP client to throw a connection exception using the `failedConnection` method: + + Http::fake([ + 'github.com/*' => Http::failedConnection(), + ]); + #### Faking Response Sequences From de55c9c4a1b77462a4dfd1c40777b55380c16d37 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 19 Nov 2024 14:25:58 -0600 Subject: [PATCH 1928/2609] wip --- scheduling.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/scheduling.md b/scheduling.md index 5ef90e01c73..39ed28f3c8e 100644 --- a/scheduling.md +++ b/scheduling.md @@ -11,6 +11,7 @@ - [Running Tasks on One Server](#running-tasks-on-one-server) - [Background Tasks](#background-tasks) - [Maintenance Mode](#maintenance-mode) + - [Schedule Groups](#schedule-groups) - [Running the Scheduler](#running-the-scheduler) - [Sub-Minute Scheduled Tasks](#sub-minute-scheduled-tasks) - [Running the Scheduler Locally](#running-the-scheduler-locally) @@ -364,6 +365,25 @@ Your application's scheduled tasks will not run when the application is in [main Schedule::command('emails:send')->evenInMaintenanceMode(); + +### Schedule Groups + +When defining multiple scheduled tasks with similar configurations, you can use Laravel’s task grouping feature to avoid repeating the same settings for each task. Grouping tasks simplifies your code and ensures consistency across related tasks. + +To create a group of scheduled tasks, invoke the desired task configuration methods, followed by the `group` method. The `group` method accepts a closure that is responsible for defining the tasks that share the specified configuration: + +```php +use Illuminate\Support\Facades\Schedule; + +Schedule::daily() + ->onOneServer() + ->timezone('America/New_York') + ->group(function () { + Schedule::command('emails:send --force'); + Schedule::command('emails:prune'); + }); +``` + ## Running the Scheduler From f81965f336cd20e8af9999cde362175547b48f02 Mon Sep 17 00:00:00 2001 From: Julius Kiekbusch Date: Tue, 19 Nov 2024 21:40:09 +0100 Subject: [PATCH 1929/2609] [11.x] Add PHP 8.4 (#10019) * Add PHP 8.4 * Update PHP 8.4 in Sail --- releases.md | 4 ++-- sail.md | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/releases.md b/releases.md index 894f97c5123..ff5401eae82 100644 --- a/releases.md +++ b/releases.md @@ -27,8 +27,8 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | --- | --- | --- | --- | --- | | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | -| 11 | 8.2 - 8.3 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | -| 12 | 8.2 - 8.3 | Q1 2025 | Q3 2026 | Q1 2027 | +| 11 | 8.2 - 8.4 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | +| 12 | 8.2 - 8.4 | Q1 2025 | Q3 2026 | Q1 2027 | diff --git a/sail.md b/sail.md index f1bfd8c54b0..dcc10298226 100644 --- a/sail.md +++ b/sail.md @@ -416,9 +416,12 @@ sail tinker ## PHP Versions -Sail currently supports serving your application via PHP 8.3, 8.2, 8.1, or PHP 8.0. The default PHP version used by Sail is currently PHP 8.3. 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: +Sail currently supports serving your application via PHP 8.4, 8.3, 8.2, 8.1, or PHP 8.0. The default PHP version used by Sail is currently PHP 8.3. 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.4 +context: ./vendor/laravel/sail/runtimes/8.4 + # PHP 8.3 context: ./vendor/laravel/sail/runtimes/8.3 From c4151f0848b5fb1188b7d8349196f7817fcc510f Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:58:38 +0900 Subject: [PATCH 1930/2609] Remove return keyword from example (#10038) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 43d71477dab..e3841ffc850 100644 --- a/validation.md +++ b/validation.md @@ -1354,7 +1354,7 @@ If you would like to customize the query executed by the validation rule, you ma 'email' => [ 'required', Rule::exists('staff')->where(function (Builder $query) { - return $query->where('account_id', 1); + $query->where('account_id', 1); }), ], ]); From 0db6ab246107750937a39f29489a0af33be7266b Mon Sep 17 00:00:00 2001 From: Noboru Shiroiwa <14008307+nshiro@users.noreply.github.com> Date: Thu, 21 Nov 2024 00:01:02 +0900 Subject: [PATCH 1931/2609] Update contextual attributes example (#10037) * Update contextual attributes example * Update container.md --------- Co-authored-by: Taylor Otwell --- container.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/container.md b/container.md index 61b90d81f2a..a538174bab5 100644 --- a/container.md +++ b/container.md @@ -266,15 +266,17 @@ In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config` namespace App\Http\Controllers; +use App\Models\Photo; use Illuminate\Container\Attributes\Auth; use Illuminate\Container\Attributes\Cache; use Illuminate\Container\Attributes\Config; use Illuminate\Container\Attributes\DB; use Illuminate\Container\Attributes\Log; +use Illuminate\Container\Attributes\RouteParameter; use Illuminate\Container\Attributes\Tag; use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Cache\Repository; -use Illuminate\Contracts\Database\Connection; +use Illuminate\Database\Connection; use Psr\Log\LoggerInterface; class PhotoController extends Controller @@ -285,6 +287,7 @@ class PhotoController extends Controller #[Config('app.timezone')] protected string $timezone, #[DB('mysql')] protected Connection $connection, #[Log('daily')] protected LoggerInterface $log, + #[RouteParameter('photo')] protected Photo $photo, #[Tag('reports')] protected iterable $reports, ) { From ea8c67bb179ffd41616900519f17f2e9c58e5e47 Mon Sep 17 00:00:00 2001 From: Ahmadreza Bashari <61243238+ahmadreza1383@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:53:38 +0330 Subject: [PATCH 1932/2609] Add assertCount for filesystem doc (#10041) * Add assertCount for filesystem doc * Update filesystem.md --------- Co-authored-by: Taylor Otwell --- filesystem.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/filesystem.md b/filesystem.md index 0f3c04f78e7..5229c37d1cc 100644 --- a/filesystem.md +++ b/filesystem.md @@ -701,6 +701,9 @@ test('albums can be uploaded', function () { Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); + // Assert that the number of files in a given directory matches the expected count... + Storage::disk('photos')->assertCount('/wallpapers', 2); + // Assert that a given directory is empty... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); }); @@ -734,6 +737,9 @@ class ExampleTest extends TestCase Storage::disk('photos')->assertMissing('missing.jpg'); Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']); + // Assert that the number of files in a given directory matches the expected count... + Storage::disk('photos')->assertCount('/wallpapers', 2); + // Assert that a given directory is empty... Storage::disk('photos')->assertDirectoryEmpty('/wallpapers'); } From bf57399422909dce882226ede0a560d1fe3aa2ee Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 25 Nov 2024 17:47:08 -0600 Subject: [PATCH 1933/2609] wip --- sail.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sail.md b/sail.md index f1bfd8c54b0..903914475d8 100644 --- a/sail.md +++ b/sail.md @@ -194,11 +194,11 @@ docker run --rm \ -u "$(id -u):$(id -g)" \ -v "$(pwd):/var/www/html" \ -w /var/www/html \ - laravelsail/php83-composer:latest \ + laravelsail/php84-composer:latest \ composer install --ignore-platform-reqs ``` -When using the `laravelsail/phpXX-composer` image, you should use the same version of PHP that you plan to use for your application (`80`, `81`, `82`, or `83`). +When using the `laravelsail/phpXX-composer` image, you should use the same version of PHP that you plan to use for your application (`80`, `81`, `82`, `83`, or `84`). ### Executing Artisan Commands @@ -416,9 +416,12 @@ sail tinker ## PHP Versions -Sail currently supports serving your application via PHP 8.3, 8.2, 8.1, or PHP 8.0. The default PHP version used by Sail is currently PHP 8.3. 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: +Sail currently supports serving your application via PHP 8.4, 8.3, 8.2, 8.1, or PHP 8.0. The default PHP version used by Sail is currently PHP 8.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.4 +context: ./vendor/laravel/sail/runtimes/8.4 + # PHP 8.3 context: ./vendor/laravel/sail/runtimes/8.3 From f821f3d559de9f57121dc5b6530eb088d988aa00 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 26 Nov 2024 09:35:18 -0600 Subject: [PATCH 1934/2609] wip --- installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/installation.md b/installation.md index bf624e5d66f..559595d7c35 100644 --- a/installation.md +++ b/installation.md @@ -66,16 +66,16 @@ Before creating your first Laravel application, make sure that your local machin If you don't have PHP and Composer installed on your local machine, the following commands will install PHP, Composer, and the Laravel installer on macOS, Windows, or Linux: ```shell tab=macOS -/bin/bash -c "$(curl -fsSL https://php.new/install/mac)" +/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.3)" ``` ```shell tab=Windows PowerShell # Run as administrator... -Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows')) +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows/8.3')) ``` ```shell tab=Linux -/bin/bash -c "$(curl -fsSL https://php.new/install/linux)" +/bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.3)" ``` After running one of the commands above, you should restart your terminal session. To update PHP, Composer, and the Laravel installer after installing them via `php.new`, you can re-run the command in your terminal. From 65a9f2e285fad310d2a3de756877b375232ff9fc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 26 Nov 2024 15:57:30 -0600 Subject: [PATCH 1935/2609] convenience methods --- http-client.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/http-client.md b/http-client.md index f613fe09032..cd3f01a30c3 100644 --- a/http-client.md +++ b/http-client.md @@ -530,6 +530,14 @@ If you would like to specify a fallback URL pattern that will stub all unmatched '*' => Http::response('Hello World', 200, ['Headers']), ]); +For convenience, simple string, JSON, and empty responses may be generated by providing a string, array, or integer as the response: + + Http::fake([ + 'google.com/*' => 'Hello World', + 'github.com/*' => ['foo' => 'bar'], + 'chatgpt.com/*' => 200, + ]); + #### Faking Connection Exceptions From d468548b1184fc289e09888881de4664f8936ec4 Mon Sep 17 00:00:00 2001 From: Abdel Elrafa Date: Fri, 29 Nov 2024 11:08:17 -0500 Subject: [PATCH 1936/2609] [11.x] Clarify how schedule:work will handle sub-minute tasks. (#10047) * Clarify how schedule:work will handle sub-minute tasks. * Update scheduling.md --------- Co-authored-by: Taylor Otwell --- scheduling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index 39ed28f3c8e..dcf7f2db019 100644 --- a/scheduling.md +++ b/scheduling.md @@ -430,7 +430,7 @@ php artisan schedule:interrupt ### 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: +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. When sub-minute tasks are defined, the scheduler will continue running within each minute to process those tasks: ```shell php artisan schedule:work From 660e0c0b5d1a90c8d7e04b1711032d4499496ee2 Mon Sep 17 00:00:00 2001 From: Owen Andrews Date: Wed, 4 Dec 2024 03:22:19 +1100 Subject: [PATCH 1937/2609] [11.x] Improve explanation of skipWhile and skipUntil (#10050) * Improve explanation of skipWhile and skipUntil * Fix spelling mistakes --- collections.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collections.md b/collections.md index a963e8bd22e..836cbe4d89b 100644 --- a/collections.md +++ b/collections.md @@ -2304,7 +2304,7 @@ The `skip` method returns a new collection, with the given number of elements re #### `skipUntil()` {.collection-method} -The `skipUntil` method skips over items from the collection until the given callback returns `true` and then returns the remaining items in the collection as a new collection instance: +The `skipUntil` method skips over items from the collection while the given callback returns `false`. Once the callback returns `true` all of the remaining items in the collection will be returned as a new collection: $collection = collect([1, 2, 3, 4]); @@ -2332,7 +2332,7 @@ You may also pass a simple value to the `skipUntil` method to skip all items unt #### `skipWhile()` {.collection-method} -The `skipWhile` method skips over items from the collection while the given callback returns `true` and then returns the remaining items in the collection as a new collection: +The `skipWhile` method skips over items from the collection while the given callback returns `true`. Once the callback returns `false` all of the remaining items in the collection will be returned as a new collection: $collection = collect([1, 2, 3, 4]); From f23019a2ff3a4d937f4b876190031881a7cca947 Mon Sep 17 00:00:00 2001 From: Artfaith <25136754+serious-angel@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:51:48 +0200 Subject: [PATCH 1938/2609] Allow only /index.php to be passed to FastCGI. (#10053) --- deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index ccf8ee042b9..fc8b049e1b4 100644 --- a/deployment.md +++ b/deployment.md @@ -77,7 +77,7 @@ server { error_page 404 /index.php; - location ~ \.php$ { + location ~ ^/index\.php(/|$) { { fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; From ad340137755df077c5efe7d0a57e3579aa1e0ca2 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Fri, 6 Dec 2024 14:22:30 -0500 Subject: [PATCH 1939/2609] [11x.] Add queued listener `backoff` docs (#10052) * Add queued listener `backoff` docs * Update events.md * Update events.md --------- Co-authored-by: Taylor Otwell --- events.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/events.md b/events.md index 9fffbe352ac..dcfac0fe8f1 100644 --- a/events.md +++ b/events.md @@ -487,6 +487,40 @@ As an alternative to defining how many times a listener may be attempted before return now()->addMinutes(5); } + +#### Specifying Queued Listener Backoff + +If you would like to configure how many seconds Laravel should wait before retrying a listener that has encountered an exception, you may do so by defining a `backoff` property on your listener class: + + /** + * The number of seconds to wait before retrying the queued listener. + * + * @var int + */ + public $backoff = 3; + +If you require more complex logic for determining the listeners's backoff time, you may define a `backoff` method on your listener class: + + /** + * Calculate the number of seconds to wait before retrying the queued listener. + */ + public function backoff(): int + { + 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 second for the first retry, 5 seconds for the second retry, 10 seconds for the third retry, and 10 seconds for every subsequent retry if there are more attempts remaining: + + /** + * Calculate the number of seconds to wait before retrying the queued listener. + * + * @return array + */ + public function backoff(): array + { + return [1, 5, 10]; + } + ## Dispatching Events From f8931dd81ba4d3039c549b787130c1542991681c Mon Sep 17 00:00:00 2001 From: "Md. Rasel" Date: Sun, 8 Dec 2024 21:19:17 +0600 Subject: [PATCH 1940/2609] Update deployment.md (#10055) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update deployment.md left double curly brace issue. * Update deployment.md Co-authored-by: Señor Henrik Nordquist --------- Co-authored-by: Taylor Otwell Co-authored-by: Señor Henrik Nordquist --- deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.md b/deployment.md index fc8b049e1b4..11ad9a88089 100644 --- a/deployment.md +++ b/deployment.md @@ -77,7 +77,7 @@ server { error_page 404 /index.php; - location ~ ^/index\.php(/|$) { { + location ~ ^/index\.php(/|$) { fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; From 667396a7971720b717079436ff1fe8682168d72e Mon Sep 17 00:00:00 2001 From: Fady Bark <71821989+Bark-fa@users.noreply.github.com> Date: Tue, 10 Dec 2024 21:06:31 +0200 Subject: [PATCH 1941/2609] Update billing.md to include new billing meters docs (#10045) * Update billing.md to include new billing meters docs * formatting * formatting * Update billing.md * Update billing.md --------- Co-authored-by: Taylor Otwell --- billing.md | 49 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/billing.md b/billing.md index 782851c54d5..294f6827856 100644 --- a/billing.md +++ b/billing.md @@ -35,7 +35,7 @@ - [Subscription Quantity](#subscription-quantity) - [Subscriptions With Multiple Products](#subscriptions-with-multiple-products) - [Multiple Subscriptions](#multiple-subscriptions) - - [Metered Billing](#metered-billing) + - [Usage Based Billing](#usage-based-billing) - [Subscription Taxes](#subscription-taxes) - [Subscription Anchor Date](#subscription-anchor-date) - [Canceling Subscriptions](#cancelling-subscriptions) @@ -1309,12 +1309,12 @@ Of course, you may also cancel the subscription entirely: $user->subscription('swimming')->cancel(); - -### Metered Billing + +### Usage Based Billing -[Metered billing](https://stripe.com/docs/billing/subscriptions/metered-billing) allows you to charge customers based on their product usage during a billing cycle. For example, you may charge customers based on the number of text messages or emails they send per month. +[Usage based billing](https://stripe.com/docs/billing/subscriptions/metered-billing) allows you to charge customers based on their product usage during a billing cycle. For example, you may charge customers based on the number of text messages or emails they send per month. -To start using metered billing, you will first need to create a new product in your Stripe dashboard with a metered price. Then, use the `meteredPrice` to add the metered price ID to a customer subscription: +To start using usage billing, you will first need to create a new product in your Stripe dashboard with a [usage based billing model](https://docs.stripe.com/billing/subscriptions/usage-based/implementation-guide) and a [meter](https://docs.stripe.com/billing/subscriptions/usage-based/recording-usage#configure-meter). After creating the meter, store the associated event name and meter ID, which you will need to report and retrieve usage. Then, use the `meteredPrice` method to add the metered price ID to a customer subscription: use Illuminate\Http\Request; @@ -1340,54 +1340,33 @@ You may also start a metered subscription via [Stripe Checkout](#checkout): #### Reporting Usage -As your customer uses your application, you will report their usage to Stripe so that they can be billed accurately. To increment the usage of a metered subscription, you may use the `reportUsage` method: +As your customer uses your application, you will report their usage to Stripe so that they can be billed accurately. To report the usage of a metered event, you may use the `reportMeterEvent` method on your `Billable` model: $user = User::find(1); - $user->subscription('default')->reportUsage(); + $user->reportMeterEvent('emails-sent'); By default, a "usage quantity" of 1 is added to the billing period. Alternatively, you may pass a specific amount of "usage" to add to the customer's usage for the billing period: $user = User::find(1); - $user->subscription('default')->reportUsage(15); + $user->reportMeterEvent('emails-sent', quantity: 15); -If your application offers multiple prices on a single subscription, you will need to use the `reportUsageFor` method to specify the metered price you want to report usage for: +To retrieve a customer's event summary for a meter, you may use a `Billable` instance's `meterEventSummaries` method: $user = User::find(1); - $user->subscription('default')->reportUsageFor('price_metered', 15); + $meterUsage = $user->meterEventSummaries($meterId); -Sometimes, you may need to update usage which you have previously reported. To accomplish this, you may pass a timestamp or a `DateTimeInterface` instance as the second parameter to `reportUsage`. When doing so, Stripe will update the usage that was reported at that given time. You can continue to update previous usage records as the given date and time is still within the current billing period: + $meterUsage->first()->aggregated_value // 10 - $user = User::find(1); - - $user->subscription('default')->reportUsage(5, $timestamp); - - -#### Retrieving Usage Records - -To retrieve a customer's past usage, you may use a subscription instance's `usageRecords` method: - - $user = User::find(1); - - $usageRecords = $user->subscription('default')->usageRecords(); +Please refer to Stripe's [Meter Event Summary object documentation](https://docs.stripe.com/api/billing/meter-event_summary/object) for more information on meter event summaries. -If your application offers multiple prices on a single subscription, you may use the `usageRecordsFor` method to specify the metered price that you wish to retrieve usage records for: +To [list all meters](https://docs.stripe.com/api/billing/meter/list), you may use a `Billable` instance's `meters` method: $user = User::find(1); - $usageRecords = $user->subscription('default')->usageRecordsFor('price_metered'); - -The `usageRecords` and `usageRecordsFor` methods return a Collection instance containing an associative array of usage records. You may iterate over this array to display a customer's total usage: - - @foreach ($usageRecords as $usageRecord) - - Period Starting: {{ $usageRecord['period']['start'] }} - - Period Ending: {{ $usageRecord['period']['end'] }} - - Total Usage: {{ $usageRecord['total_usage'] }} - @endforeach - -For a full reference of all usage data returned and how to use Stripe's cursor based pagination, please consult [the official Stripe API documentation](https://stripe.com/docs/api/usage_records/subscription_item_summary_list). + $user->meters(); ### Subscription Taxes From 519c46b94461471dcb4bf2f4692e4edb481b808c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Dec 2024 13:19:25 -0600 Subject: [PATCH 1942/2609] message truncation --- http-client.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/http-client.md b/http-client.md index cd3f01a30c3..34b8396c730 100644 --- a/http-client.md +++ b/http-client.md @@ -337,6 +337,16 @@ If you would like to perform some additional logic before the exception is throw // ... })->json(); +By default, `RequestException` messages are truncated to 120 characters when logged or reported. To customize or disable this behavior, you may utilize the `truncateRequestExceptionsAt` and `dontTruncateRequestExceptions` methods when configuring your application's exception handling behavior in your `bootstrap/app.php` file: + + ->withExceptions(function (Exceptions $exceptions) { + // Truncate request exception messages to 240 characters... + $exceptions->truncateRequestExceptionsAt(240); + + // Disable request exception message truncation... + $exceptions->dontTruncateRequestExceptions(); + }) + ### Guzzle Middleware From ace0221459d884e41a90aa4c3a23cff35248a5d4 Mon Sep 17 00:00:00 2001 From: Zakaria Arrid <35905260+zakariaarrid@users.noreply.github.com> Date: Tue, 10 Dec 2024 20:30:45 +0100 Subject: [PATCH 1943/2609] [11.x] pingOnSuccessIf and pingOnFailureIf -scheduling.md- PR:#53795 (#10057) * [11.x] pingOnSuccessIf and pingOnFailureIf -scheduling.md- PR:#53795 * Update scheduling.md --------- Co-authored-by: Taylor Otwell --- scheduling.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/scheduling.md b/scheduling.md index dcf7f2db019..0cb90589a6b 100644 --- a/scheduling.md +++ b/scheduling.md @@ -519,19 +519,24 @@ Using the `pingBefore` and `thenPing` methods, the scheduler can automatically p ->pingBefore($url) ->thenPing($url); -The `pingBeforeIf` and `thenPingIf` methods may be used to ping a given URL only if a given condition is `true`: +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() - ->pingBeforeIf($condition, $url) - ->thenPingIf($condition, $url); + ->pingOnSuccess($successUrl) + ->pingOnFailure($failureUrl); -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: +The `pingBeforeIf`,`thenPingIf`,`pingOnSuccessIf`, and `pingOnFailureIf` methods may be used to ping a given URL only if a given condition is `true`: Schedule::command('emails:send') ->daily() - ->pingOnSuccess($successUrl) - ->pingOnFailure($failureUrl); + ->pingBeforeIf($condition, $url) + ->thenPingIf($condition, $url); + + Schedule::command('emails:send') + ->daily() + ->pingOnSuccessIf($condition, $successUrl) + ->pingOnFailureIf($condition, $failureUrl); ## Events From cf389d90871ca35d24b1fd1f8d728c3ffe085edd Mon Sep 17 00:00:00 2001 From: Benjamin Crozat Date: Wed, 11 Dec 2024 17:20:24 +0100 Subject: [PATCH 1944/2609] Updated Telescope setup for local environments (#10064) * Update telescope.md * Update telescope.md * Update telescope.md --------- Co-authored-by: Taylor Otwell --- telescope.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telescope.md b/telescope.md index 38457bf70d0..6e3e24f3b6d 100644 --- a/telescope.md +++ b/telescope.md @@ -78,7 +78,7 @@ After running `telescope:install`, you should remove the `TelescopeServiceProvid */ public function register(): void { - if ($this->app->environment('local')) { + if ($this->app->environment('local') && class_exists(\Laravel\Telescope\TelescopeServiceProvider::class)) { $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); $this->app->register(TelescopeServiceProvider::class); } From e49f5b8d02f6c41ee5952e2630043173bd707dae Mon Sep 17 00:00:00 2001 From: Andy Hinkle Date: Wed, 11 Dec 2024 10:34:08 -0600 Subject: [PATCH 1945/2609] bump installation docs to 8.4 (#10061) --- installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/installation.md b/installation.md index 559595d7c35..548503f3cf9 100644 --- a/installation.md +++ b/installation.md @@ -66,16 +66,16 @@ Before creating your first Laravel application, make sure that your local machin If you don't have PHP and Composer installed on your local machine, the following commands will install PHP, Composer, and the Laravel installer on macOS, Windows, or Linux: ```shell tab=macOS -/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.3)" +/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.4)" ``` ```shell tab=Windows PowerShell # Run as administrator... -Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows/8.3')) +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows/8.4')) ``` ```shell tab=Linux -/bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.3)" +/bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.4)" ``` After running one of the commands above, you should restart your terminal session. To update PHP, Composer, and the Laravel installer after installing them via `php.new`, you can re-run the command in your terminal. From 9fdd948a5a10ecef51622b903f66d8f76fc3e85d Mon Sep 17 00:00:00 2001 From: Chetan Patel <87816669+ChetanTechnource@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:13:34 +0530 Subject: [PATCH 1946/2609] Storage:link source and target directory (#10062) Storage:link source and target directory for beginners --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index 5229c37d1cc..79cfa25fb16 100644 --- a/filesystem.md +++ b/filesystem.md @@ -54,7 +54,7 @@ When using the `local` driver, all file operations are relative to the `root` di The `public` disk included in your application's `filesystems` configuration file is intended for files that are going to be publicly accessible. By default, the `public` disk uses the `local` driver and stores its files in `storage/app/public`. -To make these files accessible from the web, you should create a symbolic link from `public/storage` to `storage/app/public`. Utilizing this folder convention will keep your publicly accessible files in one directory that can be easily shared across deployments when using zero down-time deployment systems like [Envoyer](https://envoyer.io). +To make these files accessible from the web, you should create a symbolic link from source directory `storage/app/public` to target directory `public/storage`. Utilizing this folder convention will keep your publicly accessible files in one directory that can be easily shared across deployments when using zero down-time deployment systems like [Envoyer](https://envoyer.io). To create the symbolic link, you may use the `storage:link` Artisan command: From a8861d2e1b82d8d0ef11ef8138af59d718bac386 Mon Sep 17 00:00:00 2001 From: "Dr. Adam Nielsen" <1765602+iwasherefirst2@users.noreply.github.com> Date: Thu, 12 Dec 2024 00:36:01 +0100 Subject: [PATCH 1947/2609] Add missing steps for enabling Xdebug in Laravel Sail docs (#10067) * Add missing steps for enabling Xdebug in Laravel Sail docs * formatting * formatting --------- Co-authored-by: Taylor Otwell --- sail.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 903914475d8..2c0aed7741f 100644 --- a/sail.md +++ b/sail.md @@ -496,12 +496,25 @@ sail share --subdomain=my-sail-site ## Debugging With Xdebug -Laravel Sail's Docker configuration includes support for [Xdebug](https://xdebug.org/), a popular and powerful debugger for PHP. In order to enable Xdebug, you will need to add a few variables to your application's `.env` file to [configure Xdebug](https://xdebug.org/docs/step_debug#mode). To enable Xdebug you must set the appropriate mode(s) before starting Sail: +Laravel Sail's Docker configuration includes support for [Xdebug](https://xdebug.org/), a popular and powerful debugger for PHP. To enable Xdebug, ensure you have [published your Sail configuration](#sail-customization). Then, add the following variables to your application's `.env` file to configure Xdebug: ```ini SAIL_XDEBUG_MODE=develop,debug,coverage ``` +Next, ensure that your published `php.ini` file includes the following configuration so that Xdebug is activated in the specified modes: + +```ini +[xdebug] +xdebug.mode=${XDEBUG_MODE} +``` + +After modifying the `php.ini` file, remember to rebuild your Docker images so that your changes to the `php.ini` file take effect: + +```shell +sail build --no-cache +``` + #### Linux Host IP Configuration Internally, the `XDEBUG_CONFIG` environment variable is defined as `client_host=host.docker.internal` so that Xdebug will be properly configured for Mac and Windows (WSL2). If your local machine is running Linux and you're using Docker 20.10+, `host.docker.internal` is available, and no manual configuration is required. From 7e89b0d5ae05e39854aa7e78c44bdc9ff6de624d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Dec 2024 13:41:40 -0600 Subject: [PATCH 1948/2609] wip --- urls.md | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/urls.md b/urls.md index 35a730ae271..5becbe7d49f 100644 --- a/urls.md +++ b/urls.md @@ -238,20 +238,9 @@ Setting URL default values can interfere with Laravel's handling of implicit mod ```php ->withMiddleware(function (Middleware $middleware) { - $middleware->priority([ - \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, - \Illuminate\Cookie\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, - \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, - \Illuminate\Routing\Middleware\ThrottleRequests::class, - \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, - \Illuminate\Session\Middleware\AuthenticateSession::class, - \App\Http\Middleware\SetDefaultLocaleForUrls::class, // [tl! add] - \Illuminate\Routing\Middleware\SubstituteBindings::class, - \Illuminate\Auth\Middleware\Authorize::class, - ]); + $middleware->prependToPriorityList( + before: \Illuminate\Routing\Middleware\SubstituteBindings::class, + prepend: \App\Http\Middleware\SetDefaultLocaleForUrls::class, + ); }) ``` From 933bda5f2aa87567d24096f8967a4c287ee6b9f3 Mon Sep 17 00:00:00 2001 From: salah eddine bendyab <64494826+salahhusa9@users.noreply.github.com> Date: Sat, 14 Dec 2024 22:20:24 +0100 Subject: [PATCH 1949/2609] Update pennant.md (#10070) change `Feature::for($user)->loadAll();` to `Feature::for($users)->loadAll();` --- pennant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennant.md b/pennant.md index fd9c757fc49..c6ab436696f 100644 --- a/pennant.md +++ b/pennant.md @@ -815,7 +815,7 @@ Feature::for($users)->loadMissing([ You may load all defined features using the `loadAll` method: ```php -Feature::for($user)->loadAll(); +Feature::for($users)->loadAll(); ``` From e438d061210631c1a8f8a637d9c80b3d5c3a6e87 Mon Sep 17 00:00:00 2001 From: MOHAMED CHARRAFI <130717329+CharrafiMed@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:32:11 +0100 Subject: [PATCH 1950/2609] fix a wrong props cashier-paddle.md (#10078) The documentation incorrectly suggests using ``:url="$payLink"`` with ````. However, this should be ``:checkout="$checkout"`` as per ```` component code source --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 0894f29b7b6..23126a4e7e1 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -444,7 +444,7 @@ Cashier includes a `paddle-button` [Blade component](/docs/{{version}}/blade#com By default, this will display the widget using Paddle's default styling. You can customize the widget by adding [Paddle supported attributes](https://developer.paddle.com/paddlejs/html-data-attributes) like the `data-theme='light'` attribute to the component: ```html - + Subscribe ``` From a40f7f46c186fa4ce16c40097e5773aacc9b6949 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Dec 2024 12:20:47 -0600 Subject: [PATCH 1951/2609] wip --- sanctum.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index c1fd78ab487..ee09eaba6d2 100644 --- a/sanctum.md +++ b/sanctum.md @@ -133,12 +133,16 @@ Sanctum allows you to assign "abilities" to tokens. Abilities serve a similar pu return $user->createToken('token-name', ['server:update'])->plainTextToken; -When handling an incoming request authenticated by Sanctum, you may determine if the token has a given ability using the `tokenCan` method: +When handling an incoming request authenticated by Sanctum, you may determine if the token has a given ability using the `tokenCan` or `tokenCant` methods: if ($user->tokenCan('server:update')) { // ... } + if ($user->tokenCant('server:update')) { + // ... + } + #### Token Ability Middleware From 514b4f4783d8b519f51d5ee8083183fc4e1dde21 Mon Sep 17 00:00:00 2001 From: tatata-keshi <73019225+tatata-keshi@users.noreply.github.com> Date: Wed, 18 Dec 2024 06:26:24 +0900 Subject: [PATCH 1952/2609] Update queries.md (#10079) --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index c5f93a92afa..4704519e22a 100644 --- a/queries.md +++ b/queries.md @@ -820,7 +820,7 @@ select * from users where name = 'John' and (votes > 100 or title = 'Admin') > You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. -### Advanced Where Clauses +## Advanced Where Clauses ### Where Exists Clauses From 8c64a3b84e67f55673ce84024cd87e20335d7c76 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Thu, 19 Dec 2024 18:05:10 +0000 Subject: [PATCH 1953/2609] update monitoring docs (#10082) --- reverb.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reverb.md b/reverb.md index ab054f31696..e58460859b3 100644 --- a/reverb.md +++ b/reverb.md @@ -190,6 +190,8 @@ Next, add the Pulse cards for each recorder to your [Pulse dashboard](/docs/{{ve ``` +Connection activity is recorded by polling for new updates on a periodic basis. To ensure this information is rendered correctly on the Pulse dashboard, you must run the `pulse:check` daemon on your Reverb server. If you are running Reverb in a [horizontally scaled](#scaling) configuration, you should only run this daemon on one of your servers. + ## Running Reverb in Production From 09492ea7b419970a560f6b43abbc969b4a07aaa0 Mon Sep 17 00:00:00 2001 From: Cas Ebbers <617080+CasEbb@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:45:29 +0100 Subject: [PATCH 1954/2609] Move `toHtmlString()` to fluent strings (#10083) * Move `toHtmlString()` to fluent strings * Update strings.md --------- Co-authored-by: Taylor Otwell --- strings.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/strings.md b/strings.md index 7a75023e369..1bac19163a8 100644 --- a/strings.md +++ b/strings.md @@ -100,7 +100,6 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::take](#method-take) [Str::title](#method-title-case) [Str::toBase64](#method-str-to-base64) -[Str::toHtmlString](#method-str-to-html-string) [Str::transliterate](#method-str-transliterate) [Str::trim](#method-str-trim) [Str::ltrim](#method-str-ltrim) @@ -205,6 +204,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [test](#method-fluent-str-test) [title](#method-fluent-str-title) [toBase64](#method-fluent-str-to-base64) +[toHtmlString](#method-fluent-str-to-html-string) [transliterate](#method-fluent-str-transliterate) [trim](#method-fluent-str-trim) [ltrim](#method-fluent-str-ltrim) @@ -496,7 +496,7 @@ You may disable case sensitivity by setting the `ignoreCase` argument to `true`: $doesntContain = Str::doesntContain('This is name', 'MY', ignoreCase: true); // true - + #### `Str::deduplicate()` {.collection-method} @@ -1313,15 +1313,6 @@ The `Str::toBase64` method converts the given string to Base64: // TGFyYXZlbA== - -#### `Str::toHtmlString()` {.collection-method} - -The `Str::toHtmlString` method converts the string instance to an instance of `Illuminate\Support\HtmlString`, which may be displayed in Blade templates: - - use Illuminate\Support\Str; - - $htmlString = Str::of('Nuno Maduro')->toHtmlString(); - #### `Str::transliterate()` {.collection-method} @@ -1793,7 +1784,7 @@ You can disable case sensitivity by setting the `ignoreCase` argument to `true`: $containsAll = Str::of('This is my name')->containsAll(['MY', 'NAME'], ignoreCase: true); // true - + #### `deduplicate` {.collection-method} @@ -2729,7 +2720,7 @@ The `title` method converts the given string to `Title Case`: // A Nice Title Uses The Correct Case -#### `toBase64()` {.collection-method} +#### `toBase64` {.collection-method} The `toBase64` method converts the given string to Base64: @@ -2739,6 +2730,15 @@ The `toBase64` method converts the given string to Base64: // TGFyYXZlbA== + +#### `toHtmlString` {.collection-method} + +The `toHtmlString` method converts the given string to an instance of `Illuminate\Support\HtmlString`, which will not be escaped when rendered in Blade templates: + + use Illuminate\Support\Str; + + $htmlString = Str::of('Nuno Maduro')->toHtmlString(); + #### `transliterate` {.collection-method} From 06bc7bd2acc706e564254994b1d3ed7baad8f5f2 Mon Sep 17 00:00:00 2001 From: Zakaria Arrid <35905260+zakariaarrid@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:43:03 +0100 Subject: [PATCH 1955/2609] [11.x] update Str::is() strings.md (#10084) --- strings.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/strings.md b/strings.md index 1bac19163a8..f5b1b957326 100644 --- a/strings.md +++ b/strings.md @@ -634,6 +634,14 @@ The `Str::is` method determines if a given string matches a given pattern. Aster // false +You may disable case sensitivity by setting the `ignoreCase` argument to `true`: + + use Illuminate\Support\Str; + + $matches = Str::is('*.jpg', 'photo.JPG', ignoreCase: true); + + // true + #### `Str::isAscii()` {.collection-method} From ecae9ec0d8b7dd60bae30101d80cb26e21ee94bb Mon Sep 17 00:00:00 2001 From: Andy Hinkle Date: Thu, 26 Dec 2024 15:35:29 -0600 Subject: [PATCH 1956/2609] bump github actions php to 8.4 (#10087) --- pint.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pint.md b/pint.md index d4741dc41d3..ea7f8132122 100644 --- a/pint.md +++ b/pint.md @@ -178,7 +178,7 @@ jobs: strategy: fail-fast: true matrix: - php: [8.3] + php: [8.4] steps: - name: Checkout code @@ -199,6 +199,4 @@ jobs: - name: Commit linted files uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: "Fixes coding style" ``` From cd9999d15d4f6fcb2e4686bd508a065933955616 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Dec 2024 16:00:49 -0600 Subject: [PATCH 1957/2609] wip --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index e3841ffc850..e89a9c934dc 100644 --- a/validation.md +++ b/validation.md @@ -415,7 +415,7 @@ By adding a `stopOnFirstFailure` property to your request class, you may inform #### Customizing the Redirect Location -As previously discussed, a redirect response will be generated to send the user back to their previous location when form request validation fails. However, you are free to customize this behavior. To do so, define a `$redirect` property on your form request: +When form request validation fails, a redirect response will be generated to send the user back to their previous location. However, you are free to customize this behavior. To do so, define a `$redirect` property on your form request: /** * The URI that users should be redirected to if validation fails. From 981c6152c4a49a0910d72577afc43025ca43602b Mon Sep 17 00:00:00 2001 From: AriaieBOY Date: Mon, 30 Dec 2024 19:30:15 +0330 Subject: [PATCH 1958/2609] Sail valkey (#10090) * update sail to add valkey * add valkey to supported services in installation doc * Update sail.md --------- Co-authored-by: Taylor Otwell --- installation.md | 2 +- sail.md | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/installation.md b/installation.md index 548503f3cf9..ad451b0d5ad 100644 --- a/installation.md +++ b/installation.md @@ -340,7 +340,7 @@ Finally, you can access the application in your web browser at: http://localhost ### Choosing Your Sail Services -When creating a new Laravel application via Sail, you may use the `with` query string variable to choose which services should be configured in your new application's `docker-compose.yml` file. Available services include `mysql`, `pgsql`, `mariadb`, `redis`, `memcached`, `meilisearch`, `typesense`, `minio`, `selenium`, and `mailpit`: +When creating a new Laravel application via Sail, you may use the `with` query string variable to choose which services should be configured in your new application's `docker-compose.yml` file. Available services include `mysql`, `pgsql`, `mariadb`, `redis`, `valkey`, `memcached`, `meilisearch`, `typesense`, `minio`, `selenium`, and `mailpit`: ```shell curl -s "https://laravel.build/example-app?with=mysql,redis" | bash diff --git a/sail.md b/sail.md index 2c0aed7741f..a9932622b25 100644 --- a/sail.md +++ b/sail.md @@ -15,6 +15,7 @@ - [MySQL](#mysql) - [MongoDB](#mongodb) - [Redis](#redis) + - [Valkey](#valkey) - [Meilisearch](#meilisearch) - [Typesense](#typesense) - [File Storage](#file-storage) @@ -260,10 +261,17 @@ To connect to your application's MongoDB database from your local machine, you m ### 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`. +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 instance 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. + +### Valkey + +If you choose to install Valkey service when installing Sail, your application's `docker-compose.yml` file will contain an entry for [Valkey](https://valkey.io/). This container uses a [Docker volume](https://docs.docker.com/storage/volumes/) so that the data stored in your Valkey instance is persisted even when stopping and restarting your containers. You can connect to this container in you application by setting your `REDIS_HOST` environment variable within your application's `.env` file to `valkey`. + +To connect to your application's Valkey database from your local machine, you may use a graphical database management application such as [TablePlus](https://tableplus.com). By default, the Valkey database is accessible at `localhost` port 6379. + ### Meilisearch From c1f0f269efd002ada4f699e8ee19378088758f76 Mon Sep 17 00:00:00 2001 From: Jonathan Shull <58316242+aMytho@users.noreply.github.com> Date: Mon, 30 Dec 2024 10:07:28 -0600 Subject: [PATCH 1959/2609] Add Sanctum CSRF Token Encoding Info (#10089) * Add note about CSRF token encoding * Update sanctum.md * Update sanctum.md --------- Co-authored-by: Taylor Otwell --- sanctum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index ee09eaba6d2..06e1f17f5f2 100644 --- a/sanctum.md +++ b/sanctum.md @@ -306,7 +306,7 @@ axios.get('/sanctum/csrf-cookie').then(response => { }); ``` -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. +During this request, Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be URL decoded and 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 URL decoded value of the `XSRF-TOKEN` cookie that is set by this route. #### Logging In From 8fb4d801525a6cafcfd40b71b1f9c22bf6bb90d3 Mon Sep 17 00:00:00 2001 From: Emily <98989685+EmilyTheFox@users.noreply.github.com> Date: Tue, 31 Dec 2024 18:48:32 +0100 Subject: [PATCH 1960/2609] [11.x] Update validation.md documentation for uuid to mention RFC 9562 (#10094) * Update validation.md RFC 4122 has been made obsolete by RFC 9562 which the uuid rule does actually implement. So uuid validation rule docs should be updated to reflect that change * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index e89a9c934dc..5d101fedb62 100644 --- a/validation.md +++ b/validation.md @@ -1907,7 +1907,7 @@ The field under validation must be a valid [Universally Unique Lexicographically #### uuid -The field under validation must be a valid RFC 4122 (version 1, 3, 4, or 5) universally unique identifier (UUID). +The field under validation must be a valid RFC 9562 (version 1, 3, 4, 5, 6, 7, or 8) universally unique identifier (UUID). ## Conditionally Adding Rules From c4b06525d9893aaa7d45c71e162945fde065238e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 2 Jan 2025 14:13:41 -0600 Subject: [PATCH 1961/2609] wip --- passwords.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passwords.md b/passwords.md index 1f2ae5c7489..658c6ef5e46 100644 --- a/passwords.md +++ b/passwords.md @@ -73,7 +73,7 @@ Next, we will define a route that handles the form submission request from the " $request->only('email') ); - return $status === Password::RESET_LINK_SENT + return $status === Password::ResetLinkSent ? back()->with(['status' => __($status)]) : back()->withErrors(['email' => __($status)]); })->middleware('guest')->name('password.email'); @@ -136,7 +136,7 @@ Of course, we need to define a route to actually handle the password reset form } ); - return $status === Password::PASSWORD_RESET + return $status === Password::PasswordReset ? redirect()->route('login')->with('status', __($status)) : back()->withErrors(['email' => [__($status)]]); })->middleware('guest')->name('password.update'); From 1ab0c5a7e803612ae8ea6dbc7d4aeb7a9035dbe5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 2 Jan 2025 14:35:35 -0600 Subject: [PATCH 1962/2609] wip --- queues.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/queues.md b/queues.md index f90404984b2..6bb53fe10a5 100644 --- a/queues.md +++ b/queues.md @@ -2434,7 +2434,7 @@ In addition, you may occasionally need to test an individual job's interaction w Sometimes, you may need to test that a queued job [releases itself back onto the queue](#manually-releasing-a-job). Or, you may need to test that the job deleted itself. You may test these queue interactions by instantiating the job and invoking the `withFakeQueueInteractions` method. -Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assertReleased`, `assertDeleted`, `assertNotDeleted`, `assertFailed`, and `assertNotFailed` methods may be used to make assertions against the job's queue interactions: +Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assertReleased`, `assertDeleted`, `assertNotDeleted`, `assertFailed`, `assertFailedWith`, and `assertNotFailed` methods may be used to make assertions against the job's queue interactions: ```php use App\Jobs\ProcessPodcast; @@ -2447,6 +2447,7 @@ $job->assertReleased(delay: 30); $job->assertDeleted(); $job->assertNotDeleted(); $job->assertFailed(); +$job->assertFailedWith(Exception::class); $job->assertNotFailed(); ``` From 390399dd7e7101a8cecc9b11d3bf1f5e1e6b6760 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 2 Jan 2025 14:36:25 -0600 Subject: [PATCH 1963/2609] wip --- queues.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 6bb53fe10a5..96b52b43b33 100644 --- a/queues.md +++ b/queues.md @@ -2437,6 +2437,7 @@ Sometimes, you may need to test that a queued job [releases itself back onto the Once the job's queue interactions have been faked, you may invoke the `handle` method on the job. After invoking the job, the `assertReleased`, `assertDeleted`, `assertNotDeleted`, `assertFailed`, `assertFailedWith`, and `assertNotFailed` methods may be used to make assertions against the job's queue interactions: ```php +use App\Exceptions\CorruptedAudioException; use App\Jobs\ProcessPodcast; $job = (new ProcessPodcast)->withFakeQueueInteractions(); @@ -2447,7 +2448,7 @@ $job->assertReleased(delay: 30); $job->assertDeleted(); $job->assertNotDeleted(); $job->assertFailed(); -$job->assertFailedWith(Exception::class); +$job->assertFailedWith(CorruptedAudioException::class); $job->assertNotFailed(); ``` From 81ea18410c5aa2a7c276c3c44e35306c1248b244 Mon Sep 17 00:00:00 2001 From: Milos Dakic Date: Wed, 8 Jan 2025 10:22:22 +1100 Subject: [PATCH 1964/2609] docs(11.X): Incorrect property name (#10103) --- reverb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverb.md b/reverb.md index e58460859b3..de2ae7a8a46 100644 --- a/reverb.md +++ b/reverb.md @@ -57,7 +57,7 @@ You may also define the origins from which client requests may originate by upda ```php 'apps' => [ [ - 'id' => 'my-app-id', + 'app_id' => 'my-app-id', 'allowed_origins' => ['laravel.com'], // ... ] From 131983e0a57076fc769cc402876338704aa5810a Mon Sep 17 00:00:00 2001 From: Jacco Broeren Date: Wed, 8 Jan 2025 00:23:25 +0100 Subject: [PATCH 1965/2609] [11.x] Add documentation for dusk.md/assertAttributeMissing (#10102) * Update dusk.md Update documentation of assertAttributeMissing and assertAttributeDoesntContain * Update dusk.md --- dusk.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dusk.md b/dusk.md index 0f49217b95a..fb8afd15278 100644 --- a/dusk.md +++ b/dusk.md @@ -1227,6 +1227,7 @@ Dusk provides a variety of assertions that you may make against your application [assertValue](#assert-value) [assertValueIsNot](#assert-value-is-not) [assertAttribute](#assert-attribute) +[assertAttributeMissing](#assert-attribute-missing) [assertAttributeContains](#assert-attribute-contains) [assertAttributeDoesntContain](#assert-attribute-doesnt-contain) [assertAriaAttribute](#assert-aria-attribute) @@ -1630,6 +1631,14 @@ Assert that the element matching the given selector has the given value in the p $browser->assertAttribute($selector, $attribute, $value); + +#### assertAttributeMissing + +Assert that the element matching the given selector is missing the provided attribute: + + $browser->assertAttributeMissing($selector, $attribute); + + #### assertAttributeContains From 74c3d3ac1f48a8ff8ac39bcbfe5fe8506c60c877 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 9 Jan 2025 22:16:53 +0200 Subject: [PATCH 1966/2609] Missing semi-colon in session.md (#10105) --- session.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/session.md b/session.md index 7daa7d74481..55bb3797557 100644 --- a/session.md +++ b/session.md @@ -253,11 +253,11 @@ To mitigate this, Laravel provides functionality that allows you to limit concur Route::post('/profile', function () { // ... - })->block($lockSeconds = 10, $waitSeconds = 10) + })->block($lockSeconds = 10, $waitSeconds = 10); Route::post('/order', function () { // ... - })->block($lockSeconds = 10, $waitSeconds = 10) + })->block($lockSeconds = 10, $waitSeconds = 10); The `block` method accepts two optional arguments. The first argument accepted by the `block` method is the maximum number of seconds the session lock should be held for before it is released. Of course, if the request finishes executing before this time the lock will be released earlier. @@ -267,7 +267,7 @@ If neither of these arguments is passed, the lock will be obtained for a maximum Route::post('/profile', function () { // ... - })->block() + })->block(); ## Adding Custom Session Drivers From 6b78d379a055aefcd8e8ae8eed0bf7efac6f79f9 Mon Sep 17 00:00:00 2001 From: carlosmintfan <118076740+carlosmintfan@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:41:56 -0300 Subject: [PATCH 1967/2609] Make argument to Checkout::guest() an array (#10111) Checkout::guest() expects an array, not a string: https://github.com/laravel/cashier-paddle/blob/5019728a54604c9fede22c7162d33f8cf7a820d9/src/Checkout.php#L33 --- cashier-paddle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 23126a4e7e1..9b6c2e0f47c 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -558,7 +558,7 @@ Sometimes, you may need to create a checkout session for users that do not need use Laravel\Paddle\Checkout; Route::get('/buy', function (Request $request) { - $checkout = Checkout::guest('pri_34567') + $checkout = Checkout::guest(['pri_34567']) ->returnTo(route('home')); return view('billing', ['checkout' => $checkout]); From 5876c879aca23901bec0d19737d8a09ea08db159 Mon Sep 17 00:00:00 2001 From: Len Woodward Date: Tue, 14 Jan 2025 08:13:51 -0800 Subject: [PATCH 1968/2609] [11.x] Adds documentation for `pint --diff=[DIFF]` (#10100) * adds documentation for pint --diff * Update pint.md --------- Co-authored-by: Taylor Otwell --- pint.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pint.md b/pint.md index ea7f8132122..a485fa4414a 100644 --- a/pint.md +++ b/pint.md @@ -55,6 +55,12 @@ If you would like Pint to simply inspect your code for style errors without actu ./vendor/bin/pint --test ``` +If you would like Pint to only modify the files that differ from the provided branch according to Git, you may use the `--diff=[branch]` option. This can be effectively used in your CI environment (like GitHub actions) to save time by only inspecting new or modified files: + +```shell +./vendor/bin/pint --diff=main +``` + If you would like Pint to only modify the files that have uncommitted changes according to Git, you may use the `--dirty` option: ```shell From 29a04f0fe33e301efb052a3360f1df7897c2b706 Mon Sep 17 00:00:00 2001 From: Josh Salway Date: Thu, 16 Jan 2025 05:35:26 +1000 Subject: [PATCH 1969/2609] Update filesystem.md (#10114) feat(docs): Expand S3-compatible storage options in file storage documentation - Updated the file storage documentation to include additional S3-compatible storage services. - Added references to Amazon S3, MinIO, DigitalOcean Spaces, Akamai / Linode Object Storage, Vultr Object Storage, and Hetzner Cloud Storage. - Ensured all links are accurate and point to the respective service documentation. --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index 79cfa25fb16..038013c7618 100644 --- a/filesystem.md +++ b/filesystem.md @@ -203,7 +203,7 @@ Next, you may include the `read-only` configuration option in one or more of you ### Amazon S3 Compatible Filesystems -By default, your application's `filesystems` configuration file contains a disk configuration for the `s3` disk. In addition to using this disk to interact with Amazon S3, you may use it to interact with any S3 compatible file storage service such as [MinIO](https://github.com/minio/minio) or [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/). +By default, your application's `filesystems` configuration file contains a disk configuration for the `s3` disk. In addition to using this disk to interact with [Amazon S3](https://aws.amazon.com/s3/), you may use it to interact with any S3-compatible file storage service such as [MinIO](https://github.com/minio/minio), [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/), [Akamai / Linode Object Storage](https://www.linode.com/products/object-storage/), [Vultr Object Storage](https://www.vultr.com/products/object-storage/), or [Hetzner Cloud Storage](https://www.hetzner.com/storage/object-storage/). Typically, after updating the disk's credentials to match the credentials of the service you are planning to use, you only need to update the value of the `endpoint` configuration option. This option's value is typically defined via the `AWS_ENDPOINT` environment variable: From 33fee970ce91416f93f3438500dbd3b370cbb579 Mon Sep 17 00:00:00 2001 From: Italo Date: Wed, 15 Jan 2025 16:54:00 -0300 Subject: [PATCH 1970/2609] Add pessimistic locking extended example (#10113) * Add pessimistic locking extended example * Update queries.md --------- Co-authored-by: Taylor Otwell --- queries.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/queries.md b/queries.md index 4704519e22a..4114fff698b 100644 --- a/queries.md +++ b/queries.md @@ -1191,6 +1191,34 @@ Alternatively, you may use the `lockForUpdate` method. A "for update" lock preve ->lockForUpdate() ->get(); +It is recommended, but not obligatory, to wrap pessimistic locks inside a [transaction](/docs/{{version}}/database#database-transactions), especially when you require the data retrieved to not be altered on the database until the entire transaction is finished. If the transaction fails, the locks will be freed and any changes will be rolled back: + + DB::transaction(function () { + $sender = DB::table('users') + ->lockForUpdate() + ->find(1); + + $receiver = DB::table('users') + ->lockForUpdate(); + ->find(2); + + if ($user->balance < 100) { + throw new RuntimeException('Balance too low.'); + } + + DB::table('users') + ->where('id', $sender->id) + ->update([ + 'balance' => $sender->balance - 100 + ]); + + DB::table('users') + ->where('id', $receiver->id) + ->update([ + 'balance' => $receiver->balance + 100 + ]); + }); + ## Debugging From 91cfc0b594b21670aa869ab315d85c15606e9678 Mon Sep 17 00:00:00 2001 From: Vadim Dvorovenko Date: Thu, 16 Jan 2025 02:59:41 +0700 Subject: [PATCH 1971/2609] [11.x] Delete all models example (#10119) * Example of deleting all rows in table * Update eloquent.md --------- Co-authored-by: Taylor Otwell --- eloquent.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eloquent.md b/eloquent.md index 3cdc7f0b0cd..ddaeed0c187 100644 --- a/eloquent.md +++ b/eloquent.md @@ -954,6 +954,10 @@ Of course, you may build an Eloquent query to delete all models matching your qu $deleted = Flight::where('active', 0)->delete(); +To delete all models in a table, you should execute a query without adding any conditions: + + $deleted = Flight::query()->delete(); + > [!WARNING] > When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement. From 668ffdebfbbeaad1960364ccbc77f05080d0f220 Mon Sep 17 00:00:00 2001 From: Vadim Dvorovenko Date: Thu, 16 Jan 2025 03:40:10 +0700 Subject: [PATCH 1972/2609] [11.x] Note about client sharding only with predis (#10121) * Note about client sharding only with predis * Update redis.md --------- Co-authored-by: Taylor Otwell --- redis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.md b/redis.md index 0cb2a26deac..681b0e223d3 100644 --- a/redis.md +++ b/redis.md @@ -125,7 +125,7 @@ If your application is utilizing a cluster of Redis servers, you should define t By default, Laravel will use native Redis clustering since the `options.cluster` configuration value is set to `redis`. Redis clustering is a great default option, as it gracefully handles failover. -Laravel also supports client-side sharding. 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. +Laravel also supports client-side sharding when using Predis. 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 client-side sharding instead of native Redis clustering, you may remove the `options.cluster` configuration value within your application's `config/database.php` configuration file: From 326322f08e79453caca7e256638731b110c4676f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 15 Jan 2025 15:44:16 -0600 Subject: [PATCH 1973/2609] wip --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 4114fff698b..5293537405b 100644 --- a/queries.md +++ b/queries.md @@ -1191,7 +1191,7 @@ Alternatively, you may use the `lockForUpdate` method. A "for update" lock preve ->lockForUpdate() ->get(); -It is recommended, but not obligatory, to wrap pessimistic locks inside a [transaction](/docs/{{version}}/database#database-transactions), especially when you require the data retrieved to not be altered on the database until the entire transaction is finished. If the transaction fails, the locks will be freed and any changes will be rolled back: +While not obligatory, it is recommended to wrap pessimistic locks within a [transaction](/docs/{{version}}/database#database-transactions). This ensures that the data retrieved remains unaltered in the database until the entire operation completes. In case of a failure, the transaction will roll back any changes and release the locks automatically: DB::transaction(function () { $sender = DB::table('users') From 57bd7449518537e6d40af0cfc80aa9503cd64a12 Mon Sep 17 00:00:00 2001 From: Petr Levtonov Date: Wed, 15 Jan 2025 22:47:00 +0100 Subject: [PATCH 1974/2609] Add additional phpredis configs (#10122) Mention the newly added phpredis config options that users are able to specify (see: https://github.com/laravel/framework/pull/54191) --- redis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.md b/redis.md index 681b0e223d3..7615f351090 100644 --- a/redis.md +++ b/redis.md @@ -176,7 +176,7 @@ By default, Laravel will use the PhpRedis extension to communicate with Redis. T // ... ], -In addition to the default configuration options, PhpRedis supports the following additional connection parameters: `name`, `persistent`, `persistent_id`, `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 configuration options, PhpRedis supports the following additional connection parameters: `name`, `persistent`, `persistent_id`, `prefix`, `read_timeout`, `retry_interval`, `max_retries`, `backoff_algorithm`, `backoff_base`, `backoff_cap`, `timeout`, and `context`. You may add any of these options to your Redis server configuration in the `config/database.php` configuration file: 'default' => [ 'url' => env('REDIS_URL'), From 1e02dbea5f7485b03215f8fe7f54ef5c8d8d3a66 Mon Sep 17 00:00:00 2001 From: Vadim Dvorovenko Date: Thu, 16 Jan 2025 04:48:06 +0700 Subject: [PATCH 1975/2609] Remove `truncate` from docs. (#10120) --- eloquent.md | 4 ---- queries.md | 9 --------- 2 files changed, 13 deletions(-) diff --git a/eloquent.md b/eloquent.md index ddaeed0c187..d8cc14aaa69 100644 --- a/eloquent.md +++ b/eloquent.md @@ -923,10 +923,6 @@ To delete a model, you may call the `delete` method on the model instance: $flight->delete(); -You may call the `truncate` method to delete all of the model's associated database records. The `truncate` operation will also reset any auto-incrementing IDs on the model's associated table: - - Flight::truncate(); - #### Deleting an Existing Model by its Primary Key diff --git a/queries.md b/queries.md index 5293537405b..0e900fa78a8 100644 --- a/queries.md +++ b/queries.md @@ -1165,15 +1165,6 @@ The query builder's `delete` method may be used to delete records from the table $deleted = DB::table('users')->where('votes', '>', 100)->delete(); -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(); - - -#### Table Truncation and PostgreSQL - -When truncating a PostgreSQL database, the `CASCADE` behavior will be applied. This means that all foreign key related records in other tables will be deleted as well. - ## Pessimistic Locking From 9cf60b00916028819dbc019d0361139e0bc1ce41 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 17 Jan 2025 03:24:26 +0800 Subject: [PATCH 1976/2609] Add warning regarding using `HasMiddleware` interface and extends `Illuminate\Routing\Controller` (#10123) * Add warning regarding using `HasMiddleware` interface and extends `Illuminate\Routing\Controller`. fixes laravel/framework#54181 Signed-off-by: Mior Muhammad Zaki * Update controllers.md --------- Signed-off-by: Mior Muhammad Zaki Co-authored-by: Taylor Otwell --- controllers.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/controllers.md b/controllers.md index c2c22c7bffa..da3a3d4b726 100644 --- a/controllers.md +++ b/controllers.md @@ -152,6 +152,9 @@ You may also define controller middleware as closures, which provides a convenie ]; } +> [!WARNING] +> Controllers implementing `Illuminate\Routing\Controllers\HasMiddleware` should not extend `Illuminate\Routing\Controller`. + ## Resource Controllers From 572395406fc6cf79d1577c335d7aa6e50a210b4e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 17 Jan 2025 11:00:30 -0600 Subject: [PATCH 1977/2609] wip --- validation.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/validation.md b/validation.md index 5d101fedb62..388126b275f 100644 --- a/validation.md +++ b/validation.md @@ -1226,16 +1226,30 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida
    -- `rfc`: `RFCValidation` -- `strict`: `NoRFCWarningsValidation` -- `dns`: `DNSCheckValidation` -- `spoof`: `SpoofCheckValidation` -- `filter`: `FilterEmailValidation` -- `filter_unicode`: `FilterEmailValidation::unicode()` +- `rfc`: `RFCValidation` - Validate the email address according to RFC 5322. +- `strict`: `NoRFCWarningsValidation` - Validate the email according to RFC 5322, rejecting trailing periods or multiple consecutive periods. +- `dns`: `DNSCheckValidation` - Ensure the email address's domain has a valid MX record. +- `spoof`: `SpoofCheckValidation` - Ensure the email address does not contain homograph or deceptive Unicode characters. +- `filter`: `FilterEmailValidation` - Ensure the email address is valid according to PHP's `filter_var` function. +- `filter_unicode`: `FilterEmailValidation::unicode()` - Ensure the email address is valid according to PHP's `filter_var` function, allowing some Unicode characters.
    -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. +For convenience, email validation rules may be built using the fluent rule builder: + +```php +use Illuminate\Validation\Rule; + +$request->validate([ + 'email' => [ + 'required', + Rule::email() + ->rfcCompliant(strict: false) + ->validateMxRecord() + ->preventSpoofing() + ], +]); +``` > [!WARNING] > The `dns` and `spoof` validators require the PHP `intl` extension. From af5efe8d70460ada4b24dfc3d50cde3eb8a3ab26 Mon Sep 17 00:00:00 2001 From: Italo Date: Sat, 18 Jan 2025 17:55:18 -0300 Subject: [PATCH 1978/2609] Minor variable fix on example (#10126) It should be `$sender` rather than `$user`. --- queries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries.md b/queries.md index 0e900fa78a8..052ca425893 100644 --- a/queries.md +++ b/queries.md @@ -1193,7 +1193,7 @@ While not obligatory, it is recommended to wrap pessimistic locks within a [tran ->lockForUpdate(); ->find(2); - if ($user->balance < 100) { + if ($sender->balance < 100) { throw new RuntimeException('Balance too low.'); } From 754bf9befc765afc30a3d37041271b35bd744b90 Mon Sep 17 00:00:00 2001 From: Zakaria Arrid <35905260+zakariaarrid@users.noreply.github.com> Date: Wed, 22 Jan 2025 17:41:25 +0100 Subject: [PATCH 1979/2609] [11.x] update collections.md : add collapseWithKeys doc (#10128) --- collections.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/collections.md b/collections.md index 836cbe4d89b..ef019168703 100644 --- a/collections.md +++ b/collections.md @@ -102,6 +102,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [chunk](#method-chunk) [chunkWhile](#method-chunkwhile) [collapse](#method-collapse) +[collapseWithKeys](#method-collapsewithkeys) [collect](#method-collect) [combine](#method-combine) [concat](#method-concat) @@ -401,6 +402,28 @@ The `collapse` method collapses a collection of arrays into a single, flat colle // [1, 2, 3, 4, 5, 6, 7, 8, 9] + +#### `collapseWithKeys()` {.collection-method} + +The `collapseWithKeys` method flattens a collection of arrays or collections into a single collection, keeping the original keys intact: + + $collection = collect([ + ['first' => collect([1, 2, 3])], + ['second' => [4, 5, 6]], + ['third' => collect([7, 8, 9])] + ]); + + + $collapsed = $collection->collapseWithKeys(); + + $collapsed->all(); + + // [ + // 'first' => [1, 2, 3], + // 'second' => [4, 5, 6], + // 'third' => [7, 8, 9], + // ] + #### `collect()` {.collection-method} From a9c55262e3a81dc7877c886a301ae132d778234b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 22 Jan 2025 15:05:06 -0600 Subject: [PATCH 1980/2609] categorize validation rules --- validation.md | 170 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 117 insertions(+), 53 deletions(-) diff --git a/validation.md b/validation.md index 388126b275f..1dfb1047ed3 100644 --- a/validation.md +++ b/validation.md @@ -863,81 +863,156 @@ Below is a list of all available validation rules and their function: } +#### Booleans +
    [Accepted](#rule-accepted) [Accepted If](#rule-accepted-if) +[Boolean](#rule-boolean) +[Declined](#rule-declined) +[Declined If](#rule-declined-if) + +
    + +#### Strings + +
    + [Active URL](#rule-active-url) -[After (Date)](#rule-after) -[After Or Equal (Date)](#rule-after-or-equal) [Alpha](#rule-alpha) [Alpha Dash](#rule-alpha-dash) [Alpha Numeric](#rule-alpha-num) -[Array](#rule-array) [Ascii](#rule-ascii) -[Bail](#rule-bail) -[Before (Date)](#rule-before) -[Before Or Equal (Date)](#rule-before-or-equal) -[Between](#rule-between) -[Boolean](#rule-boolean) [Confirmed](#rule-confirmed) -[Contains](#rule-contains) [Current Password](#rule-current-password) -[Date](#rule-date) -[Date Equals](#rule-date-equals) -[Date Format](#rule-date-format) -[Decimal](#rule-decimal) -[Declined](#rule-declined) -[Declined If](#rule-declined-if) [Different](#rule-different) -[Digits](#rule-digits) -[Digits Between](#rule-digits-between) -[Dimensions (Image Files)](#rule-dimensions) -[Distinct](#rule-distinct) [Doesnt Start With](#rule-doesnt-start-with) [Doesnt End With](#rule-doesnt-end-with) [Email](#rule-email) [Ends With](#rule-ends-with) [Enum](#rule-enum) -[Exclude](#rule-exclude) -[Exclude If](#rule-exclude-if) -[Exclude Unless](#rule-exclude-unless) -[Exclude With](#rule-exclude-with) -[Exclude Without](#rule-exclude-without) -[Exists (Database)](#rule-exists) -[Extensions](#rule-extensions) -[File](#rule-file) -[Filled](#rule-filled) -[Greater Than](#rule-gt) -[Greater Than Or Equal](#rule-gte) [Hex Color](#rule-hex-color) -[Image (File)](#rule-image) [In](#rule-in) -[In Array](#rule-in-array) -[Integer](#rule-integer) [IP Address](#rule-ip) [JSON](#rule-json) -[Less Than](#rule-lt) -[Less Than Or Equal](#rule-lte) -[List](#rule-list) [Lowercase](#rule-lowercase) [MAC Address](#rule-mac) [Max](#rule-max) +[Min](#rule-min) +[Not In](#rule-not-in) +[Regular Expression](#rule-regex) +[Not Regular Expression](#rule-not-regex) +[Same](#rule-same) +[Size](#rule-size) +[Starts With](#rule-starts-with) +[String](#rule-string) +[Uppercase](#rule-uppercase) +[URL](#rule-url) +[ULID](#rule-ulid) +[UUID](#rule-uuid) + +
    + +#### Numbers + +
    + +[Between](#rule-between) +[Decimal](#rule-decimal) +[Different](#rule-different) +[Digits](#rule-digits) +[Digits Between](#rule-digits-between) +[Greater Than](#rule-gt) +[Greater Than Or Equal](#rule-gte) +[Integer](#rule-integer) +[Less Than](#rule-lt) +[Less Than Or Equal](#rule-lte) +[Max](#rule-max) [Max Digits](#rule-max-digits) -[MIME Types](#rule-mimetypes) -[MIME Type By File Extension](#rule-mimes) [Min](#rule-min) [Min Digits](#rule-min-digits) +[Multiple Of](#rule-multiple-of) +[Numeric](#rule-numeric) +[Same](#rule-same) +[Size](#rule-size) + +
    + +#### Arrays + +
    + +[Array](#rule-array) +[Between](#rule-between) +[Contains](#rule-contains) +[Distinct](#rule-distinct) +[In Array](#rule-in-array) +[List](#rule-list) +[Max](#rule-max) +[Min](#rule-min) +[Size](#rule-size) + +
    + +#### Dates + +
    + +[After](#rule-after) +[After Or Equal](#rule-after-or-equal) +[Before](#rule-before) +[Before Or Equal](#rule-before-or-equal) +[Date](#rule-date) +[Date Equals](#rule-date-equals) +[Date Format](#rule-date-format) +[Different](#rule-different) +[Timezone](#rule-timezone) + +
    + +#### Files + +
    + +[Between](#rule-between) +[Dimensions](#rule-dimensions) +[Extensions](#rule-extensions) +[File](#rule-file) +[Image](#rule-image) +[Max](#rule-max) +[MIME Types](#rule-mimetypes) +[MIME Type By File Extension](#rule-mimes) +[Size](#rule-size) + +
    + +#### Database + +
    + +[Exists](#rule-exists) +[Unique](#rule-unique) + +
    + +#### Utilities + +
    + +[Bail](#rule-bail) +[Exclude](#rule-exclude) +[Exclude If](#rule-exclude-if) +[Exclude Unless](#rule-exclude-unless) +[Exclude With](#rule-exclude-with) +[Exclude Without](#rule-exclude-without) +[Filled](#rule-filled) [Missing](#rule-missing) [Missing If](#rule-missing-if) [Missing Unless](#rule-missing-unless) [Missing With](#rule-missing-with) [Missing With All](#rule-missing-with-all) -[Multiple Of](#rule-multiple-of) -[Not In](#rule-not-in) -[Not Regex](#rule-not-regex) [Nullable](#rule-nullable) -[Numeric](#rule-numeric) [Present](#rule-present) [Present If](#rule-present-if) [Present Unless](#rule-present-unless) @@ -947,7 +1022,6 @@ Below is a list of all available validation rules and their function: [Prohibited If](#rule-prohibited-if) [Prohibited Unless](#rule-prohibited-unless) [Prohibits](#rule-prohibits) -[Regular Expression](#rule-regex) [Required](#rule-required) [Required If](#rule-required-if) [Required If Accepted](#rule-required-if-accepted) @@ -958,17 +1032,7 @@ Below is a list of all available validation rules and their function: [Required Without](#rule-required-without) [Required Without All](#rule-required-without-all) [Required Array Keys](#rule-required-array-keys) -[Same](#rule-same) -[Size](#rule-size) [Sometimes](#validating-when-present) -[Starts With](#rule-starts-with) -[String](#rule-string) -[Timezone](#rule-timezone) -[Unique (Database)](#rule-unique) -[Uppercase](#rule-uppercase) -[URL](#rule-url) -[ULID](#rule-ulid) -[UUID](#rule-uuid)
    From d83edfc43fa745bc73531b830248326262771f07 Mon Sep 17 00:00:00 2001 From: Zakaria Arrid <35905260+zakariaarrid@users.noreply.github.com> Date: Thu, 23 Jan 2025 23:42:01 +0100 Subject: [PATCH 1981/2609] 11.x update collection md (#10130) * [11.x] update collections.md:add intersectAssocUsing,intersectUsing doc * [11.x] update collections.md:add intersectAssocUsing,intersectUsing doc done * Update collections.md --------- Co-authored-by: Taylor Otwell --- collections.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/collections.md b/collections.md index ef019168703..6ad182c8d98 100644 --- a/collections.md +++ b/collections.md @@ -142,7 +142,9 @@ For the majority of the remaining collection documentation, we'll discuss each m [hasAny](#method-hasany) [implode](#method-implode) [intersect](#method-intersect) +[intersectUsing](#method-intersectusing) [intersectAssoc](#method-intersectAssoc) +[intersectAssocUsing](#method-intersectassocusing) [intersectByKeys](#method-intersectbykeys) [isEmpty](#method-isempty) [isNotEmpty](#method-isnotempty) @@ -1311,6 +1313,21 @@ The `intersect` method removes any values from the original collection that are > [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). + +#### `intersectUsing()` {.collection-method} + +The `intersectUsing` method removes any values from the original collection that are not present in the given `array` or collection, using a custom callback to compare the values. The resulting collection will preserve the original collection's keys: + + $collection = collect(['Desk', 'Sofa', 'Chair']); + + $intersect = $collection->intersectUsing(['desk', 'chair', 'bookcase'], function ($a, $b) { + return strcasecmp($a, $b); + }); + + $intersect->all(); + + // [0 => 'Desk', 2 => 'Chair'] + #### `intersectAssoc()` {.collection-method} @@ -1332,6 +1349,29 @@ The `intersectAssoc` method compares the original collection against another col // ['size' => 'M'] + +#### `intersectAssocUsing()` {.collection-method} + +The `intersectAssocUsing` method compares the original collection against another collection or `array`, returning the key / value pairs that are present in both, using a custom comparison callback to determine equality for both keys and values: + + $collection = collect([ + 'color' => 'red', + 'Size' => 'M', + 'material' => 'cotton', + ]); + + $intersect = $collection->intersectAssocUsing([ + 'color' => 'blue', + 'size' => 'M', + 'material' => 'polyester', + ], function ($a, $b) { + return strcasecmp($a, $b); + }); + + $intersect->all(); + + // ['Size' => 'M'] + #### `intersectByKeys()` {.collection-method} From a61f12b92f0aadef75dac1458fa131719c801a06 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Sat, 25 Jan 2025 04:07:54 +1100 Subject: [PATCH 1982/2609] [11.x] Add CORS configuration to Vite plugin docs (#10131) * Add CORS configuration to Vite plugin docs * wording --------- Co-authored-by: Taylor Otwell --- vite.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/vite.md b/vite.md index 69bf5b6f5c4..f002e40426f 100644 --- a/vite.md +++ b/vite.md @@ -28,6 +28,7 @@ - [Subresource Integrity (SRI)](#subresource-integrity-sri) - [Arbitrary Attributes](#arbitrary-attributes) - [Advanced Customization](#advanced-customization) + - [Dev Server Cross-Origin Resource Sharing (CORS)](#cors) - [Correcting Dev Server URLs](#correcting-dev-server-urls) @@ -928,6 +929,72 @@ export default defineConfig({ }); ``` + +### Dev Server Cross-Origin Resource Sharing (CORS) + +If you are experiencing Cross-Origin Resource Sharing (CORS) issues in the browser while fetching assets from the Vite dev server, you may need to grant your custom origin access to the dev server. Vite combined with the Laravel plugin allows the following origins without any additional configuration: + +- `::1` +- `127.0.0.1` +- `localhost` +- `*.test` +- `*.localhost` +- `APP_URL` in the project's `.env` + +The easiest way to allow a custom origin for your project is to ensure that your application's `APP_URL` environment variable matches the origin you are visiting in your browser. For example, if you visiting `https://my-app.laravel`, you should update your `.env` to match: + +```env +APP_URL=https://my-app.laravel +``` + +If you need more fine-grained control over the origins, such as supporting multiple origins, you should utilize [Vite's comprehensive and flexible built-in CORS server configuration](https://vite.dev/config/server-options.html#server-cors). For example, you may specify multiple origins in the `server.cors.origin` configuration option in the project's `vite.config.js` file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: 'resources/js/app.js', + refresh: true, + }), + ], + server: { // [tl! add] + cors: { // [tl! add] + origin: [ // [tl! add] + 'https://backend.laravel/', // [tl! add] + 'http://admin.laravel:8566/', // [tl! add] + ], // [tl! add] + }, // [tl! add] + }, // [tl! add] +}); +``` + +You may also include regex patterns, which can be helpful if you would like to allow all origins for a given top-level domain, such as `*.laravel`: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: 'resources/js/app.js', + refresh: true, + }), + ], + server: { // [tl! add] + cors: { // [tl! add] + origin: [ // [tl! add] + // Supports: SCHEME://DOMAIN.laravel[:PORT] [tl! add] + /^https?:\/\/.*\.laravel(:\d+)?$/, //[tl! add] + ], // [tl! add] + }, // [tl! add] + }, // [tl! add] +}); +``` + ### Correcting Dev Server URLs From 660476cce3eafe428106ab48e263a3c053f69b94 Mon Sep 17 00:00:00 2001 From: Iosif Miclaus Date: Mon, 27 Jan 2025 01:09:25 +0100 Subject: [PATCH 1983/2609] Add previousPath usage (#10139) --- urls.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/urls.md b/urls.md index 5becbe7d49f..d3bf482ca81 100644 --- a/urls.md +++ b/urls.md @@ -68,6 +68,9 @@ If no path is provided to the `url` helper, an `Illuminate\Routing\UrlGenerator` // Get the full URL for the previous request... echo url()->previous(); + // Get the path for the previous request... + echo url()->previousPath(); + Each of these methods may also be accessed via the `URL` [facade](/docs/{{version}}/facades): use Illuminate\Support\Facades\URL; From 1ebe27624e02f01d844c4d3cf1f4b4e5b8711535 Mon Sep 17 00:00:00 2001 From: Rainer Bendig Date: Mon, 27 Jan 2025 01:57:25 +0100 Subject: [PATCH 1984/2609] =?UTF-8?q?[11.x]=20migration=20docs:=20=20addin?= =?UTF-8?q?g=20clarification=20for=20json/jsonb=20datatypes=E2=80=A6=20(#1?= =?UTF-8?q?0134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [11.x] migration docs: adding clarification for json/jsonb datatypes on sqlite Adding clarification for json/jsonb datatypes on sqlite. * Update migrations.md --------- Co-authored-by: Taylor Otwell --- migrations.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/migrations.md b/migrations.md index a0be8bed27a..e7658997709 100644 --- a/migrations.md +++ b/migrations.md @@ -638,6 +638,8 @@ The `json` method creates a `JSON` equivalent column: $table->json('options'); +When using SQLite, a `TEXT` column will be created. + #### `jsonb()` {.collection-method} @@ -645,6 +647,8 @@ The `jsonb` method creates a `JSONB` equivalent column: $table->jsonb('options'); +When using SQLite, a `TEXT` column will be created. + #### `longText()` {.collection-method} From ecc940f39ff2e052a1eafac1edb70777754f747c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 26 Jan 2025 19:03:25 -0600 Subject: [PATCH 1985/2609] formatting --- filesystem.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filesystem.md b/filesystem.md index 038013c7618..8486e0041d9 100644 --- a/filesystem.md +++ b/filesystem.md @@ -43,7 +43,7 @@ The `local` driver interacts with files stored locally on the server running the ### The Local Driver -When using the `local` driver, all file operations are relative to the `root` directory defined in your `filesystems` configuration file. By default, this value is set to the `storage/app` directory. Therefore, the following method would write to `storage/app/example.txt`: +When using the `local` driver, all file operations are relative to the `root` directory defined in your `filesystems` configuration file. By default, this value is set to the `storage/app/private` directory. Therefore, the following method would write to `storage/app/private/example.txt`: use Illuminate\Support\Facades\Storage; @@ -54,7 +54,7 @@ When using the `local` driver, all file operations are relative to the `root` di The `public` disk included in your application's `filesystems` configuration file is intended for files that are going to be publicly accessible. By default, the `public` disk uses the `local` driver and stores its files in `storage/app/public`. -To make these files accessible from the web, you should create a symbolic link from source directory `storage/app/public` to target directory `public/storage`. Utilizing this folder convention will keep your publicly accessible files in one directory that can be easily shared across deployments when using zero down-time deployment systems like [Envoyer](https://envoyer.io). +If your `public` disk uses the `local` driver and you want to make these files accessible from the web, you should create a symbolic link from source directory `storage/app/public` to target directory `public/storage`: To create the symbolic link, you may use the `storage:link` Artisan command: From dbb8b042f43d19448eadde81e3e1a821667996cb Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Mon, 27 Jan 2025 23:35:18 +0100 Subject: [PATCH 1986/2609] [11.x] Improve File validation structure and clarify `File::image()` usage (#10142) * Update validation.md * Update validation.md * Update validation.md * Update validation.md * Update validation.md * Remove extra new line * Update validation.md * Update validation.md * formatting --------- Co-authored-by: Taylor Otwell --- validation.md | 71 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/validation.md b/validation.md index 1dfb1047ed3..28081132560 100644 --- a/validation.md +++ b/validation.md @@ -1242,7 +1242,7 @@ A _ratio_ constraint should be represented as width divided by height. This can 'avatar' => 'dimensions:ratio=3/2' -Since this rule requires several arguments, you may use the `Rule::dimensions` method to fluently construct the rule: +Since this rule requires several arguments, it is often more convenient to use use the `Rule::dimensions` method to fluently construct the rule: use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; @@ -1250,7 +1250,10 @@ Since this rule requires several arguments, you may use the `Rule::dimensions` m Validator::make($data, [ 'avatar' => [ 'required', - Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2), + Rule::dimensions() + ->maxWidth(1000) + ->maxHeight(500) + ->ratio(3 / 2), ], ]); @@ -2196,42 +2199,60 @@ Laravel provides a variety of validation rules that may be used to validate uplo ], ]); -If your application accepts images uploaded by your users, you may use the `File` rule's `image` constructor method to indicate that the uploaded file should be an image. In addition, the `dimensions` rule may be used to limit the dimensions of the image: - - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; - use Illuminate\Validation\Rules\File; + +#### Validating File Types - Validator::validate($input, [ - 'photo' => [ - 'required', - File::image() - ->min(1024) - ->max(12 * 1024) - ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)), - ], - ]); +Even though you only need to specify the extensions when invoking the `types` method, this method 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: -> [!NOTE] -> More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). +[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) -#### File Sizes +#### Validating File Sizes For convenience, minimum and maximum file sizes may be specified as a string with a suffix indicating the file size units. The `kb`, `mb`, `gb`, and `tb` suffixes are supported: ```php -File::image() +File::types(['mp3', 'wav']) ->min('1kb') - ->max('10mb') + ->max('10mb'); ``` - -#### File Types + +#### Validating Image Files -Even though you only need to specify the extensions when invoking the `types` method, this method 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: +To validate that uploaded files are images, you can use the `File` rule's `image` constructor method. The `File::image()` rule ensures that the file under validation is an image (jpg, jpeg, png, bmp, gif, svg, or webp): -[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) +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; + +Validator::validate($input, [ + 'photo' => [ + 'required', + File::image(), + ], +]); +``` + + +#### Validating Image Dimensions + +You may also validate the dimensions of an image. For example, to validate that an uploaded image is at least 1000 pixels wide and 500 pixels tall, you may use the `dimensions` rule: + +```php +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; + +File::image()->dimensions( + Rule::dimensions() + ->maxWidth(1000) + ->maxHeight(500) +) +``` + +> [!NOTE] +> More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). ## Validating Passwords From 38946b43de81952f916100ee3a3a6ef5b7052749 Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Tue, 28 Jan 2025 00:00:32 +0100 Subject: [PATCH 1987/2609] [12.x] Document `image` rule SVG security changes (#10136) * Update validation.md * Update validation.md * Update validation.md * Update validation.md * Update validation.md * Remove extra new line * Revert "Remove extra new line" This reverts commit 8cadafddb0b0d780a563e7347a7b41230a6561f8. * Revert "Update validation.md" This reverts commit aa684e6e8658d044ec2727661dc738e15fdacaf0. * Revert "Update validation.md" This reverts commit 84f56ee1bf108de53dfcac14099a505d43e096ed. * Revert "Update validation.md" This reverts commit d262a82aa94a7bdfeea9cf33faee0c717b920ca7. * Update validation.md * fix commas * Revert "fix commas" This reverts commit a9749b72e7adf3656642368bbc35854f75c99c7c. * formatting * Update validation.md * formatting --------- Co-authored-by: Taylor Otwell --- validation.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 86262b306fb..287eb94170a 100644 --- a/validation.md +++ b/validation.md @@ -1479,7 +1479,10 @@ The field under validation must contain a valid color value in [hexadecimal](htt #### 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, or webp). + +> [!WARNING] +> By default, the image rule does not allow SVG files due to the possibility of XSS vulnerabilities. If you need to allow SVG files, you may provide the `allow_svg` directive to the `image` rule (`image:allow_svg`). #### in:_foo_,_bar_,... @@ -2202,7 +2205,9 @@ Laravel provides a variety of validation rules that may be used to validate uplo ], ]); -If your application accepts images uploaded by your users, you may use the `File` rule's `image` constructor method to indicate that the uploaded file should be an image. In addition, the `dimensions` rule may be used to limit the dimensions of the image: +If your application accepts images uploaded by your users, you may use the `File` rule's `image` constructor method to ensure that the file under validation is an image (jpg, jpeg, png, bmp, gif, or webp). + +In addition, the `dimensions` rule may be used to limit the dimensions of the image: use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; @@ -2221,6 +2226,9 @@ If your application accepts images uploaded by your users, you may use the `File > [!NOTE] > More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). +> [!WARNING] +> By default, the `image` rule does not allow SVG files due to the possibility of XSS vulnerabilities. If you need to allow SVG files, you may pass `allowSvg: true` to the `image` rule: `File::image(allowSvg: true)`. + #### File Sizes From c7f4f79bde551dbc8b783f84b4255dec20d69348 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 28 Jan 2025 16:38:50 -0500 Subject: [PATCH 1988/2609] fluent date rules --- validation.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/validation.md b/validation.md index 28081132560..81d35a967da 100644 --- a/validation.md +++ b/validation.md @@ -1062,11 +1062,36 @@ Instead of passing a date string to be evaluated by `strtotime`, you may specify 'finish_date' => 'required|date|after:start_date' +For convenience, date based rules may be constructed using the fluent `date` rule builder: + + use Illuminate\Validation\Rule; + + 'start_date' => [ + 'required', + Rule::date()->after(today()->addDays(7)), + ], + +The `afterToday` and `todayOrAfter` methods may be used to fluently express the date must be after today or or today or after, respectively: + + 'start_date' => [ + 'required', + Rule::date()->afterToday(), + ], + #### after\_or\_equal:_date_ The field under validation must be a value after or equal to the given date. For more information, see the [after](#rule-after) rule. +For convenience, date based rules may be constructed using the fluent `date` rule builder: + + use Illuminate\Validation\Rule; + + 'start_date' => [ + 'required', + Rule::date()->afterOrEqual(today()->addDays(7)), + ], + #### alpha @@ -1144,11 +1169,36 @@ While the `bail` rule will only stop validating a specific field when it encount 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`. +For convenience, date based rules may also be constructed using the fluent `date` rule builder: + + use Illuminate\Validation\Rule; + + 'start_date' => [ + 'required', + Rule::date()->before(today()->subDays(7)), + ], + +The `beforeToday` and `todayOrBefore` methods may be used to fluently express the date must be before today or or today or before, respectively: + + 'start_date' => [ + 'required', + Rule::date()->beforeToday(), + ], + #### 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 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`. +For convenience, date based rules may also be constructed using the fluent `date` rule builder: + + use Illuminate\Validation\Rule; + + 'start_date' => [ + 'required', + Rule::date()->beforeOrEqual(today()->subDays(7)), + ], + #### between:_min_,_max_ @@ -1193,6 +1243,15 @@ The field under validation must be equal to the given date. The dates will be pa The field under validation must match one of the given _formats_. You should use **either** `date` or `date_format` when validating a field, not both. This validation rule supports all formats supported by PHP's [DateTime](https://www.php.net/manual/en/class.datetime.php) class. +For convenience, date based rules may be constructed using the fluent `date` rule builder: + + use Illuminate\Validation\Rule; + + 'start_date' => [ + 'required', + Rule::date()->format('Y-m-d'), + ], + #### decimal:_min_,_max_ From 3ea43ef22a0ab25634b8a40add3237d8aae1e15c Mon Sep 17 00:00:00 2001 From: Alain Cis Date: Thu, 30 Jan 2025 10:22:51 +0100 Subject: [PATCH 1989/2609] Regrouped migrations avalable column types. Column types are now grouped for better organization and easier navigation. (#10144) * Regrouped migrations avalable column types. Column types are now grouped for better organization and easier navigation. * Fix: Fixing missing tags * formatting --------- Co-authored-by: Taylor Otwell --- migrations.md | 154 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 114 insertions(+), 40 deletions(-) diff --git a/migrations.md b/migrations.md index e7658997709..7d46c8aabd0 100644 --- a/migrations.md +++ b/migrations.md @@ -396,70 +396,144 @@ The schema builder blueprint offers a variety of methods that correspond to the } + +#### Boolean Types +
    -[bigIncrements](#column-method-bigIncrements) -[bigInteger](#column-method-bigInteger) -[binary](#column-method-binary) [boolean](#column-method-boolean) + +
    + + +#### String & Text Types + +
    + [char](#column-method-char) -[dateTimeTz](#column-method-dateTimeTz) -[dateTime](#column-method-dateTime) -[date](#column-method-date) +[longText](#column-method-longText) +[mediumText](#column-method-mediumText) +[string](#column-method-string) +[text](#column-method-text) +[tinyText](#column-method-tinyText) +[longText](#column-method-longText) + +
    + + +#### Numeric Types + +
    + +[bigIncrements](#column-method-bigIncrements) +[bigInteger](#column-method-bigInteger) [decimal](#column-method-decimal) [double](#column-method-double) -[enum](#column-method-enum) [float](#column-method-float) -[foreignId](#column-method-foreignId) -[foreignIdFor](#column-method-foreignIdFor) -[foreignUlid](#column-method-foreignUlid) -[foreignUuid](#column-method-foreignUuid) -[geography](#column-method-geography) -[geometry](#column-method-geometry) [id](#column-method-id) [increments](#column-method-increments) [integer](#column-method-integer) -[ipAddress](#column-method-ipAddress) -[json](#column-method-json) -[jsonb](#column-method-jsonb) -[longText](#column-method-longText) -[macAddress](#column-method-macAddress) [mediumIncrements](#column-method-mediumIncrements) [mediumInteger](#column-method-mediumInteger) -[mediumText](#column-method-mediumText) -[morphs](#column-method-morphs) -[nullableMorphs](#column-method-nullableMorphs) -[nullableTimestamps](#column-method-nullableTimestamps) -[nullableUlidMorphs](#column-method-nullableUlidMorphs) -[nullableUuidMorphs](#column-method-nullableUuidMorphs) -[rememberToken](#column-method-rememberToken) -[set](#column-method-set) [smallIncrements](#column-method-smallIncrements) [smallInteger](#column-method-smallInteger) -[softDeletesTz](#column-method-softDeletesTz) -[softDeletes](#column-method-softDeletes) -[string](#column-method-string) -[text](#column-method-text) -[timeTz](#column-method-timeTz) -[time](#column-method-time) -[timestampTz](#column-method-timestampTz) -[timestamp](#column-method-timestamp) -[timestampsTz](#column-method-timestampsTz) -[timestamps](#column-method-timestamps) [tinyIncrements](#column-method-tinyIncrements) [tinyInteger](#column-method-tinyInteger) -[tinyText](#column-method-tinyText) [unsignedBigInteger](#column-method-unsignedBigInteger) [unsignedInteger](#column-method-unsignedInteger) [unsignedMediumInteger](#column-method-unsignedMediumInteger) [unsignedSmallInteger](#column-method-unsignedSmallInteger) [unsignedTinyInteger](#column-method-unsignedTinyInteger) -[ulidMorphs](#column-method-ulidMorphs) -[uuidMorphs](#column-method-uuidMorphs) + +
    + + +#### Date & Time Types + +
    + +[dateTime](#column-method-dateTime) +[dateTimeTz](#column-method-dateTimeTz) +[date](#column-method-date) +[time](#column-method-time) +[timeTz](#column-method-timeTz) +[timestamp](#column-method-timestamp) +[timestamps](#column-method-timestamps) +[nullableTimestamps](#column-method-nullableTimestamps) +[timestampsTz](#column-method-timestampsTz) +[softDeletes](#column-method-softDeletes) +[softDeletesTz](#column-method-softDeletesTz) +[year](#column-method-year) + +
    + + +#### Binary Types + +
    + +[binary](#column-method-binary) + +
    + + +#### Object & Json Types + +
    + +[json](#column-method-json) +[jsonb](#column-method-jsonb) + +
    + + +#### UUID & ULID Types + +
    + [ulid](#column-method-ulid) +[ulidMorphs](#column-method-ulidMorphs) [uuid](#column-method-uuid) +[uuidMorphs](#column-method-uuidMorphs) +[nullableUlidMorphs](#column-method-nullableUlidMorphs) +[nullableUuidMorphs](#column-method-nullableUuidMorphs) + +
    + + +#### Spatial Types + +
    + +[geography](#column-method-geography) +[geometry](#column-method-geometry) + +
    + +#### Relationship Types + +
    + +[foreignId](#column-method-foreignId) +[foreignIdFor](#column-method-foreignIdFor) +[foreignUlid](#column-method-foreignUlid) +[foreignUuid](#column-method-foreignUuid) +[morphs](#column-method-morphs) +[nullableMorphs](#column-method-nullableMorphs) + +
    + + +#### Specialty Types + +
    + +[enum](#column-method-enum) +[set](#column-method-set) +[macAddress](#column-method-macAddress) +[ipAddress](#column-method-ipAddress) +[rememberToken](#column-method-rememberToken) [vector](#column-method-vector) -[year](#column-method-year)
    From cc3a852f9c72e25f7a954009d5399413a36d9797 Mon Sep 17 00:00:00 2001 From: Bram Nijssen <49712778+bramnijssen@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:32:47 +0100 Subject: [PATCH 1990/2609] Remove duplicate longText column type (#10150) --- migrations.md | 1 - 1 file changed, 1 deletion(-) diff --git a/migrations.md b/migrations.md index 7d46c8aabd0..af17832848f 100644 --- a/migrations.md +++ b/migrations.md @@ -416,7 +416,6 @@ The schema builder blueprint offers a variety of methods that correspond to the [string](#column-method-string) [text](#column-method-text) [tinyText](#column-method-tinyText) -[longText](#column-method-longText) From a03b68d4a9fa901420016c9ec67053ba44de47bc Mon Sep 17 00:00:00 2001 From: Joost de Bruijn Date: Wed, 5 Feb 2025 13:11:38 +0100 Subject: [PATCH 1991/2609] Add explanation for index-settings for Algolia in Scout (#10138) * Add explanation for index-settings for Algolia in Scout * docs: add focus on why you might want to deploy scout index configuration from source code * formatting --------- Co-authored-by: Taylor Otwell --- scout.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/scout.md b/scout.md index 4812de51f48..6146ea36e4c 100644 --- a/scout.md +++ b/scout.md @@ -123,7 +123,7 @@ For more information regarding Meilisearch, please consult the [Meilisearch docu In addition, you should ensure that you install a version of `meilisearch/meilisearch-php` that is compatible with your Meilisearch binary version by reviewing [Meilisearch's documentation regarding binary compatibility](https://github.com/meilisearch/meilisearch-php#-compatibility-with-meilisearch). -> [!WARNING] +> [!WARNING] > When upgrading Scout on an application that utilizes Meilisearch, you should always [review any additional breaking changes](https://github.com/meilisearch/Meilisearch/releases) to the Meilisearch service itself. @@ -281,6 +281,49 @@ Some search engines such as Meilisearch will only perform filter operations (`>` ]; } + +#### Configuring Index Settings (Algolia) + +Sometimes you may want to configure additional settings on your Algolia indexes. While you can manage these settings via the Algolia UI, it is sometimes more efficient to manage the desired state of your index configuration directly from your application's `config/scout.php` configuration file. + +This approach allows you to deploy these settings through your application's automated deployment pipeline, avoiding manual configuration and ensuring consistency across multiple environments. You may configure filterable attributes, ranking, faceting, or [any other supported settings](https://www.algolia.com/doc/rest-api/search/#tag/Indices/operation/setSettings). + +To get started, add settings for each index in your application's `config/scout.php` configuration file: + +```php +use App\Models\User; +use App\Models\Flight; + +'algolia' => [ + 'id' => env('ALGOLIA_APP_ID', ''), + 'secret' => env('ALGOLIA_SECRET', ''), + 'index-settings' => [ + User::class => [ + 'searchableAttributes' => ['id', 'name', 'email'], + 'attributesForFaceting'=> ['filterOnly(email)'], + // Other settings fields... + ], + Flight::class => [ + 'searchableAttributes'=> ['id', 'destination'], + ], + ], +], +``` + +If the model underlying a given index is soft deletable and is included in the `index-settings` array, Scout will automatically include support for faceting on soft deleted models on that index. If you have no other faceting attributes to define for a soft deletable model index, you may simply add an empty entry to the `index-settings` array for that model: + +```php +'index-settings' => [ + Flight::class => [] +], +``` + +After configuring your application's index settings, you must invoke the `scout:sync-index-settings` Artisan command. This command will inform Algolia of your currently configured index settings. For convenience, you may wish to make this command part of your deployment process: + +```shell +php artisan scout:sync-index-settings +``` + #### Configuring Filterable Data and Index Settings (Meilisearch) @@ -400,7 +443,7 @@ Enabling this feature will also pass the request's IP address and your authentic ### Database Engine -> [!WARNING] +> [!WARNING] > The database engine currently supports MySQL and PostgreSQL. If your application interacts with small to medium sized databases or has a light workload, you may find it more convenient to get started with Scout's "database" engine. The database engine will use "where like" clauses and full text indexes when filtering results from your existing database to determine the applicable search results for your query. @@ -441,7 +484,7 @@ public function toSearchableArray(): array } ``` -> [!WARNING] +> [!WARNING] > Before specifying that a column should use full text query constraints, ensure that the column has been assigned a [full text index](/docs/{{version}}/migrations#available-index-types). @@ -496,7 +539,7 @@ If you would like to modify the query that is used to retrieve all of your model return $query->with('author'); } -> [!WARNING] +> [!WARNING] > The `makeAllSearchableUsing` method may not be applicable when using a queue to batch import models. Relationships are [not restored](/docs/{{version}}/queues#handling-relationships) when model collections are processed by jobs. @@ -529,7 +572,7 @@ Or, if you already have a collection of Eloquent models in memory, you may call $orders->searchable(); -> [!NOTE] +> [!NOTE] > 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. @@ -625,7 +668,7 @@ Sometimes you may need to only make a model searchable under certain conditions. 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. -> [!WARNING] +> [!WARNING] > The `shouldBeSearchable` method is not applicable when using Scout's "database" engine, as all searchable data is always stored in the database. To achieve similar behavior when using the database engine, you should use [where clauses](#where-clauses) instead. @@ -682,7 +725,7 @@ The `whereNotIn` method verifies that the given column's value is not contained Since a search index is not a relational database, more advanced "where" clauses are not currently supported. -> [!WARNING] +> [!WARNING] > If your application is using Meilisearch, you must configure your application's [filterable attributes](#configuring-filterable-data-for-meilisearch) before utilizing Scout's "where" clauses. @@ -719,7 +762,7 @@ Of course, if you would like to retrieve the pagination results as JSON, you may return Order::search($request->input('query'))->paginate(15); }); -> [!WARNING] +> [!WARNING] > Since search engines are not aware of your Eloquent model's global scope definitions, you should not utilize global scopes in applications that utilize Scout pagination. Or, you should recreate the global scope's constraints when searching via Scout. @@ -739,7 +782,7 @@ When this configuration option is `true`, Scout will not remove soft deleted mod // Only include trashed records when retrieving results... $orders = Order::search('Star Trek')->onlyTrashed()->get(); -> [!NOTE] +> [!NOTE] > When a soft deleted model is permanently deleted using `forceDelete`, Scout will remove it from the search index automatically. From 4892dd2a25cac549a0af4ee36efe2df309fd1a7b Mon Sep 17 00:00:00 2001 From: Andrew Koper Date: Thu, 6 Feb 2025 16:43:38 -0500 Subject: [PATCH 1992/2609] Update mail.md (#10151) * Update mail.md To send email with Mailgun (and any external mail service, probably) and Laravel, there are three changes to config files that have to be made. Two of them had code examples in the documentation. The third one did not. Currently, it is just written with words as "change the default value..." but the fact that the item in the array is named "default" and value in the key/value pair is "default" leads to ambiguity. After reading Laraval mail's documentation, doing a lot of set up and configuring, and programming a controller and mailable, I experienced hours of stress and frustration because the emails weren't being sent. All of the Mailgun env and config vars seemed to be good. This was the problem. Please accept this mail documentation change. The one-line code example is important in getting mail to work * Update mail.md --------- Co-authored-by: Taylor Otwell --- mail.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mail.md b/mail.md index af1fe496b8e..fe4acb5eb82 100644 --- a/mail.md +++ b/mail.md @@ -59,7 +59,11 @@ To use the Mailgun driver, install Symfony's Mailgun Mailer transport via Compos 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`: +Next, you will need to make two changes in your application's `config/mail.php` configuration file. First, set your default mailer to `mailgun`: + + 'default' => env('MAIL_MAILER', 'mailgun'), + +Second, add the following configuration array to your array of `mailers`: 'mailgun' => [ 'transport' => 'mailgun', From 64976f8cf35d1fc633d862669a58f316766692af Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 10 Feb 2025 19:17:17 +0800 Subject: [PATCH 1993/2609] Update Laravel 12 release plan (#10156) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index ff5401eae82..6008bea61bf 100644 --- a/releases.md +++ b/releases.md @@ -28,7 +28,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 - 8.4 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | -| 12 | 8.2 - 8.4 | Q1 2025 | Q3 2026 | Q1 2027 | +| 12 | 8.2 - 8.4 | February 24th, 2025 | August 13th, 2026 | February 24th, 2027 | From e72cad414639359b6d41ae7ac626c2f821c387c8 Mon Sep 17 00:00:00 2001 From: Daramola Babatunde <62488893+ritechoice23@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:02:29 +0100 Subject: [PATCH 1994/2609] Add cloudflare R2 to the list of S3 Compatible Filesystems (#10158) * Add cloudflare R2 to the list of S3 Compatible Filesystems * Update filesystem.md --------- Co-authored-by: Taylor Otwell --- filesystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem.md b/filesystem.md index 8486e0041d9..dd302ead5e5 100644 --- a/filesystem.md +++ b/filesystem.md @@ -203,7 +203,7 @@ Next, you may include the `read-only` configuration option in one or more of you ### Amazon S3 Compatible Filesystems -By default, your application's `filesystems` configuration file contains a disk configuration for the `s3` disk. In addition to using this disk to interact with [Amazon S3](https://aws.amazon.com/s3/), you may use it to interact with any S3-compatible file storage service such as [MinIO](https://github.com/minio/minio), [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/), [Akamai / Linode Object Storage](https://www.linode.com/products/object-storage/), [Vultr Object Storage](https://www.vultr.com/products/object-storage/), or [Hetzner Cloud Storage](https://www.hetzner.com/storage/object-storage/). +By default, your application's `filesystems` configuration file contains a disk configuration for the `s3` disk. In addition to using this disk to interact with [Amazon S3](https://aws.amazon.com/s3/), you may use it to interact with any S3-compatible file storage service such as [MinIO](https://github.com/minio/minio), [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/), [Vultr Object Storage](https://www.vultr.com/products/object-storage/), [Cloudflare R2](https://www.cloudflare.com/developer-platform/products/r2/), or [Hetzner Cloud Storage](https://www.hetzner.com/storage/object-storage/). Typically, after updating the disk's credentials to match the credentials of the service you are planning to use, you only need to update the value of the `endpoint` configuration option. This option's value is typically defined via the `AWS_ENDPOINT` environment variable: From 8561390252a1b5632e0d15adc160b3519fd21f0b Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 12 Feb 2025 02:49:06 +1100 Subject: [PATCH 1995/2609] Improve wizard documentation (#10058) --- precognition.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/precognition.md b/precognition.md index a3942e15135..1af5cfa8110 100644 --- a/precognition.md +++ b/precognition.md @@ -152,12 +152,13 @@ If you are validating a subset of a form's inputs with Precognition, it can be u As we have seen, you can hook into an input's `change` event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step. -To do this with Precognition, you should mark the fields you wish to validate as "touched" by passing their names to the `touch` method. Then, call the `validate` method with `onSuccess` or `onValidationError` callbacks: +To do this with Precognition, you should call the `validate` method passing the field names you wish to validate to the `only` configuration key. You may handle the validation result with `onSuccess` or `onValidationError` callbacks: ```html + +``` - // Test... +```php +// Test... - $browser->click('.login-page .container div > button'); +$browser->click('.login-page .container div > button'); +``` Dusk selectors allow you to focus on writing effective tests rather than remembering CSS selectors. To define a selector, add a `dusk` attribute to your HTML element. Then, when interacting with a Dusk browser, prefix the selector with `@` to manipulate the attached element within your test: - // HTML... +```html +// HTML... - + +``` - // Test... +```php +// Test... - $browser->click('@login-button'); +$browser->click('@login-button'); +``` If desired, you may customize the HTML attribute that the Dusk selector utilizes via the `selectorHtmlAttribute` method. Typically, this method should be called from the `boot` method of your application's `AppServiceProvider`: - use Laravel\Dusk\Dusk; +```php +use Laravel\Dusk\Dusk; - Dusk::selectorHtmlAttribute('data-dusk'); +Dusk::selectorHtmlAttribute('data-dusk'); +``` ### Text, Values, and Attributes @@ -611,29 +681,37 @@ If desired, you may customize the HTML attribute that the Dusk selector utilizes Dusk provides several methods for interacting with the current value, display text, and attributes of elements on the page. For example, to get the "value" of an element that matches a given CSS or Dusk selector, use the `value` method: - // Retrieve the value... - $value = $browser->value('selector'); +```php +// Retrieve the value... +$value = $browser->value('selector'); - // Set the value... - $browser->value('selector', 'value'); +// Set the value... +$browser->value('selector', 'value'); +``` You may use the `inputValue` method to get the "value" of an input element that has a given field name: - $value = $browser->inputValue('field'); +```php +$value = $browser->inputValue('field'); +``` #### Retrieving Text The `text` method may be used to retrieve the display text of an element that matches the given selector: - $text = $browser->text('selector'); +```php +$text = $browser->text('selector'); +``` #### Retrieving Attributes Finally, the `attribute` method may be used to retrieve the value of an attribute of an element matching the given selector: - $attribute = $browser->attribute('selector', 'value'); +```php +$attribute = $browser->attribute('selector', 'value'); +``` ### Interacting With Forms @@ -643,69 +721,95 @@ Finally, the `attribute` method may be used to retrieve the value of an attribut Dusk provides a variety of methods for interacting with forms and input elements. First, let's take a look at an example of typing text into an input field: - $browser->type('email', 'taylor@laravel.com'); +```php +$browser->type('email', 'taylor@laravel.com'); +``` Note that, although the method accepts one if necessary, we are not required to pass a CSS selector into the `type` method. If a CSS selector is not provided, Dusk will search for an `input` or `textarea` field with the given `name` attribute. To append text to a field without clearing its content, you may use the `append` method: - $browser->type('tags', 'foo') - ->append('tags', ', bar, baz'); +```php +$browser->type('tags', 'foo') + ->append('tags', ', bar, baz'); +``` You may clear the value of an input using the `clear` method: - $browser->clear('email'); +```php +$browser->clear('email'); +``` You can instruct Dusk to type slowly using the `typeSlowly` method. By default, Dusk will pause for 100 milliseconds between key presses. To customize the amount of time between key presses, you may pass the appropriate number of milliseconds as the third argument to the method: - $browser->typeSlowly('mobile', '+1 (202) 555-5555'); +```php +$browser->typeSlowly('mobile', '+1 (202) 555-5555'); - $browser->typeSlowly('mobile', '+1 (202) 555-5555', 300); +$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300); +``` You may use the `appendSlowly` method to append text slowly: - $browser->type('tags', 'foo') - ->appendSlowly('tags', ', bar, baz'); +```php +$browser->type('tags', 'foo') + ->appendSlowly('tags', ', bar, baz'); +``` #### Dropdowns To select a value available on a `select` element, you may use the `select` method. Like the `type` method, the `select` method does not require a full CSS selector. When passing a value to the `select` method, you should pass the underlying option value instead of the display text: - $browser->select('size', 'Large'); +```php +$browser->select('size', 'Large'); +``` You may select a random option by omitting the second argument: - $browser->select('size'); +```php +$browser->select('size'); +``` + +By providing an array as the second```php -By providing an array as the second argument to the `select` method, you can instruct the method to select multiple options: +``` argument to the `select` method, you can instruct the method to select multiple options: - $browser->select('categories', ['Art', 'Music']); +```php +$browser->select('categories', ['Art', 'Music']); +``` #### Checkboxes To "check" a checkbox input, you may use the `check` method. Like many other input related methods, a full CSS selector is not required. If a CSS selector match can't be found, Dusk will search for a checkbox with a matching `name` attribute: - $browser->check('terms'); +```php +$browser->check('terms'); +``` The `uncheck` method may be used to "uncheck" a checkbox input: - $browser->uncheck('terms'); +```php +$browser->uncheck('terms'); +``` #### Radio Buttons To "select" a `radio` input option, you may use the `radio` method. Like many other input related methods, a full CSS selector is not required. If a CSS selector match can't be found, Dusk will search for a `radio` input with matching `name` and `value` attributes: - $browser->radio('size', 'large'); +```php +$browser->radio('size', 'large'); +``` ### Attaching Files The `attach` method may be used to attach a file to a `file` input element. Like many other input related methods, a full CSS selector is not required. If a CSS selector match can't be found, Dusk will search for a `file` input with a matching `name` attribute: - $browser->attach('photo', __DIR__.'/photos/mountains.png'); +```php +$browser->attach('photo', __DIR__.'/photos/mountains.png'); +``` > [!WARNING] > The attach function requires the `Zip` PHP extension to be installed and enabled on your server. @@ -715,28 +819,36 @@ The `attach` method may be used to attach a file to a `file` input element. Like The `press` method may be used to click a button element on the page. The argument given to the `press` method may be either the display text of the button or a CSS / Dusk selector: - $browser->press('Login'); +```php +$browser->press('Login'); +``` When submitting forms, many applications disable the form's submission button after it is pressed and then re-enable the button when the form submission's HTTP request is complete. To press a button and wait for the button to be re-enabled, you may use the `pressAndWaitFor` method: - // Press the button and wait a maximum of 5 seconds for it to be enabled... - $browser->pressAndWaitFor('Save'); +```php +// Press the button and wait a maximum of 5 seconds for it to be enabled... +$browser->pressAndWaitFor('Save'); - // Press the button and wait a maximum of 1 second for it to be enabled... - $browser->pressAndWaitFor('Save', 1); +// Press the button and wait a maximum of 1 second for it to be enabled... +$browser->pressAndWaitFor('Save', 1); +``` ### Clicking Links To click a link, you may use the `clickLink` method on the browser instance. The `clickLink` method will click the link that has the given display text: - $browser->clickLink($linkText); +```php +$browser->clickLink($linkText); +``` You may use the `seeLink` method to determine if a link with the given display text is visible on the page: - if ($browser->seeLink($linkText)) { - // ... - } +```php +if ($browser->seeLink($linkText)) { + // ... +} +``` > [!WARNING] > These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. @@ -746,11 +858,15 @@ You may use the `seeLink` method to determine if a link with the given display t The `keys` method allows you to provide more complex input sequences to a given element than normally allowed by the `type` method. For example, you may instruct Dusk to hold modifier keys while entering values. In this example, the `shift` key will be held while `taylor` is entered into the element matching the given selector. After `taylor` is typed, `swift` will be typed without any modifier keys: - $browser->keys('selector', ['{shift}', 'taylor'], 'swift'); +```php +$browser->keys('selector', ['{shift}', 'taylor'], 'swift'); +``` Another valuable use case for the `keys` method is sending a "keyboard shortcut" combination to the primary CSS selector for your application: - $browser->keys('.app', ['{command}', 'j']); +```php +$browser->keys('.app', ['{command}', 'j']); +``` > [!NOTE] > All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). @@ -760,60 +876,66 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" Dusk also provides a `withKeyboard` method, allowing you to fluently perform complex keyboard interactions via the `Laravel\Dusk\Keyboard` class. The `Keyboard` class provides `press`, `release`, `type`, and `pause` methods: - use Laravel\Dusk\Keyboard; +```php +use Laravel\Dusk\Keyboard; - $browser->withKeyboard(function (Keyboard $keyboard) { - $keyboard->press('c') - ->pause(1000) - ->release('c') - ->type(['c', 'e', 'o']); - }); +$browser->withKeyboard(function (Keyboard $keyboard) { + $keyboard->press('c') + ->pause(1000) + ->release('c') + ->type(['c', 'e', 'o']); +}); +``` #### Keyboard Macros If you would like to define custom keyboard interactions that you can easily re-use throughout your test suite, you may use the `macro` method provided by the `Keyboard` class. Typically, you should call this method from a [service provider's](/docs/{{version}}/providers) `boot` method: - type([ - OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', - ]); - - return $this; - }); + Keyboard::macro('copy', function (string $element = null) { + $this->type([ + OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', + ]); - Keyboard::macro('paste', function (string $element = null) { - $this->type([ - OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', - ]); + return $this; + }); - return $this; - }); - } + Keyboard::macro('paste', function (string $element = null) { + $this->type([ + OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', + ]); + + return $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 as a method on a `Keyboard` instance: - $browser->click('@textarea') - ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) - ->click('@another-textarea') - ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste()); +```php +$browser->click('@textarea') + ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) + ->click('@another-textarea') + ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste()); +``` ### Using the Mouse @@ -823,127 +945,165 @@ The `macro` function accepts a name as its first argument and a closure as its s The `click` method may be used to click on an element matching the given CSS or Dusk selector: - $browser->click('.selector'); +```php +$browser->click('.selector'); +``` The `clickAtXPath` method may be used to click on an element matching the given XPath expression: - $browser->clickAtXPath('//div[@class = "selector"]'); +```php +$browser->clickAtXPath('//div[@class = "selector"]'); +``` The `clickAtPoint` method may be used to click on the topmost element at a given pair of coordinates relative to the viewable area of the browser: - $browser->clickAtPoint($x = 0, $y = 0); +```php +$browser->clickAtPoint($x = 0, $y = 0); +``` The `doubleClick` method may be used to simulate the double click of a mouse: - $browser->doubleClick(); +```php +$browser->doubleClick(); - $browser->doubleClick('.selector'); +$browser->doubleClick('.selector'); +``` The `rightClick` method may be used to simulate the right click of a mouse: - $browser->rightClick(); +```php +$browser->rightClick(); - $browser->rightClick('.selector'); +$browser->rightClick('.selector'); +``` The `clickAndHold` method may be used to simulate a mouse button being clicked and held down. A subsequent call to the `releaseMouse` method will undo this behavior and release the mouse button: - $browser->clickAndHold('.selector'); +```php +$browser->clickAndHold('.selector'); - $browser->clickAndHold() - ->pause(1000) - ->releaseMouse(); +$browser->clickAndHold() + ->pause(1000) + ->releaseMouse(); +``` The `controlClick` method may be used to simulate the `ctrl+click` event within the browser: - $browser->controlClick(); +```php +$browser->controlClick(); - $browser->controlClick('.selector'); +$browser->controlClick('.selector'); +``` #### Mouseover The `mouseover` method may be used when you need to move the mouse over an element matching the given CSS or Dusk selector: - $browser->mouseover('.selector'); +```php +$browser->mouseover('.selector'); +``` #### Drag and Drop The `drag` method may be used to drag an element matching the given selector to another element: - $browser->drag('.from-selector', '.to-selector'); +```php +$browser->drag('.from-selector', '.to-selector'); +``` Or, you may drag an element in a single direction: - $browser->dragLeft('.selector', $pixels = 10); - $browser->dragRight('.selector', $pixels = 10); - $browser->dragUp('.selector', $pixels = 10); - $browser->dragDown('.selector', $pixels = 10); +```php +$browser->dragLeft('.selector', $pixels = 10); +$browser->dragRight('.selector', $pixels = 10); +$browser->dragUp('.selector', $pixels = 10); +$browser->dragDown('.selector', $pixels = 10); +``` Finally, you may drag an element by a given offset: - $browser->dragOffset('.selector', $x = 10, $y = 10); +```php +$browser->dragOffset('.selector', $x = 10, $y = 10); +``` ### JavaScript Dialogs Dusk provides various methods to interact with JavaScript Dialogs. For example, you may use the `waitForDialog` method to wait for a JavaScript dialog to appear. This method accepts an optional argument indicating how many seconds to wait for the dialog to appear: - $browser->waitForDialog($seconds = null); +```php +$browser->waitForDialog($seconds = null); +``` The `assertDialogOpened` method may be used to assert that a dialog has been displayed and contains the given message: - $browser->assertDialogOpened('Dialog message'); +```php +$browser->assertDialogOpened('Dialog message'); +``` If the JavaScript dialog contains a prompt, you may use the `typeInDialog` method to type a value into the prompt: - $browser->typeInDialog('Hello World'); +```php +$browser->typeInDialog('Hello World'); +``` To close an open JavaScript dialog by clicking the "OK" button, you may invoke the `acceptDialog` method: - $browser->acceptDialog(); +```php +$browser->acceptDialog(); +``` To close an open JavaScript dialog by clicking the "Cancel" button, you may invoke the `dismissDialog` method: - $browser->dismissDialog(); +```php +$browser->dismissDialog(); +``` ### Interacting With Inline Frames If you need to interact with elements within an iframe, you may use the `withinFrame` method. All element interactions that take place within the closure provided to the `withinFrame` method will be scoped to the context of the specified iframe: - $browser->withinFrame('#credit-card-details', function ($browser) { - $browser->type('input[name="cardnumber"]', '4242424242424242') - ->type('input[name="exp-date"]', '1224') - ->type('input[name="cvc"]', '123') - ->press('Pay'); - }); +```php +$browser->withinFrame('#credit-card-details', function ($browser) { + $browser->type('input[name="cardnumber"]', '4242424242424242') + ->type('input[name="exp-date"]', '1224') + ->type('input[name="cvc"]', '123') + ->press('Pay'); +}); +``` ### Scoping Selectors Sometimes you may wish to perform several operations while scoping all of the operations within a given selector. For example, you may wish to assert that some text exists only within a table and then click a button within that table. You may use the `with` method to accomplish this. All operations performed within the closure given to the `with` method will be scoped to the original selector: - $browser->with('.table', function (Browser $table) { - $table->assertSee('Hello World') - ->clickLink('Delete'); - }); +```php +$browser->with('.table', function (Browser $table) { + $table->assertSee('Hello World') + ->clickLink('Delete'); +}); +``` You may occasionally need to execute assertions outside of the current scope. You may use the `elsewhere` and `elsewhereWhenAvailable` methods to accomplish this: - $browser->with('.table', function (Browser $table) { - // Current scope is `body .table`... +```php +$browser->with('.table', function (Browser $table) { + // Current scope is `body .table`... - $browser->elsewhere('.page-title', function (Browser $title) { - // Current scope is `body .page-title`... - $title->assertSee('Hello World'); - }); + $browser->elsewhere('.page-title', function (Browser $title) { + // Current scope is `body .page-title`... + $title->assertSee('Hello World'); + }); - $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { - // Current scope is `body .page-title`... - $title->assertSee('Hello World'); - }); - }); + $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { + // Current scope is `body .page-title`... + $title->assertSee('Hello World'); + }); +}); +``` ### Waiting for Elements @@ -955,204 +1115,254 @@ When testing applications that use JavaScript extensively, it often becomes nece If you just need to pause the test for a given number of milliseconds, use the `pause` method: - $browser->pause(1000); +```php +$browser->pause(1000); +``` If you need to pause the test only if a given condition is `true`, use the `pauseIf` method: - $browser->pauseIf(App::environment('production'), 1000); +```php +$browser->pauseIf(App::environment('production'), 1000); +``` Likewise, if you need to pause the test unless a given condition is `true`, you may use the `pauseUnless` method: - $browser->pauseUnless(App::environment('testing'), 1000); +```php +$browser->pauseUnless(App::environment('testing'), 1000); +``` #### Waiting for Selectors The `waitFor` method may be used to pause the execution of the test until the element matching the given CSS or Dusk selector is displayed on the page. By default, this will pause the test for a maximum of five seconds before throwing an exception. If necessary, you may pass a custom timeout threshold as the second argument to the method: - // Wait a maximum of five seconds for the selector... - $browser->waitFor('.selector'); +```php +// Wait a maximum of five seconds for the selector... +$browser->waitFor('.selector'); - // Wait a maximum of one second for the selector... - $browser->waitFor('.selector', 1); +// Wait a maximum of one second for the selector... +$browser->waitFor('.selector', 1); +``` You may also wait until the element matching the given selector contains the given text: - // Wait a maximum of five seconds for the selector to contain the given text... - $browser->waitForTextIn('.selector', 'Hello World'); +```php +// Wait a maximum of five seconds for the selector to contain the given text... +$browser->waitForTextIn('.selector', 'Hello World'); - // Wait a maximum of one second for the selector to contain the given text... - $browser->waitForTextIn('.selector', 'Hello World', 1); +// Wait a maximum of one second for the selector to contain the given text... +$browser->waitForTextIn('.selector', 'Hello World', 1); +``` You may also wait until the element matching the given selector is missing from the page: - // Wait a maximum of five seconds until the selector is missing... - $browser->waitUntilMissing('.selector'); +```php +// Wait a maximum of five seconds until the selector is missing... +$browser->waitUntilMissing('.selector'); - // Wait a maximum of one second until the selector is missing... - $browser->waitUntilMissing('.selector', 1); +// Wait a maximum of one second until the selector is missing... +$browser->waitUntilMissing('.selector', 1); +``` Or, you may wait until the element matching the given selector is enabled or disabled: - // Wait a maximum of five seconds until the selector is enabled... - $browser->waitUntilEnabled('.selector'); +```php +// Wait a maximum of five seconds until the selector is enabled... +$browser->waitUntilEnabled('.selector'); - // Wait a maximum of one second until the selector is enabled... - $browser->waitUntilEnabled('.selector', 1); +// Wait a maximum of one second until the selector is enabled... +$browser->waitUntilEnabled('.selector', 1); - // Wait a maximum of five seconds until the selector is disabled... - $browser->waitUntilDisabled('.selector'); +// Wait a maximum of five seconds until the selector is disabled... +$browser->waitUntilDisabled('.selector'); - // Wait a maximum of one second until the selector is disabled... - $browser->waitUntilDisabled('.selector', 1); +// Wait a maximum of one second until the selector is disabled... +$browser->waitUntilDisabled('.selector', 1); +``` #### Scoping Selectors When Available Occasionally, you may wish to wait for an element to appear that matches a given selector and then interact with the element. For example, you may wish to wait until a modal window is available and then press the "OK" button within the modal. The `whenAvailable` method may be used to accomplish this. All element operations performed within the given closure will be scoped to the original selector: - $browser->whenAvailable('.modal', function (Browser $modal) { - $modal->assertSee('Hello World') - ->press('OK'); - }); +```php +$browser->whenAvailable('.modal', function (Browser $modal) { + $modal->assertSee('Hello World') + ->press('OK'); +}); +``` #### Waiting for Text The `waitForText` method may be used to wait until the given text is displayed on the page: - // Wait a maximum of five seconds for the text... - $browser->waitForText('Hello World'); +```php +// Wait a maximum of five seconds for the text... +$browser->waitForText('Hello World'); - // Wait a maximum of one second for the text... - $browser->waitForText('Hello World', 1); +// Wait a maximum of one second for the text... +$browser->waitForText('Hello World', 1); +``` You may use the `waitUntilMissingText` method to wait until the displayed text has been removed from the page: - // Wait a maximum of five seconds for the text to be removed... - $browser->waitUntilMissingText('Hello World'); +```php +// Wait a maximum of five seconds for the text to be removed... +$browser->waitUntilMissingText('Hello World'); - // Wait a maximum of one second for the text to be removed... - $browser->waitUntilMissingText('Hello World', 1); +// Wait a maximum of one second for the text to be removed... +$browser->waitUntilMissingText('Hello World', 1); +``` #### Waiting for Links The `waitForLink` method may be used to wait until the given link text is displayed on the page: - // Wait a maximum of five seconds for the link... - $browser->waitForLink('Create'); +```php +// Wait a maximum of five seconds for the link... +$browser->waitForLink('Create'); - // Wait a maximum of one second for the link... - $browser->waitForLink('Create', 1); +// Wait a maximum of one second for the link... +$browser->waitForLink('Create', 1); +``` #### Waiting for Inputs The `waitForInput` method may be used to wait until the given input field is visible on the page: - // Wait a maximum of five seconds for the input... - $browser->waitForInput($field); +```php +// Wait a maximum of five seconds for the input... +$browser->waitForInput($field); - // Wait a maximum of one second for the input... - $browser->waitForInput($field, 1); +// Wait a maximum of one second for the input... +$browser->waitForInput($field, 1); +``` #### Waiting on the Page Location When making a path assertion such as `$browser->assertPathIs('/home')`, the assertion can fail if `window.location.pathname` is being updated asynchronously. You may use the `waitForLocation` method to wait for the location to be a given value: - $browser->waitForLocation('/secret'); +```php +$browser->waitForLocation('/secret'); +``` The `waitForLocation` method can also be used to wait for the current window location to be a fully qualified URL: - $browser->waitForLocation('https://example.com/path'); +```php +$browser->waitForLocation('https://example.com/path'); +``` You may also wait for a [named route's](/docs/{{version}}/routing#named-routes) location: - $browser->waitForRoute($routeName, $parameters); +```php +$browser->waitForRoute($routeName, $parameters); +``` #### Waiting for Page Reloads If you need to wait for a page to reload after performing an action, use the `waitForReload` method: - use Laravel\Dusk\Browser; +```php +use Laravel\Dusk\Browser; - $browser->waitForReload(function (Browser $browser) { - $browser->press('Submit'); - }) - ->assertSee('Success!'); +$browser->waitForReload(function (Browser $browser) { + $browser->press('Submit'); +}) +->assertSee('Success!'); +``` Since the need to wait for the page to reload typically occurs after clicking a button, you may use the `clickAndWaitForReload` method for convenience: - $browser->clickAndWaitForReload('.selector') - ->assertSee('something'); +```php +$browser->clickAndWaitForReload('.selector') + ->assertSee('something'); +``` #### Waiting on JavaScript Expressions Sometimes you may wish to pause the execution of a test until a given JavaScript expression evaluates to `true`. You may easily accomplish this using the `waitUntil` method. When passing an expression to this method, you do not need to include the `return` keyword or an ending semi-colon: - // Wait a maximum of five seconds for the expression to be true... - $browser->waitUntil('App.data.servers.length > 0'); +```php +// Wait a maximum of five seconds for the expression to be true... +$browser->waitUntil('App.data.servers.length > 0'); - // Wait a maximum of one second for the expression to be true... - $browser->waitUntil('App.data.servers.length > 0', 1); +// Wait a maximum of one second for the expression to be true... +$browser->waitUntil('App.data.servers.length > 0', 1); +``` #### Waiting on Vue Expressions The `waitUntilVue` and `waitUntilVueIsNot` methods may be used to wait until a [Vue component](https://vuejs.org) attribute has a given value: - // Wait until the component attribute contains the given value... - $browser->waitUntilVue('user.name', 'Taylor', '@user'); +```php +// Wait until the component attribute contains the given value... +$browser->waitUntilVue('user.name', 'Taylor', '@user'); - // Wait until the component attribute doesn't contain the given value... - $browser->waitUntilVueIsNot('user.name', null, '@user'); +// Wait until the component attribute doesn't contain the given value... +$browser->waitUntilVueIsNot('user.name', null, '@user'); +``` #### Waiting for JavaScript Events The `waitForEvent` method can be used to pause the execution of a test until a JavaScript event occurs: - $browser->waitForEvent('load'); +```php +$browser->waitForEvent('load'); +``` The event listener is attached to the current scope, which is the `body` element by default. When using a scoped selector, the event listener will be attached to the matching element: - $browser->with('iframe', function (Browser $iframe) { - // Wait for the iframe's load event... - $iframe->waitForEvent('load'); - }); +```php +$browser->with('iframe', function (Browser $iframe) { + // Wait for the iframe's load event... + $iframe->waitForEvent('load'); +}); +``` You may also provide a selector as the second argument to the `waitForEvent` method to attach the event listener to a specific element: - $browser->waitForEvent('load', '.selector'); +```php +$browser->waitForEvent('load', '.selector'); +``` You may also wait for events on the `document` and `window` objects: - // Wait until the document is scrolled... - $browser->waitForEvent('scroll', 'document'); +```php +// Wait until the document is scrolled... +$browser->waitForEvent('scroll', 'document'); - // Wait a maximum of five seconds until the window is resized... - $browser->waitForEvent('resize', 'window', 5); +// Wait a maximum of five seconds until the window is resized... +$browser->waitForEvent('resize', 'window', 5); +``` #### Waiting With a Callback Many of the "wait" methods in Dusk rely on the underlying `waitUsing` method. You may use this method directly to wait for a given closure to return `true`. The `waitUsing` method accepts the maximum number of seconds to wait, the interval at which the closure should be evaluated, the closure, and an optional failure message: - $browser->waitUsing(10, 1, function () use ($something) { - return $something->isReady(); - }, "Something wasn't ready in time."); +```php +$browser->waitUsing(10, 1, function () use ($something) { + return $something->isReady(); +}, "Something wasn't ready in time."); +``` ### Scrolling an Element Into View Sometimes you may not be able to click on an element because it is outside of the viewable area of the browser. The `scrollIntoView` method will scroll the browser window until the element at the given selector is within the view: - $browser->scrollIntoView('.selector') - ->click('.selector'); +```php +$browser->scrollIntoView('.selector') + ->click('.selector'); +``` ## Available Assertions @@ -1260,383 +1470,493 @@ Dusk provides a variety of assertions that you may make against your application Assert that the page title matches the given text: - $browser->assertTitle($title); +```php +$browser->assertTitle($title); +``` #### assertTitleContains Assert that the page title contains the given text: - $browser->assertTitleContains($title); +```php +$browser->assertTitleContains($title); +``` #### assertUrlIs Assert that the current URL (without the query string) matches the given string: - $browser->assertUrlIs($url); +```php +$browser->assertUrlIs($url); +``` #### assertSchemeIs Assert that the current URL scheme matches the given scheme: - $browser->assertSchemeIs($scheme); +```php +$browser->assertSchemeIs($scheme); +``` #### assertSchemeIsNot Assert that the current URL scheme does not match the given scheme: - $browser->assertSchemeIsNot($scheme); +```php +$browser->assertSchemeIsNot($scheme); +``` #### assertHostIs Assert that the current URL host matches the given host: - $browser->assertHostIs($host); +```php +$browser->assertHostIs($host); +``` #### assertHostIsNot Assert that the current URL host does not match the given host: - $browser->assertHostIsNot($host); +```php +$browser->assertHostIsNot($host); +``` #### assertPortIs Assert that the current URL port matches the given port: - $browser->assertPortIs($port); +```php +$browser->assertPortIs($port); +``` #### assertPortIsNot Assert that the current URL port does not match the given port: - $browser->assertPortIsNot($port); +```php +$browser->assertPortIsNot($port); +``` #### assertPathBeginsWith Assert that the current URL path begins with the given path: - $browser->assertPathBeginsWith('/home'); +```php +$browser->assertPathBeginsWith('/home'); +``` #### assertPathEndsWith Assert that the current URL path ends with the given path: - $browser->assertPathEndsWith('/home'); +```php +$browser->assertPathEndsWith('/home'); +``` #### assertPathContains Assert that the current URL path contains the given path: - $browser->assertPathContains('/home'); +```php +$browser->assertPathContains('/home'); +``` #### assertPathIs Assert that the current path matches the given path: - $browser->assertPathIs('/home'); +```php +$browser->assertPathIs('/home'); +``` #### assertPathIsNot Assert that the current path does not match the given path: - $browser->assertPathIsNot('/home'); +```php +$browser->assertPathIsNot('/home'); +``` #### assertRouteIs Assert that the current URL matches the given [named route's](/docs/{{version}}/routing#named-routes) URL: - $browser->assertRouteIs($name, $parameters); +```php +$browser->assertRouteIs($name, $parameters); +``` #### assertQueryStringHas Assert that the given query string parameter is present: - $browser->assertQueryStringHas($name); +```php +$browser->assertQueryStringHas($name); +``` Assert that the given query string parameter is present and has a given value: - $browser->assertQueryStringHas($name, $value); +```php +$browser->assertQueryStringHas($name, $value); +``` #### assertQueryStringMissing Assert that the given query string parameter is missing: - $browser->assertQueryStringMissing($name); +```php +$browser->assertQueryStringMissing($name); +``` #### assertFragmentIs Assert that the URL's current hash fragment matches the given fragment: - $browser->assertFragmentIs('anchor'); +```php +$browser->assertFragmentIs('anchor'); +``` #### assertFragmentBeginsWith Assert that the URL's current hash fragment begins with the given fragment: - $browser->assertFragmentBeginsWith('anchor'); +```php +$browser->assertFragmentBeginsWith('anchor'); +``` #### assertFragmentIsNot Assert that the URL's current hash fragment does not match the given fragment: - $browser->assertFragmentIsNot('anchor'); +```php +$browser->assertFragmentIsNot('anchor'); +``` #### assertHasCookie Assert that the given encrypted cookie is present: - $browser->assertHasCookie($name); +```php +$browser->assertHasCookie($name); +``` #### assertHasPlainCookie Assert that the given unencrypted cookie is present: - $browser->assertHasPlainCookie($name); +```php +$browser->assertHasPlainCookie($name); +``` #### assertCookieMissing Assert that the given encrypted cookie is not present: - $browser->assertCookieMissing($name); +```php +$browser->assertCookieMissing($name); +``` #### assertPlainCookieMissing Assert that the given unencrypted cookie is not present: - $browser->assertPlainCookieMissing($name); +```php +$browser->assertPlainCookieMissing($name); +``` #### assertCookieValue Assert that an encrypted cookie has a given value: - $browser->assertCookieValue($name, $value); +```php +$browser->assertCookieValue($name, $value); +``` #### assertPlainCookieValue Assert that an unencrypted cookie has a given value: - $browser->assertPlainCookieValue($name, $value); +```php +$browser->assertPlainCookieValue($name, $value); +``` #### assertSee Assert that the given text is present on the page: - $browser->assertSee($text); +```php +$browser->assertSee($text); +``` #### assertDontSee Assert that the given text is not present on the page: - $browser->assertDontSee($text); +```php +$browser->assertDontSee($text); +``` #### assertSeeIn Assert that the given text is present within the selector: - $browser->assertSeeIn($selector, $text); +```php +$browser->assertSeeIn($selector, $text); +``` #### assertDontSeeIn Assert that the given text is not present within the selector: - $browser->assertDontSeeIn($selector, $text); +```php +$browser->assertDontSeeIn($selector, $text); +``` #### assertSeeAnythingIn Assert that any text is present within the selector: - $browser->assertSeeAnythingIn($selector); +```php +$browser->assertSeeAnythingIn($selector); +``` #### assertSeeNothingIn Assert that no text is present within the selector: - $browser->assertSeeNothingIn($selector); +```php +$browser->assertSeeNothingIn($selector); +``` #### assertScript Assert that the given JavaScript expression evaluates to the given value: - $browser->assertScript('window.isLoaded') - ->assertScript('document.readyState', 'complete'); +```php +$browser->assertScript('window.isLoaded') + ->assertScript('document.readyState', 'complete'); +``` #### assertSourceHas Assert that the given source code is present on the page: - $browser->assertSourceHas($code); +```php +$browser->assertSourceHas($code); +``` #### assertSourceMissing Assert that the given source code is not present on the page: - $browser->assertSourceMissing($code); +```php +$browser->assertSourceMissing($code); +``` #### assertSeeLink Assert that the given link is present on the page: - $browser->assertSeeLink($linkText); +```php +$browser->assertSeeLink($linkText); +``` #### assertDontSeeLink Assert that the given link is not present on the page: - $browser->assertDontSeeLink($linkText); +```php +$browser->assertDontSeeLink($linkText); +``` #### assertInputValue Assert that the given input field has the given value: - $browser->assertInputValue($field, $value); +```php +$browser->assertInputValue($field, $value); +``` #### assertInputValueIsNot Assert that the given input field does not have the given value: - $browser->assertInputValueIsNot($field, $value); +```php +$browser->assertInputValueIsNot($field, $value); +``` #### assertChecked Assert that the given checkbox is checked: - $browser->assertChecked($field); +```php +$browser->assertChecked($field); +``` #### assertNotChecked Assert that the given checkbox is not checked: - $browser->assertNotChecked($field); +```php +$browser->assertNotChecked($field); +``` #### assertIndeterminate Assert that the given checkbox is in an indeterminate state: - $browser->assertIndeterminate($field); +```php +$browser->assertIndeterminate($field); +``` #### assertRadioSelected Assert that the given radio field is selected: - $browser->assertRadioSelected($field, $value); +```php +$browser->assertRadioSelected($field, $value); +``` #### assertRadioNotSelected Assert that the given radio field is not selected: - $browser->assertRadioNotSelected($field, $value); +```php +$browser->assertRadioNotSelected($field, $value); +``` #### assertSelected Assert that the given dropdown has the given value selected: - $browser->assertSelected($field, $value); +```php +$browser->assertSelected($field, $value); +``` #### assertNotSelected Assert that the given dropdown does not have the given value selected: - $browser->assertNotSelected($field, $value); +```php +$browser->assertNotSelected($field, $value); +``` #### assertSelectHasOptions Assert that the given array of values are available to be selected: - $browser->assertSelectHasOptions($field, $values); +```php +$browser->assertSelectHasOptions($field, $values); +``` #### assertSelectMissingOptions Assert that the given array of values are not available to be selected: - $browser->assertSelectMissingOptions($field, $values); +```php +$browser->assertSelectMissingOptions($field, $values); +``` #### assertSelectHasOption Assert that the given value is available to be selected on the given field: - $browser->assertSelectHasOption($field, $value); +```php +$browser->assertSelectHasOption($field, $value); +``` #### assertSelectMissingOption Assert that the given value is not available to be selected: - $browser->assertSelectMissingOption($field, $value); +```php +$browser->assertSelectMissingOption($field, $value); +``` #### assertValue Assert that the element matching the given selector has the given value: - $browser->assertValue($selector, $value); +```php +$browser->assertValue($selector, $value); +``` #### assertValueIsNot Assert that the element matching the given selector does not have the given value: - $browser->assertValueIsNot($selector, $value); +```php +$browser->assertValueIsNot($selector, $value); +``` #### assertAttribute Assert that the element matching the given selector has the given value in the provided attribute: - $browser->assertAttribute($selector, $attribute, $value); +```php +$browser->assertAttribute($selector, $attribute, $value); +``` #### assertAttributeMissing Assert that the element matching the given selector is missing the provided attribute: - $browser->assertAttributeMissing($selector, $attribute); +```php +$browser->assertAttributeMissing($selector, $attribute); +``` @@ -1644,148 +1964,192 @@ Assert that the element matching the given selector is missing the provided attr Assert that the element matching the given selector contains the given value in the provided attribute: - $browser->assertAttributeContains($selector, $attribute, $value); +```php +$browser->assertAttributeContains($selector, $attribute, $value); +``` #### assertAttributeDoesntContain Assert that the element matching the given selector does not contain the given value in the provided attribute: - $browser->assertAttributeDoesntContain($selector, $attribute, $value); +```php +$browser->assertAttributeDoesntContain($selector, $attribute, $value); +``` #### assertAriaAttribute Assert that the element matching the given selector has the given value in the provided aria attribute: - $browser->assertAriaAttribute($selector, $attribute, $value); +```php +$browser->assertAriaAttribute($selector, $attribute, $value); +``` For example, given the markup ``, you may assert against the `aria-label` attribute like so: - $browser->assertAriaAttribute('button', 'label', 'Add') +```php +$browser->assertAriaAttribute('button', 'label', 'Add') +``` #### assertDataAttribute Assert that the element matching the given selector has the given value in the provided data attribute: - $browser->assertDataAttribute($selector, $attribute, $value); +```php +$browser->assertDataAttribute($selector, $attribute, $value); +``` For example, given the markup ``, you may assert against the `data-label` attribute like so: - $browser->assertDataAttribute('#row-1', 'content', 'attendees') +```php +$browser->assertDataAttribute('#row-1', 'content', 'attendees') +``` #### assertVisible Assert that the element matching the given selector is visible: - $browser->assertVisible($selector); +```php +$browser->assertVisible($selector); +``` #### assertPresent Assert that the element matching the given selector is present in the source: - $browser->assertPresent($selector); +```php +$browser->assertPresent($selector); +``` #### assertNotPresent Assert that the element matching the given selector is not present in the source: - $browser->assertNotPresent($selector); +```php +$browser->assertNotPresent($selector); +``` #### assertMissing Assert that the element matching the given selector is not visible: - $browser->assertMissing($selector); +```php +$browser->assertMissing($selector); +``` #### assertInputPresent Assert that an input with the given name is present: - $browser->assertInputPresent($name); +```php +$browser->assertInputPresent($name); +``` #### assertInputMissing Assert that an input with the given name is not present in the source: - $browser->assertInputMissing($name); +```php +$browser->assertInputMissing($name); +``` #### assertDialogOpened Assert that a JavaScript dialog with the given message has been opened: - $browser->assertDialogOpened($message); +```php +$browser->assertDialogOpened($message); +``` #### assertEnabled Assert that the given field is enabled: - $browser->assertEnabled($field); +```php +$browser->assertEnabled($field); +``` #### assertDisabled Assert that the given field is disabled: - $browser->assertDisabled($field); +```php +$browser->assertDisabled($field); +``` #### assertButtonEnabled Assert that the given button is enabled: - $browser->assertButtonEnabled($button); +```php +$browser->assertButtonEnabled($button); +``` #### assertButtonDisabled Assert that the given button is disabled: - $browser->assertButtonDisabled($button); +```php +$browser->assertButtonDisabled($button); +``` #### assertFocused Assert that the given field is focused: - $browser->assertFocused($field); +```php +$browser->assertFocused($field); +``` #### assertNotFocused Assert that the given field is not focused: - $browser->assertNotFocused($field); +```php +$browser->assertNotFocused($field); +``` #### assertAuthenticated Assert that the user is authenticated: - $browser->assertAuthenticated(); +```php +$browser->assertAuthenticated(); +``` #### assertGuest Assert that the user is not authenticated: - $browser->assertGuest(); +```php +$browser->assertGuest(); +``` #### assertAuthenticatedAs Assert that the user is authenticated as the given user: - $browser->assertAuthenticatedAs($user); +```php +$browser->assertAuthenticatedAs($user); +``` #### assertVue @@ -1839,21 +2203,27 @@ public function test_vue(): void Assert that a given Vue component data property does not match the given value: - $browser->assertVueIsNot($property, $value, $componentSelector = null); +```php +$browser->assertVueIsNot($property, $value, $componentSelector = null); +``` #### assertVueContains Assert that a given Vue component data property is an array and contains the given value: - $browser->assertVueContains($property, $value, $componentSelector = null); +```php +$browser->assertVueContains($property, $value, $componentSelector = null); +``` #### assertVueDoesntContain Assert that a given Vue component data property is an array and does not contain the given value: - $browser->assertVueDoesntContain($property, $value, $componentSelector = null); +```php +$browser->assertVueDoesntContain($property, $value, $componentSelector = null); +``` ## Pages @@ -1865,7 +2235,9 @@ Sometimes, tests require several complicated actions to be performed in sequence To generate a page object, execute the `dusk:page` Artisan command. All page objects will be placed in your application's `tests/Browser/Pages` directory: - php artisan dusk:page Login +```shell +php artisan dusk:page Login +``` ### Configuring Pages @@ -1877,117 +2249,135 @@ By default, pages have three methods: `url`, `assert`, and `elements`. We will d The `url` method should return the path of the URL that represents the page. Dusk will use this URL when navigating to the page in the browser: - /** - * Get the URL for the page. - */ - public function url(): string - { - return '/login'; - } +```php +/** + * Get the URL for the page. + */ +public function url(): string +{ + return '/login'; +} +``` #### The `assert` Method The `assert` method may make any assertions necessary to verify that the browser is actually on the given page. It is not actually necessary to place anything within this method; however, you are free to make these assertions if you wish. These assertions will be run automatically when navigating to the page: - /** - * Assert that the browser is on the page. - */ - public function assert(Browser $browser): void - { - $browser->assertPathIs($this->url()); - } +```php +/** + * Assert that the browser is on the page. + */ +public function assert(Browser $browser): void +{ + $browser->assertPathIs($this->url()); +} +``` ### Navigating to Pages Once a page has been defined, you may navigate to it using the `visit` method: - use Tests\Browser\Pages\Login; +```php +use Tests\Browser\Pages\Login; - $browser->visit(new Login); +$browser->visit(new Login); +``` Sometimes you may already be on a given page and need to "load" the page's selectors and methods into the current test context. This is common when pressing a button and being redirected to a given page without explicitly navigating to it. In this situation, you may use the `on` method to load the page: - use Tests\Browser\Pages\CreatePlaylist; +```php +use Tests\Browser\Pages\CreatePlaylist; - $browser->visit('/dashboard') - ->clickLink('Create Playlist') - ->on(new CreatePlaylist) - ->assertSee('@create'); +$browser->visit('/dashboard') + ->clickLink('Create Playlist') + ->on(new CreatePlaylist) + ->assertSee('@create'); +``` ### Shorthand Selectors The `elements` method within page classes allows you to define quick, easy-to-remember shortcuts for any CSS selector on your page. For example, let's define a shortcut for the "email" input field of the application's login page: - /** - * Get the element shortcuts for the page. - * - * @return array - */ - public function elements(): array - { - return [ - '@email' => 'input[name=email]', - ]; - } +```php +/** + * Get the element shortcuts for the page. + * + * @return array + */ +public function elements(): array +{ + return [ + '@email' => 'input[name=email]', + ]; +} +``` Once the shortcut has been defined, you may use the shorthand selector anywhere you would typically use a full CSS selector: - $browser->type('@email', 'taylor@laravel.com'); +```php +$browser->type('@email', 'taylor@laravel.com'); +``` #### Global Shorthand Selectors After installing Dusk, a base `Page` class will be placed in your `tests/Browser/Pages` directory. This class contains a `siteElements` method which may be used to define global shorthand selectors that should be available on every page throughout your application: - /** - * Get the global element shortcuts for the site. - * - * @return array - */ - public static function siteElements(): array - { - return [ - '@element' => '#selector', - ]; - } +```php +/** + * Get the global element shortcuts for the site. + * + * @return array + */ +public static function siteElements(): array +{ + return [ + '@element' => '#selector', + ]; +} +``` ### Page Methods In addition to the default methods defined on pages, you may define additional methods which may be used throughout your tests. For example, let's imagine we are building a music management application. A common action for one page of the application might be to create a playlist. Instead of re-writing the logic to create a playlist in each test, you may define a `createPlaylist` method on a page class: - type('name', $name) - ->check('share') - ->press('Create Playlist'); - } + $browser->type('name', $name) + ->check('share') + ->press('Create Playlist'); } +} +``` Once the method has been defined, you may use it within any test that utilizes the page. The browser instance will automatically be passed as the first argument to custom page methods: - use Tests\Browser\Pages\Dashboard; +```php +use Tests\Browser\Pages\Dashboard; - $browser->visit(new Dashboard) - ->createPlaylist('My Playlist') - ->assertSee('My Playlist'); +$browser->visit(new Dashboard) + ->createPlaylist('My Playlist') + ->assertSee('My Playlist'); +``` ## Components @@ -1999,67 +2389,71 @@ Components are similar to Dusk’s “page objects”, but are intended for piec To generate a component, execute the `dusk:component` Artisan command. New components are placed in the `tests/Browser/Components` directory: - php artisan dusk:component DatePicker +```shell +php artisan dusk:component DatePicker +``` As shown above, a "date picker" is an example of a component that might exist throughout your application on a variety of pages. It can become cumbersome to manually write the browser automation logic to select a date in dozens of tests throughout your test suite. Instead, we can define a Dusk component to represent the date picker, allowing us to encapsulate that logic within the component: - assertVisible($this->selector()); - } + /** + * Assert that the browser page contains the component. + */ + public function assert(Browser $browser): void + { + $browser->assertVisible($this->selector()); + } - /** - * Get the element shortcuts for the component. - * - * @return array - */ - public function elements(): array - { - return [ - '@date-field' => 'input.datepicker-input', - '@year-list' => 'div > div.datepicker-years', - '@month-list' => 'div > div.datepicker-months', - '@day-list' => 'div > div.datepicker-days', - ]; - } + /** + * Get the element shortcuts for the component. + * + * @return array + */ + public function elements(): array + { + return [ + '@date-field' => 'input.datepicker-input', + '@year-list' => 'div > div.datepicker-years', + '@month-list' => 'div > div.datepicker-months', + '@day-list' => 'div > div.datepicker-days', + ]; + } - /** - * Select the given date. - */ - public function selectDate(Browser $browser, int $year, int $month, int $day): void - { - $browser->click('@date-field') - ->within('@year-list', function (Browser $browser) use ($year) { - $browser->click($year); - }) - ->within('@month-list', function (Browser $browser) use ($month) { - $browser->click($month); - }) - ->within('@day-list', function (Browser $browser) use ($day) { - $browser->click($day); - }); - } + /** + * Select the given date. + */ + public function selectDate(Browser $browser, int $year, int $month, int $day): void + { + $browser->click('@date-field') + ->within('@year-list', function (Browser $browser) use ($year) { + $browser->click($year); + }) + ->within('@month-list', function (Browser $browser) use ($month) { + $browser->click($month); + }) + ->within('@day-list', function (Browser $browser) use ($day) { + $browser->click($day); + }); } +} +``` ### Using Components @@ -2125,20 +2519,22 @@ class ExampleTest extends DuskTestCase To run Dusk tests on [Heroku CI](https://www.heroku.com/continuous-integration), add the following Google Chrome buildpack and scripts to your Heroku `app.json` file: - { - "environments": { - "test": { - "buildpacks": [ - { "url": "heroku/php" }, - { "url": "https://github.com/heroku/heroku-buildpack-chrome-for-testing" } - ], - "scripts": { - "test-setup": "cp .env.testing .env", - "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux --port=9515 > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk" - } - } +```json +{ + "environments": { + "test": { + "buildpacks": [ + { "url": "heroku/php" }, + { "url": "https://github.com/heroku/heroku-buildpack-chrome-for-testing" } + ], + "scripts": { + "test-setup": "cp .env.testing .env", + "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux --port=9515 > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk" } } + } +} +``` ### Travis CI diff --git a/eloquent-collections.md b/eloquent-collections.md index 860a1f7ea8e..4790f7a04e0 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -11,21 +11,25 @@ All Eloquent methods that return more than one model result will return instance All collections also serve as iterators, allowing you to loop over them as if they were simple PHP arrays: - use App\Models\User; +```php +use App\Models\User; - $users = User::where('active', 1)->get(); +$users = User::where('active', 1)->get(); - foreach ($users as $user) { - echo $user->name; - } +foreach ($users as $user) { + echo $user->name; +} +``` However, as previously mentioned, collections are much more powerful than arrays and expose a variety of map / reduce operations that may be chained using an intuitive interface. For example, we may remove all inactive models and then gather the first name for each remaining user: - $names = User::all()->reject(function (User $user) { - return $user->active === false; - })->map(function (User $user) { - return $user->name; - }); +```php +$names = User::all()->reject(function (User $user) { + return $user->active === false; +})->map(function (User $user) { + return $user->name; +}); +``` #### Eloquent Collection Conversion @@ -88,199 +92,239 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe The `append` method may be used to indicate that an attribute should be [appended](/docs/{{version}}/eloquent-serialization#appending-values-to-json) for every model in the collection. This method accepts an array of attributes or a single attribute: - $users->append('team'); +```php +$users->append('team'); - $users->append(['team', 'is_admin']); +$users->append(['team', 'is_admin']); +``` #### `contains($key, $operator = null, $value = null)` {.collection-method} The `contains` method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance: - $users->contains(1); +```php +$users->contains(1); - $users->contains(User::find(1)); +$users->contains(User::find(1)); +``` #### `diff($items)` {.collection-method} The `diff` method returns all of the models that are not present in the given collection: - use App\Models\User; +```php +use App\Models\User; - $users = $users->diff(User::whereIn('id', [1, 2, 3])->get()); +$users = $users->diff(User::whereIn('id', [1, 2, 3])->get()); +``` #### `except($keys)` {.collection-method} The `except` method returns all of the models that do not have the given primary keys: - $users = $users->except([1, 2, 3]); +```php +$users = $users->except([1, 2, 3]); +``` #### `find($key)` {.collection-method} The `find` method returns the model that has a primary key matching the given key. If `$key` is a model instance, `find` will attempt to return a model matching the primary key. If `$key` is an array of keys, `find` will return all models which have a primary key in the given array: - $users = User::all(); +```php +$users = User::all(); - $user = $users->find(1); +$user = $users->find(1); +``` #### `findOrFail($key)` {.collection-method} The `findOrFail` method returns the model that has a primary key matching the given key or throws an `Illuminate\Database\Eloquent\ModelNotFoundException` exception if no matching model can be found in the collection: - $users = User::all(); +```php +$users = User::all(); - $user = $users->findOrFail(1); +$user = $users->findOrFail(1); +``` #### `fresh($with = [])` {.collection-method} The `fresh` method retrieves a fresh instance of each model in the collection from the database. In addition, any specified relationships will be eager loaded: - $users = $users->fresh(); +```php +$users = $users->fresh(); - $users = $users->fresh('comments'); +$users = $users->fresh('comments'); +``` #### `intersect($items)` {.collection-method} The `intersect` method returns all of the models that are also present in the given collection: - use App\Models\User; +```php +use App\Models\User; - $users = $users->intersect(User::whereIn('id', [1, 2, 3])->get()); +$users = $users->intersect(User::whereIn('id', [1, 2, 3])->get()); +``` #### `load($relations)` {.collection-method} The `load` method eager loads the given relationships for all models in the collection: - $users->load(['comments', 'posts']); +```php +$users->load(['comments', 'posts']); - $users->load('comments.author'); +$users->load('comments.author'); - $users->load(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); +$users->load(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); +``` #### `loadMissing($relations)` {.collection-method} The `loadMissing` method eager loads the given relationships for all models in the collection if the relationships are not already loaded: - $users->loadMissing(['comments', 'posts']); +```php +$users->loadMissing(['comments', 'posts']); - $users->loadMissing('comments.author'); +$users->loadMissing('comments.author'); - $users->loadMissing(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); +$users->loadMissing(['comments', 'posts' => fn ($query) => $query->where('active', 1)]); +``` #### `modelKeys()` {.collection-method} The `modelKeys` method returns the primary keys for all models in the collection: - $users->modelKeys(); +```php +$users->modelKeys(); - // [1, 2, 3, 4, 5] +// [1, 2, 3, 4, 5] +``` #### `makeVisible($attributes)` {.collection-method} The `makeVisible` method [makes attributes visible](/docs/{{version}}/eloquent-serialization#hiding-attributes-from-json) that are typically "hidden" on each model in the collection: - $users = $users->makeVisible(['address', 'phone_number']); +```php +$users = $users->makeVisible(['address', 'phone_number']); +``` #### `makeHidden($attributes)` {.collection-method} The `makeHidden` method [hides attributes](/docs/{{version}}/eloquent-serialization#hiding-attributes-from-json) that are typically "visible" on each model in the collection: - $users = $users->makeHidden(['address', 'phone_number']); +```php +$users = $users->makeHidden(['address', 'phone_number']); +``` #### `only($keys)` {.collection-method} The `only` method returns all of the models that have the given primary keys: - $users = $users->only([1, 2, 3]); +```php +$users = $users->only([1, 2, 3]); +``` #### `setVisible($attributes)` {.collection-method} The `setVisible` method [temporarily overrides](/docs/{{version}}/eloquent-serialization#temporarily-modifying-attribute-visibility) all of the visible attributes on each model in the collection: - $users = $users->setVisible(['id', 'name']); +```php +$users = $users->setVisible(['id', 'name']); +``` #### `setHidden($attributes)` {.collection-method} The `setHidden` method [temporarily overrides](/docs/{{version}}/eloquent-serialization#temporarily-modifying-attribute-visibility) all of the hidden attributes on each model in the collection: - $users = $users->setHidden(['email', 'password', 'remember_token']); +```php +$users = $users->setHidden(['email', 'password', 'remember_token']); +``` #### `toQuery()` {.collection-method} The `toQuery` method returns an Eloquent query builder instance containing a `whereIn` constraint on the collection model's primary keys: - use App\Models\User; +```php +use App\Models\User; - $users = User::where('status', 'VIP')->get(); +$users = User::where('status', 'VIP')->get(); - $users->toQuery()->update([ - 'status' => 'Administrator', - ]); +$users->toQuery()->update([ + 'status' => 'Administrator', +]); +``` #### `unique($key = null, $strict = false)` {.collection-method} The `unique` method returns all of the unique models in the collection. Any models with the same primary key as another model in the collection are removed: - $users = $users->unique(); +```php +$users = $users->unique(); +``` ## Custom Collections If you would like to use a custom `Collection` object when interacting with a given model, you may add the `CollectedBy` attribute to your model: - $models + * @return \Illuminate\Database\Eloquent\Collection + */ + public function newCollection(array $models = []): Collection { - /** - * Create a new Eloquent Collection instance. - * - * @param array $models - * @return \Illuminate\Database\Eloquent\Collection - */ - public function newCollection(array $models = []): Collection - { - return new UserCollection($models); - } + return new UserCollection($models); } +} +``` Once you have defined a `newCollection` method or added the `CollectedBy` attribute to your model, you will receive an instance of your custom collection anytime Eloquent would normally return an `Illuminate\Database\Eloquent\Collection` instance. diff --git a/eloquent-factories.md b/eloquent-factories.md index dd315a8ff34..a95cfa08096 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -24,48 +24,50 @@ When testing your application or seeding your database, you may need to insert a To see an example of how to write a factory, take a look at the `database/factories/UserFactory.php` file in your application. This factory is included with all new Laravel applications and contains the following factory definition: - namespace Database\Factories; +```php +namespace Database\Factories; + +use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Str; + +/** + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User> + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; - use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Facades\Hash; - use Illuminate\Support\Str; + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User> + * Indicate that the model's email address should be unverified. */ - class UserFactory extends Factory + public function unverified(): static { - /** - * The current password being used by the factory. - */ - protected static ?string $password; - - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - return [ - 'name' => fake()->name(), - 'email' => fake()->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => static::$password ??= Hash::make('password'), - 'remember_token' => Str::random(10), - ]; - } - - /** - * Indicate that the model's email address should be unverified. - */ - public function unverified(): static - { - return $this->state(fn (array $attributes) => [ - 'email_verified_at' => null, - ]); - } + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); } +} +``` As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. @@ -95,30 +97,34 @@ Once you have defined your factories, you may use the static `factory` method pr The `HasFactory` trait's `factory` method will use conventions to determine the proper factory for the model the trait is assigned to. Specifically, the method will look for a factory in the `Database\Factories` namespace that has a class name matching the model name and is suffixed with `Factory`. If these conventions do not apply to your particular application or factory, you may overwrite the `newFactory` method on your model to return an instance of the model's corresponding factory directly: - use Database\Factories\Administration\FlightFactory; +```php +use Database\Factories\Administration\FlightFactory; - /** - * Create a new factory instance for the model. - */ - protected static function newFactory() - { - return FlightFactory::new(); - } +/** + * Create a new factory instance for the model. + */ +protected static function newFactory() +{ + return FlightFactory::new(); +} +``` Then, define a `model` property on the corresponding factory: - use App\Administration\Flight; - use Illuminate\Database\Eloquent\Factories\Factory; +```php +use App\Administration\Flight; +use Illuminate\Database\Eloquent\Factories\Factory; - class FlightFactory extends Factory - { - /** - * The name of the factory's corresponding model. - * - * @var class-string<\Illuminate\Database\Eloquent\Model> - */ - protected $model = Flight::class; - } +class FlightFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var class-string<\Illuminate\Database\Eloquent\Model> + */ + protected $model = Flight::class; +} +``` ### Factory States @@ -127,77 +133,85 @@ State manipulation methods allow you to define discrete modifications that can b State transformation methods typically call the `state` method provided by Laravel's base factory class. The `state` method accepts a closure which will receive the array of raw attributes defined for the factory and should return an array of attributes to modify: - use Illuminate\Database\Eloquent\Factories\Factory; +```php +use Illuminate\Database\Eloquent\Factories\Factory; - /** - * Indicate that the user is suspended. - */ - public function suspended(): Factory - { - return $this->state(function (array $attributes) { - return [ - 'account_status' => 'suspended', - ]; - }); - } +/** + * Indicate that the user is suspended. + */ +public function suspended(): Factory +{ + return $this->state(function (array $attributes) { + return [ + 'account_status' => 'suspended', + ]; + }); +} +``` #### "Trashed" State If your Eloquent model can be [soft deleted](/docs/{{version}}/eloquent#soft-deleting), you may invoke the built-in `trashed` state method to indicate that the created model should already be "soft deleted". You do not need to manually define the `trashed` state as it is automatically available to all factories: - use App\Models\User; +```php +use App\Models\User; - $user = User::factory()->trashed()->create(); +$user = User::factory()->trashed()->create(); +``` ### Factory Callbacks Factory callbacks are registered using the `afterMaking` and `afterCreating` methods and allow you to perform additional tasks after making or creating a model. You should register these callbacks by defining a `configure` method on your factory class. This method will be automatically called by Laravel when the factory is instantiated: - namespace Database\Factories; - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Factory; - - class UserFactory extends Factory - { - /** - * Configure the model factory. - */ - public function configure(): static - { - return $this->afterMaking(function (User $user) { - // ... - })->afterCreating(function (User $user) { - // ... - }); - } +```php +namespace Database\Factories; - // ... - } - -You may also register factory callbacks within state methods to perform additional tasks that are specific to a given state: - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Factory; +use App\Models\User; +use Illuminate\Database\Eloquent\Factories\Factory; +class UserFactory extends Factory +{ /** - * Indicate that the user is suspended. + * Configure the model factory. */ - public function suspended(): Factory + public function configure(): static { - return $this->state(function (array $attributes) { - return [ - 'account_status' => 'suspended', - ]; - })->afterMaking(function (User $user) { + return $this->afterMaking(function (User $user) { // ... })->afterCreating(function (User $user) { // ... }); } + // ... +} +``` + +You may also register factory callbacks within state methods to perform additional tasks that are specific to a given state: + +```php +use App\Models\User; +use Illuminate\Database\Eloquent\Factories\Factory; + +/** + * Indicate that the user is suspended. + */ +public function suspended(): Factory +{ + return $this->state(function (array $attributes) { + return [ + 'account_status' => 'suspended', + ]; + })->afterMaking(function (User $user) { + // ... + })->afterCreating(function (User $user) { + // ... + }); +} +``` + ## Creating Models Using Factories @@ -206,35 +220,45 @@ You may also register factory callbacks within state methods to perform addition Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. Let's take a look at a few examples of creating models. First, we'll use the `make` method to create models without persisting them to the database: - use App\Models\User; +```php +use App\Models\User; - $user = User::factory()->make(); +$user = User::factory()->make(); +``` You may create a collection of many models using the `count` method: - $users = User::factory()->count(3)->make(); +```php +$users = User::factory()->count(3)->make(); +``` #### Applying States You may also apply any of your [states](#factory-states) to the models. If you would like to apply multiple state transformations to the models, you may simply call the state transformation methods directly: - $users = User::factory()->count(5)->suspended()->make(); +```php +$users = User::factory()->count(5)->suspended()->make(); +``` #### Overriding Attributes If you would like to override some of the default values of your models, you may pass an array of values to the `make` method. Only the specified attributes will be replaced while the rest of the attributes remain set to their default values as specified by the factory: - $user = User::factory()->make([ - 'name' => 'Abigail Otwell', - ]); +```php +$user = User::factory()->make([ + 'name' => 'Abigail Otwell', +]); +``` Alternatively, the `state` method may be called directly on the factory instance to perform an inline state transformation: - $user = User::factory()->state([ - 'name' => 'Abigail Otwell', - ])->make(); +```php +$user = User::factory()->state([ + 'name' => 'Abigail Otwell', +])->make(); +``` > [!NOTE] > [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. @@ -244,65 +268,77 @@ Alternatively, the `state` method may be called directly on the factory instance The `create` method instantiates model instances and persists them to the database using Eloquent's `save` method: - use App\Models\User; +```php +use App\Models\User; - // Create a single App\Models\User instance... - $user = User::factory()->create(); +// Create a single App\Models\User instance... +$user = User::factory()->create(); - // Create three App\Models\User instances... - $users = User::factory()->count(3)->create(); +// Create three App\Models\User instances... +$users = User::factory()->count(3)->create(); +``` You may override the factory's default model attributes by passing an array of attributes to the `create` method: - $user = User::factory()->create([ - 'name' => 'Abigail', - ]); +```php +$user = User::factory()->create([ + 'name' => 'Abigail', +]); +``` ### Sequences Sometimes you may wish to alternate the value of a given model attribute for each created model. You may accomplish this by defining a state transformation as a sequence. For example, you may wish to alternate the value of an `admin` column between `Y` and `N` for each created user: - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Sequence; - - $users = User::factory() - ->count(10) - ->state(new Sequence( - ['admin' => 'Y'], - ['admin' => 'N'], - )) - ->create(); +```php +use App\Models\User; +use Illuminate\Database\Eloquent\Factories\Sequence; + +$users = User::factory() + ->count(10) + ->state(new Sequence( + ['admin' => 'Y'], + ['admin' => 'N'], + )) + ->create(); +``` In this example, five users will be created with an `admin` value of `Y` and five users will be created with an `admin` value of `N`. If necessary, you may include a closure as a sequence value. The closure will be invoked each time the sequence needs a new value: - use Illuminate\Database\Eloquent\Factories\Sequence; +```php +use Illuminate\Database\Eloquent\Factories\Sequence; - $users = User::factory() - ->count(10) - ->state(new Sequence( - fn (Sequence $sequence) => ['role' => UserRoles::all()->random()], - )) - ->create(); +$users = User::factory() + ->count(10) + ->state(new Sequence( + fn (Sequence $sequence) => ['role' => UserRoles::all()->random()], + )) + ->create(); +``` Within a sequence closure, you may access the `$index` or `$count` properties on the sequence instance that is injected into the closure. The `$index` property contains the number of iterations through the sequence that have occurred thus far, while the `$count` property contains the total number of times the sequence will be invoked: - $users = User::factory() - ->count(10) - ->sequence(fn (Sequence $sequence) => ['name' => 'Name '.$sequence->index]) - ->create(); +```php +$users = User::factory() + ->count(10) + ->sequence(fn (Sequence $sequence) => ['name' => 'Name '.$sequence->index]) + ->create(); +``` For convenience, sequences may also be applied using the `sequence` method, which simply invokes the `state` method internally. The `sequence` method accepts a closure or arrays of sequenced attributes: - $users = User::factory() - ->count(2) - ->sequence( - ['name' => 'First User'], - ['name' => 'Second User'], - ) - ->create(); +```php +$users = User::factory() + ->count(2) + ->sequence( + ['name' => 'First User'], + ['name' => 'Second User'], + ) + ->create(); +``` ## Factory Relationships @@ -312,230 +348,270 @@ For convenience, sequences may also be applied using the `sequence` method, whic Next, let's explore building Eloquent model relationships using Laravel's fluent factory methods. First, let's assume our application has an `App\Models\User` model and an `App\Models\Post` model. Also, let's assume that the `User` model defines a `hasMany` relationship with `Post`. We can create a user that has three posts using the `has` method provided by the Laravel's factories. The `has` method accepts a factory instance: - use App\Models\Post; - use App\Models\User; +```php +use App\Models\Post; +use App\Models\User; - $user = User::factory() - ->has(Post::factory()->count(3)) - ->create(); +$user = User::factory() + ->has(Post::factory()->count(3)) + ->create(); +``` By convention, when passing a `Post` model to the `has` method, Laravel will assume that the `User` model must have a `posts` method that defines the relationship. If necessary, you may explicitly specify the name of the relationship that you would like to manipulate: - $user = User::factory() - ->has(Post::factory()->count(3), 'posts') - ->create(); +```php +$user = User::factory() + ->has(Post::factory()->count(3), 'posts') + ->create(); +``` Of course, you may perform state manipulations on the related models. In addition, you may pass a closure based state transformation if your state change requires access to the parent model: - $user = User::factory() - ->has( - Post::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['user_type' => $user->type]; - }) - ) - ->create(); +```php +$user = User::factory() + ->has( + Post::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['user_type' => $user->type]; + }) + ) + ->create(); +``` #### Using Magic Methods For convenience, you may use Laravel's magic factory relationship methods to build relationships. For example, the following example will use convention to determine that the related models should be created via a `posts` relationship method on the `User` model: - $user = User::factory() - ->hasPosts(3) - ->create(); +```php +$user = User::factory() + ->hasPosts(3) + ->create(); +``` When using magic methods to create factory relationships, you may pass an array of attributes to override on the related models: - $user = User::factory() - ->hasPosts(3, [ - 'published' => false, - ]) - ->create(); +```php +$user = User::factory() + ->hasPosts(3, [ + 'published' => false, + ]) + ->create(); +``` You may provide a closure based state transformation if your state change requires access to the parent model: - $user = User::factory() - ->hasPosts(3, function (array $attributes, User $user) { - return ['user_type' => $user->type]; - }) - ->create(); +```php +$user = User::factory() + ->hasPosts(3, function (array $attributes, User $user) { + return ['user_type' => $user->type]; + }) + ->create(); +``` ### Belongs To Relationships Now that we have explored how to build "has many" relationships using factories, let's explore the inverse of the relationship. The `for` method may be used to define the parent model that factory created models belong to. For example, we can create three `App\Models\Post` model instances that belong to a single user: - use App\Models\Post; - use App\Models\User; +```php +use App\Models\Post; +use App\Models\User; - $posts = Post::factory() - ->count(3) - ->for(User::factory()->state([ - 'name' => 'Jessica Archer', - ])) - ->create(); +$posts = Post::factory() + ->count(3) + ->for(User::factory()->state([ + 'name' => 'Jessica Archer', + ])) + ->create(); +``` If you already have a parent model instance that should be associated with the models you are creating, you may pass the model instance to the `for` method: - $user = User::factory()->create(); +```php +$user = User::factory()->create(); - $posts = Post::factory() - ->count(3) - ->for($user) - ->create(); +$posts = Post::factory() + ->count(3) + ->for($user) + ->create(); +``` #### Using Magic Methods For convenience, you may use Laravel's magic factory relationship methods to define "belongs to" relationships. For example, the following example will use convention to determine that the three posts should belong to the `user` relationship on the `Post` model: - $posts = Post::factory() - ->count(3) - ->forUser([ - 'name' => 'Jessica Archer', - ]) - ->create(); +```php +$posts = Post::factory() + ->count(3) + ->forUser([ + 'name' => 'Jessica Archer', + ]) + ->create(); +``` ### Many to Many Relationships Like [has many relationships](#has-many-relationships), "many to many" relationships may be created using the `has` method: - use App\Models\Role; - use App\Models\User; +```php +use App\Models\Role; +use App\Models\User; - $user = User::factory() - ->has(Role::factory()->count(3)) - ->create(); +$user = User::factory() + ->has(Role::factory()->count(3)) + ->create(); +``` #### Pivot Table Attributes If you need to define attributes that should be set on the pivot / intermediate table linking the models, you may use the `hasAttached` method. This method accepts an array of pivot table attribute names and values as its second argument: - use App\Models\Role; - use App\Models\User; +```php +use App\Models\Role; +use App\Models\User; - $user = User::factory() - ->hasAttached( - Role::factory()->count(3), - ['active' => true] - ) - ->create(); +$user = User::factory() + ->hasAttached( + Role::factory()->count(3), + ['active' => true] + ) + ->create(); +``` You may provide a closure based state transformation if your state change requires access to the related model: - $user = User::factory() - ->hasAttached( - Role::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['name' => $user->name.' Role']; - }), - ['active' => true] - ) - ->create(); +```php +$user = User::factory() + ->hasAttached( + Role::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['name' => $user->name.' Role']; + }), + ['active' => true] + ) + ->create(); +``` If you already have model instances that you would like to be attached to the models you are creating, you may pass the model instances to the `hasAttached` method. In this example, the same three roles will be attached to all three users: - $roles = Role::factory()->count(3)->create(); +```php +$roles = Role::factory()->count(3)->create(); - $user = User::factory() - ->count(3) - ->hasAttached($roles, ['active' => true]) - ->create(); +$user = User::factory() + ->count(3) + ->hasAttached($roles, ['active' => true]) + ->create(); +``` #### Using Magic Methods For convenience, you may use Laravel's magic factory relationship methods to define many to many relationships. For example, the following example will use convention to determine that the related models should be created via a `roles` relationship method on the `User` model: - $user = User::factory() - ->hasRoles(1, [ - 'name' => 'Editor' - ]) - ->create(); +```php +$user = User::factory() + ->hasRoles(1, [ + 'name' => 'Editor' + ]) + ->create(); +``` ### Polymorphic Relationships [Polymorphic relationships](/docs/{{version}}/eloquent-relationships#polymorphic-relationships) may also be created using factories. Polymorphic "morph many" relationships are created in the same way as typical "has many" relationships. For example, if an `App\Models\Post` model has a `morphMany` relationship with an `App\Models\Comment` model: - use App\Models\Post; +```php +use App\Models\Post; - $post = Post::factory()->hasComments(3)->create(); +$post = Post::factory()->hasComments(3)->create(); +``` #### Morph To Relationships Magic methods may not be used to create `morphTo` relationships. Instead, the `for` method must be used directly and the name of the relationship must be explicitly provided. For example, imagine that the `Comment` model has a `commentable` method that defines a `morphTo` relationship. In this situation, we may create three comments that belong to a single post by using the `for` method directly: - $comments = Comment::factory()->count(3)->for( - Post::factory(), 'commentable' - )->create(); +```php +$comments = Comment::factory()->count(3)->for( + Post::factory(), 'commentable' +)->create(); +``` #### Polymorphic Many to Many Relationships Polymorphic "many to many" (`morphToMany` / `morphedByMany`) relationships may be created just like non-polymorphic "many to many" relationships: - use App\Models\Tag; - use App\Models\Video; +```php +use App\Models\Tag; +use App\Models\Video; - $videos = Video::factory() - ->hasAttached( - Tag::factory()->count(3), - ['public' => true] - ) - ->create(); +$videos = Video::factory() + ->hasAttached( + Tag::factory()->count(3), + ['public' => true] + ) + ->create(); +``` Of course, the magic `has` method may also be used to create polymorphic "many to many" relationships: - $videos = Video::factory() - ->hasTags(3, ['public' => true]) - ->create(); +```php +$videos = Video::factory() + ->hasTags(3, ['public' => true]) + ->create(); +``` ### Defining Relationships Within Factories To define a relationship within your model factory, you will typically assign a new factory instance to the foreign key of the relationship. This is normally done for the "inverse" relationships such as `belongsTo` and `morphTo` relationships. For example, if you would like to create a new user when creating a post, you may do the following: - use App\Models\User; - - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - return [ - 'user_id' => User::factory(), - 'title' => fake()->title(), - 'content' => fake()->paragraph(), - ]; - } +```php +use App\Models\User; + +/** + * Define the model's default state. + * + * @return array + */ +public function definition(): array +{ + return [ + 'user_id' => User::factory(), + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; +} +``` If the relationship's columns depend on the factory that defines it you may assign a closure to an attribute. The closure will receive the factory's evaluated attribute array: - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - return [ - 'user_id' => User::factory(), - 'user_type' => function (array $attributes) { - return User::find($attributes['user_id'])->type; - }, - 'title' => fake()->title(), - 'content' => fake()->paragraph(), - ]; - } +```php +/** + * Define the model's default state. + * + * @return array + */ +public function definition(): array +{ + return [ + 'user_id' => User::factory(), + 'user_type' => function (array $attributes) { + return User::find($attributes['user_id'])->type; + }, + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; +} +``` ### Recycling an Existing Model for Relationships @@ -544,14 +620,18 @@ If you have models that share a common relationship with another model, you may For example, imagine you have `Airline`, `Flight`, and `Ticket` models, where the ticket belongs to an airline and a flight, and the flight also belongs to an airline. When creating tickets, you will probably want the same airline for both the ticket and the flight, so you may pass an airline instance to the `recycle` method: - Ticket::factory() - ->recycle(Airline::factory()->create()) - ->create(); +```php +Ticket::factory() + ->recycle(Airline::factory()->create()) + ->create(); +``` You may find the `recycle` method particularly useful if you have models belonging to a common user or team. The `recycle` method also accepts a collection of existing models. When a collection is provided to the `recycle` method, a random model from the collection will be chosen when the factory needs a model of that type: - Ticket::factory() - ->recycle($airlines) - ->create(); +```php +Ticket::factory() + ->recycle($airlines) + ->create(); +``` diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 60c6aefe87b..c7909cc12f5 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -32,35 +32,39 @@ An accessor transforms an Eloquent attribute value when it is accessed. To defin In this example, we'll define an accessor for the `first_name` attribute. The accessor will automatically be called by Eloquent when attempting to retrieve the value of the `first_name` attribute. All attribute accessor / mutator methods must declare a return type-hint of `Illuminate\Database\Eloquent\Casts\Attribute`: - ucfirst($value), - ); - } + return Attribute::make( + get: fn (string $value) => ucfirst($value), + ); } +} +``` All accessor methods return an `Attribute` instance which defines how the attribute will be accessed and, optionally, mutated. In this example, we are only defining how the attribute will be accessed. To do so, we supply the `get` argument to the `Attribute` class constructor. As you can see, the original value of the column is passed to the accessor, allowing you to manipulate and return the value. To access the value of the accessor, you may simply access the `first_name` attribute on a model instance: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - $firstName = $user->first_name; +$firstName = $user->first_name; +``` > [!NOTE] > If you would like these computed values to be added to the array / JSON representations of your model, [you will need to append them](/docs/{{version}}/eloquent-serialization#appending-values-to-json). @@ -93,14 +97,16 @@ protected function address(): Attribute When returning value objects from accessors, any changes made to the value object will automatically be synced back to the model before the model is saved. This is possible because Eloquent retains instances returned by accessors so it can return the same instance each time the accessor is invoked: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - $user->address->lineOne = 'Updated Address Line 1 Value'; - $user->address->lineTwo = 'Updated Address Line 2 Value'; +$user->address->lineOne = 'Updated Address Line 1 Value'; +$user->address->lineTwo = 'Updated Address Line 2 Value'; - $user->save(); +$user->save(); +``` However, you may sometimes wish to enable caching for primitive values like strings and booleans, particularly if they are computationally intensive. To accomplish this, you may invoke the `shouldCache` method when defining your accessor: @@ -135,34 +141,38 @@ protected function address(): Attribute A mutator transforms an Eloquent attribute value when it is set. To define a mutator, you may provide the `set` argument when defining your attribute. Let's define a mutator for the `first_name` attribute. This mutator will be automatically called when we attempt to set the value of the `first_name` attribute on the model: - ucfirst($value), - set: fn (string $value) => strtolower($value), - ); - } + return Attribute::make( + get: fn (string $value) => ucfirst($value), + set: fn (string $value) => strtolower($value), + ); } +} +``` The mutator closure will receive the value that is being set on the attribute, allowing you to manipulate the value and return the manipulated value. To use our mutator, we only need to set the `first_name` attribute on an Eloquent model: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - $user->first_name = 'Sally'; +$user->first_name = 'Sally'; +``` In this example, the `set` callback will be called with the value `Sally`. The mutator will then apply the `strtolower` function to the name and set its resulting value in the model's internal `$attributes` array. @@ -228,41 +238,47 @@ The `casts` method should return an array where the key is the name of the attri To demonstrate attribute casting, let's cast the `is_admin` attribute, which is stored in our database as an integer (`0` or `1`) to a boolean value: - + */ + protected function casts(): array { - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'is_admin' => 'boolean', - ]; - } + return [ + 'is_admin' => 'boolean', + ]; } +} +``` After defining the cast, the `is_admin` attribute will always be cast to a boolean when you access it, even if the underlying value is stored in the database as an integer: - $user = App\Models\User::find(1); +```php +$user = App\Models\User::find(1); - if ($user->is_admin) { - // ... - } +if ($user->is_admin) { + // ... +} +``` If you need to add a new, temporary cast at runtime, you may use the `mergeCasts` method. These cast definitions will be added to any of the casts already defined on the model: - $user->mergeCasts([ - 'is_admin' => 'integer', - 'options' => 'object', - ]); +```php +$user->mergeCasts([ + 'is_admin' => 'integer', + 'options' => 'object', +]); +``` > [!WARNING] > Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship or assign a cast to the model's primary key. @@ -272,131 +288,147 @@ If you need to add a new, temporary cast at runtime, you may use the `mergeCasts You may use the `Illuminate\Database\Eloquent\Casts\AsStringable` cast class to cast a model attribute to a [fluent `Illuminate\Support\Stringable` object](/docs/{{version}}/strings#fluent-strings-method-list): - + */ + protected function casts(): array { - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'directory' => AsStringable::class, - ]; - } + return [ + 'directory' => AsStringable::class, + ]; } +} +``` ### Array and JSON Casting The `array` cast is particularly useful when working with columns that are stored as serialized JSON. For example, if your database has a `JSON` or `TEXT` field type that contains serialized JSON, adding the `array` cast to that attribute will automatically deserialize the attribute to a PHP array when you access it on your Eloquent model: - + */ + protected function casts(): array { - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'options' => 'array', - ]; - } + return [ + 'options' => 'array', + ]; } +} +``` Once the cast is defined, you may access the `options` attribute and it will automatically be deserialized from JSON into a PHP array. When you set the value of the `options` attribute, the given array will automatically be serialized back into JSON for storage: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - $options = $user->options; +$options = $user->options; - $options['key'] = 'value'; +$options['key'] = 'value'; - $user->options = $options; +$user->options = $options; - $user->save(); +$user->save(); +``` To update a single field of a JSON attribute with a more terse syntax, you may [make the attribute mass assignable](/docs/{{version}}/eloquent#mass-assignment-json-columns) and use the `->` operator when calling the `update` method: - $user = User::find(1); +```php +$user = User::find(1); - $user->update(['options->key' => 'value']); +$user->update(['options->key' => 'value']); +``` #### Array Object and Collection Casting Although the standard `array` cast is sufficient for many applications, it does have some disadvantages. Since the `array` cast returns a primitive type, it is not possible to mutate an offset of the array directly. For example, the following code will trigger a PHP error: - $user = User::find(1); +```php +$user = User::find(1); - $user->options['key'] = $value; +$user->options['key'] = $value; +``` To solve this, Laravel offers an `AsArrayObject` cast that casts your JSON attribute to an [ArrayObject](https://www.php.net/manual/en/class.arrayobject.php) class. This feature is implemented using Laravel's [custom cast](#custom-casts) implementation, which allows Laravel to intelligently cache and transform the mutated object such that individual offsets may be modified without triggering a PHP error. To use the `AsArrayObject` cast, simply assign it to an attribute: - use Illuminate\Database\Eloquent\Casts\AsArrayObject; +```php +use Illuminate\Database\Eloquent\Casts\AsArrayObject; - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'options' => AsArrayObject::class, - ]; - } +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'options' => AsArrayObject::class, + ]; +} +``` Similarly, Laravel offers an `AsCollection` cast that casts your JSON attribute to a Laravel [Collection](/docs/{{version}}/collections) instance: - use Illuminate\Database\Eloquent\Casts\AsCollection; +```php +use Illuminate\Database\Eloquent\Casts\AsCollection; - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'options' => AsCollection::class, - ]; - } +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'options' => AsCollection::class, + ]; +} +``` If you would like the `AsCollection` cast to instantiate a custom collection class instead of Laravel's base collection class, you may provide the collection class name as a cast argument: - use App\Collections\OptionCollection; - use Illuminate\Database\Eloquent\Casts\AsCollection; +```php +use App\Collections\OptionCollection; +use Illuminate\Database\Eloquent\Casts\AsCollection; - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'options' => AsCollection::using(OptionCollection::class), - ]; - } +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'options' => AsCollection::using(OptionCollection::class), + ]; +} +``` ### Date Casting @@ -405,38 +437,44 @@ By default, Eloquent will cast the `created_at` and `updated_at` columns to inst When defining a `date` or `datetime` cast, you may also specify the date's format. This format will be used when the [model is serialized to an array or JSON](/docs/{{version}}/eloquent-serialization): - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'created_at' => 'datetime:Y-m-d', - ]; - } +```php +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'created_at' => 'datetime:Y-m-d', + ]; +} +``` When a column is cast as a date, you may set the corresponding model attribute value to a UNIX timestamp, date string (`Y-m-d`), date-time string, or a `DateTime` / `Carbon` instance. The date's value will be correctly converted and stored in your database. You may customize the default serialization format for all of your model's dates by defining a `serializeDate` method on your model. This method does not affect how your dates are formatted for storage in the database: - /** - * Prepare a date for array / JSON serialization. - */ - protected function serializeDate(DateTimeInterface $date): string - { - return $date->format('Y-m-d'); - } +```php +/** + * Prepare a date for array / JSON serialization. + */ +protected function serializeDate(DateTimeInterface $date): string +{ + return $date->format('Y-m-d'); +} +``` To specify the format that should be used when actually storing a model's dates within your database, you should define a `$dateFormat` property on your model: - /** - * The storage format of the model's date columns. - * - * @var string - */ - protected $dateFormat = 'U'; +```php +/** + * The storage format of the model's date columns. + * + * @var string + */ +protected $dateFormat = 'U'; +``` #### Date Casting, Serialization, and Timezones @@ -450,47 +488,53 @@ If a custom format is applied to the `date` or `datetime` cast, such as `datetim Eloquent also allows you to cast your attribute values to PHP [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `casts` method: - use App\Enums\ServerStatus; +```php +use App\Enums\ServerStatus; - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'status' => ServerStatus::class, - ]; - } +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'status' => ServerStatus::class, + ]; +} +``` Once you have defined the cast on your model, the specified attribute will be automatically cast to and from an enum when you interact with the attribute: - if ($server->status == ServerStatus::Provisioned) { - $server->status = ServerStatus::Ready; +```php +if ($server->status == ServerStatus::Provisioned) { + $server->status = ServerStatus::Ready; - $server->save(); - } + $server->save(); +} +``` #### Casting Arrays of Enums Sometimes you may need your model to store an array of enum values within a single column. To accomplish this, you may utilize the `AsEnumArrayObject` or `AsEnumCollection` casts provided by Laravel: - use App\Enums\ServerStatus; - use Illuminate\Database\Eloquent\Casts\AsEnumCollection; +```php +use App\Enums\ServerStatus; +use Illuminate\Database\Eloquent\Casts\AsEnumCollection; - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'statuses' => AsEnumCollection::of(ServerStatus::class), - ]; - } +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'statuses' => AsEnumCollection::of(ServerStatus::class), + ]; +} +``` ### Encrypted Casting @@ -509,24 +553,28 @@ As you may know, Laravel encrypts strings using the `key` configuration value sp Sometimes you may need to apply casts while executing a query, such as when selecting a raw value from a table. For example, consider the following query: - use App\Models\Post; - use App\Models\User; - - $users = User::select([ - 'users.*', - 'last_posted_at' => Post::selectRaw('MAX(created_at)') - ->whereColumn('user_id', 'users.id') - ])->get(); +```php +use App\Models\Post; +use App\Models\User; + +$users = User::select([ + 'users.*', + 'last_posted_at' => Post::selectRaw('MAX(created_at)') + ->whereColumn('user_id', 'users.id') +])->get(); +``` The `last_posted_at` attribute on the results of this query will be a simple string. It would be wonderful if we could apply a `datetime` cast to this attribute when executing the query. Thankfully, we may accomplish this using the `withCasts` method: - $users = User::select([ - 'users.*', - 'last_posted_at' => Post::selectRaw('MAX(created_at)') - ->whereColumn('user_id', 'users.id') - ])->withCasts([ - 'last_posted_at' => 'datetime' - ])->get(); +```php +$users = User::select([ + 'users.*', + 'last_posted_at' => Post::selectRaw('MAX(created_at)') + ->whereColumn('user_id', 'users.id') +])->withCasts([ + 'last_posted_at' => 'datetime' +])->get(); +``` ## Custom Casts @@ -539,60 +587,64 @@ 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); - } + 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); - } + /** + * 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); } +} +``` Once you have defined a custom cast type, you may attach it to a model attribute using its class name: - + */ + 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 @@ -601,58 +653,62 @@ You are not limited to casting values to primitive types. You may also cast valu 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 { - /** - * 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'] - ); - } - - /** - * 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.'); - } + 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.'); } + + return [ + 'address_line_one' => $value->lineOne, + 'address_line_two' => $value->lineTwo, + ]; } +} +``` 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: - use App\Models\User; +```php +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. @@ -680,15 +736,17 @@ When an Eloquent model is converted to an array or JSON using the `toArray` and Therefore, you may specify that your custom cast class will be responsible for serializing the value object. To do so, your custom cast class should implement the `Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes` interface. This interface states that your class should contain a `serialize` method which should return the serialized form of your value object: - /** - * Get the serialized representation of the value. - * - * @param array $attributes - */ - public function serialize(Model $model, string $key, mixed $value, array $attributes): string - { - return (string) $value; - } +```php +/** + * Get the serialized representation of the value. + * + * @param array $attributes + */ +public function serialize(Model $model, string $key, mixed $value, array $attributes): string +{ + return (string) $value; +} +``` ### Inbound Casting @@ -703,139 +761,151 @@ 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: - $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 When attaching a custom cast to a model, cast parameters may be specified by separating them from the class name using a `:` character and comma-delimiting multiple parameters. The parameters will be passed to the constructor of the cast class: - /** - * Get the attributes that should be cast. - * - * @return array - */ - protected function casts(): array - { - return [ - 'secret' => Hash::class.':sha256', - ]; - } +```php +/** + * Get the attributes that should be cast. + * + * @return array + */ +protected function casts(): array +{ + return [ + 'secret' => Hash::class.':sha256', + ]; +} +``` ### Castables You may want to allow your application's value objects to define their own custom cast classes. Instead of attaching the custom cast class to your model, you may alternatively attach a value object class that implements the `Illuminate\Contracts\Database\Eloquent\Castable` interface: - use App\ValueObjects\Address; +```php +use App\ValueObjects\Address; - protected function casts(): array - { - return [ - 'address' => Address::class, - ]; - } +protected function casts(): array +{ + return [ + 'address' => Address::class, + ]; +} +``` Objects that implement the `Castable` interface must define a `castUsing` method that returns the class name of the custom caster class that is responsible for casting to and from the `Castable` class: - $arguments + */ + public static function castUsing(array $arguments): string { - /** - * Get the name of the caster class to use when casting from / to this cast target. - * - * @param array $arguments - */ - public static function castUsing(array $arguments): string - { - return AddressCast::class; - } + return AddressCast::class; } +} +``` When using `Castable` classes, you may still provide arguments in the `casts` method definition. The arguments will be passed to the `castUsing` method: - use App\ValueObjects\Address; +```php +use App\ValueObjects\Address; - protected function casts(): array - { - return [ - 'address' => Address::class.':argument', - ]; - } +protected function casts(): array +{ + return [ + 'address' => Address::class.':argument', + ]; +} +``` #### Castables & Anonymous Cast Classes By combining "castables" with PHP's [anonymous classes](https://www.php.net/manual/en/language.oop5.anonymous.php), you may define a value object and its casting logic as a single castable object. To accomplish this, return an anonymous class from your value object's `castUsing` method. The anonymous class should implement the `CastsAttributes` interface: - $arguments + */ + public static function castUsing(array $arguments): CastsAttributes { - // ... - - /** - * Get the caster class to use when casting from / to this cast target. - * - * @param array $arguments - */ - public static function castUsing(array $arguments): CastsAttributes + return new class implements CastsAttributes { - return new class implements CastsAttributes + public function get(Model $model, string $key, mixed $value, array $attributes): Address { - public function get(Model $model, string $key, mixed $value, array $attributes): Address - { - return new Address( - $attributes['address_line_one'], - $attributes['address_line_two'] - ); - } - - public function set(Model $model, string $key, mixed $value, array $attributes): array - { - return [ - 'address_line_one' => $value->lineOne, - 'address_line_two' => $value->lineTwo, - ]; - } - }; - } + return new Address( + $attributes['address_line_one'], + $attributes['address_line_two'] + ); + } + + public function set(Model $model, string $key, mixed $value, array $attributes): array + { + return [ + 'address_line_one' => $value->lineOne, + 'address_line_two' => $value->lineTwo, + ]; + } + }; } +} +``` diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 5011298daf0..d2020525d2a 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -64,7 +64,9 @@ Database tables are often related to one another. For example, a blog post may h Eloquent relationships are defined as methods on your Eloquent model classes. Since relationships also serve as powerful [query builders](/docs/{{version}}/queries), defining relationships as methods provides powerful method chaining and querying capabilities. For example, we may chain additional query constraints on this `posts` relationship: - $user->posts()->where('active', 1)->get(); +```php +$user->posts()->where('active', 1)->get(); +``` But, before diving too deep into using relationships, let's learn how to define each type of relationship supported by Eloquent. @@ -73,127 +75,149 @@ But, before diving too deep into using relationships, let's learn how to define A one-to-one relationship is a very basic type of database relationship. For example, a `User` model might be associated with one `Phone` model. To define this relationship, we will place a `phone` method on the `User` model. The `phone` method should call the `hasOne` method and return its result. The `hasOne` method is available to your model via the model's `Illuminate\Database\Eloquent\Model` base class: - hasOne(Phone::class); - } + return $this->hasOne(Phone::class); } +} +``` The first argument passed to the `hasOne` method is the name of the related model class. Once the relationship is defined, we may retrieve the related record using Eloquent's dynamic properties. Dynamic properties allow you to access relationship methods as if they were properties defined on the model: - $phone = User::find(1)->phone; +```php +$phone = User::find(1)->phone; +``` Eloquent determines the foreign key of the relationship based on the parent model name. In this case, the `Phone` model is automatically assumed to have a `user_id` foreign key. If you wish to override this convention, you may pass a second argument to the `hasOne` method: - return $this->hasOne(Phone::class, 'foreign_key'); +```php +return $this->hasOne(Phone::class, 'foreign_key'); +``` Additionally, Eloquent assumes that the foreign key should have a value matching the primary key column of the parent. In other words, Eloquent will look for the value of the user's `id` column in the `user_id` column of the `Phone` record. If you would like the relationship to use a primary key value other than `id` or your model's `$primaryKey` property, you may pass a third argument to the `hasOne` method: - return $this->hasOne(Phone::class, 'foreign_key', 'local_key'); +```php +return $this->hasOne(Phone::class, 'foreign_key', 'local_key'); +``` #### Defining the Inverse of the Relationship So, we can access the `Phone` model from our `User` model. Next, let's define a relationship on the `Phone` model that will let us access the user that owns the phone. We can define the inverse of a `hasOne` relationship using the `belongsTo` method: - belongsTo(User::class); - } + return $this->belongsTo(User::class); } +} +``` When invoking the `user` method, Eloquent will attempt to find a `User` model that has an `id` which matches the `user_id` column on the `Phone` model. Eloquent determines the foreign key name by examining the name of the relationship method and suffixing the method name with `_id`. So, in this case, Eloquent assumes that the `Phone` model has a `user_id` column. However, if the foreign key on the `Phone` model is not `user_id`, you may pass a custom key name as the second argument to the `belongsTo` method: - /** - * Get the user that owns the phone. - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class, 'foreign_key'); - } +```php +/** + * Get the user that owns the phone. + */ +public function user(): BelongsTo +{ + return $this->belongsTo(User::class, 'foreign_key'); +} +``` If the parent model does not use `id` as its primary key, or you wish to find the associated model using a different column, you may pass a third argument to the `belongsTo` method specifying the parent table's custom key: - /** - * Get the user that owns the phone. - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class, 'foreign_key', 'owner_key'); - } +```php +/** + * Get the user that owns the phone. + */ +public function user(): BelongsTo +{ + return $this->belongsTo(User::class, 'foreign_key', 'owner_key'); +} +``` ### One to Many / Has Many A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models. For example, a blog post may have an infinite number of comments. Like all other Eloquent relationships, one-to-many relationships are defined by defining a method on your Eloquent model: - hasMany(Comment::class); - } + return $this->hasMany(Comment::class); } +} +``` Remember, Eloquent will automatically determine the proper foreign key column for the `Comment` model. By convention, Eloquent will take the "snake case" name of the parent model and suffix it with `_id`. So, in this example, Eloquent will assume the foreign key column on the `Comment` model is `post_id`. Once the relationship method has been defined, we can access the [collection](/docs/{{version}}/eloquent-collections) of related comments by accessing the `comments` property. Remember, since Eloquent provides "dynamic relationship properties", we can access relationship methods as if they were defined as properties on the model: - use App\Models\Post; +```php +use App\Models\Post; - $comments = Post::find(1)->comments; +$comments = Post::find(1)->comments; - foreach ($comments as $comment) { - // ... - } +foreach ($comments as $comment) { + // ... +} +``` Since all relationships also serve as query builders, you may add further constraints to the relationship query by calling the `comments` method and continuing to chain conditions onto the query: - $comment = Post::find(1)->comments() - ->where('title', 'foo') - ->first(); +```php +$comment = Post::find(1)->comments() + ->where('title', 'foo') + ->first(); +``` Like the `hasOne` method, you may also override the foreign and local keys by passing additional arguments to the `hasMany` method: - return $this->hasMany(Comment::class, 'foreign_key'); +```php +return $this->hasMany(Comment::class, 'foreign_key'); - return $this->hasMany(Comment::class, 'foreign_key', 'local_key'); +return $this->hasMany(Comment::class, 'foreign_key', 'local_key'); +``` #### Automatically Hydrating Parent Models on Children @@ -214,23 +238,25 @@ In the example above, an "N + 1" query problem has been introduced because, even If you would like Eloquent to automatically hydrate parent models onto their children, you may invoke the `chaperone` method when defining a `hasMany` relationship: - hasMany(Comment::class)->chaperone(); - } + return $this->hasMany(Comment::class)->chaperone(); } +} +``` Or, if you would like to opt-in to automatic parent hydration at run time, you may invoke the `chaperone` model when eager loading the relationship: @@ -247,31 +273,35 @@ $posts = Post::with([ Now that we can access all of a post's comments, let's define a relationship to allow a comment to access its parent post. To define the inverse of a `hasMany` relationship, define a relationship method on the child model which calls the `belongsTo` method: - belongsTo(Post::class); - } + return $this->belongsTo(Post::class); } +} +``` Once the relationship has been defined, we can retrieve a comment's parent post by accessing the `post` "dynamic relationship property": - use App\Models\Comment; +```php +use App\Models\Comment; - $comment = Comment::find(1); +$comment = Comment::find(1); - return $comment->post->title; +return $comment->post->title; +``` In the example above, Eloquent will attempt to find a `Post` model that has an `id` which matches the `post_id` column on the `Comment` model. @@ -279,81 +309,97 @@ Eloquent determines the default foreign key name by examining the name of the re However, if the foreign key for your relationship does not follow these conventions, you may pass a custom foreign key name as the second argument to the `belongsTo` method: - /** - * Get the post that owns the comment. - */ - public function post(): BelongsTo - { - return $this->belongsTo(Post::class, 'foreign_key'); - } +```php +/** + * Get the post that owns the comment. + */ +public function post(): BelongsTo +{ + return $this->belongsTo(Post::class, 'foreign_key'); +} +``` If your parent model does not use `id` as its primary key, or you wish to find the associated model using a different column, you may pass a third argument to the `belongsTo` method specifying your parent table's custom key: - /** - * Get the post that owns the comment. - */ - public function post(): BelongsTo - { - return $this->belongsTo(Post::class, 'foreign_key', 'owner_key'); - } +```php +/** + * Get the post that owns the comment. + */ +public function post(): BelongsTo +{ + return $this->belongsTo(Post::class, 'foreign_key', 'owner_key'); +} +``` #### Default Models The `belongsTo`, `hasOne`, `hasOneThrough`, and `morphOne` relationships allow you to define a default model that will be returned if the given relationship is `null`. This pattern is often referred to as the [Null Object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern) and can help remove conditional checks in your code. In the following example, the `user` relation will return an empty `App\Models\User` model if no user is attached to the `Post` model: - /** - * Get the author of the post. - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class)->withDefault(); - } +```php +/** + * Get the author of the post. + */ +public function user(): BelongsTo +{ + return $this->belongsTo(User::class)->withDefault(); +} +``` To populate the default model with attributes, you may pass an array or closure to the `withDefault` method: - /** - * Get the author of the post. - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class)->withDefault([ - 'name' => 'Guest Author', - ]); - } +```php +/** + * Get the author of the post. + */ +public function user(): BelongsTo +{ + return $this->belongsTo(User::class)->withDefault([ + 'name' => 'Guest Author', + ]); +} - /** - * Get the author of the post. - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class)->withDefault(function (User $user, Post $post) { - $user->name = 'Guest Author'; - }); - } +/** + * Get the author of the post. + */ +public function user(): BelongsTo +{ + return $this->belongsTo(User::class)->withDefault(function (User $user, Post $post) { + $user->name = 'Guest Author'; + }); +} +``` #### Querying Belongs To Relationships When querying for the children of a "belongs to" relationship, you may manually build the `where` clause to retrieve the corresponding Eloquent models: - use App\Models\Post; +```php +use App\Models\Post; - $posts = Post::where('user_id', $user->id)->get(); +$posts = Post::where('user_id', $user->id)->get(); +``` However, you may find it more convenient to use the `whereBelongsTo` method, which will automatically determine the proper relationship and foreign key for the given model: - $posts = Post::whereBelongsTo($user)->get(); +```php +$posts = Post::whereBelongsTo($user)->get(); +``` You may also provide a [collection](/docs/{{version}}/eloquent-collections) instance to the `whereBelongsTo` method. When doing so, Laravel will retrieve models that belong to any of the parent models within the collection: - $users = User::where('vip', true)->get(); +```php +$users = User::where('vip', true)->get(); - $posts = Post::whereBelongsTo($users)->get(); +$posts = Post::whereBelongsTo($users)->get(); +``` By default, Laravel will determine the relationship associated with the given model based on the class name of the model; however, you may specify the relationship name manually by providing it as the second argument to the `whereBelongsTo` method: - $posts = Post::whereBelongsTo($user, 'author')->get(); +```php +$posts = Post::whereBelongsTo($user, 'author')->get(); +``` ### Has One of Many @@ -451,39 +497,43 @@ The "has-one-through" relationship defines a one-to-one relationship with anothe For example, in a vehicle repair shop application, each `Mechanic` model may be associated with one `Car` model, and each `Car` model may be associated with one `Owner` model. While the mechanic and the owner have no direct relationship within the database, the mechanic can access the owner _through_ the `Car` model. Let's look at the tables necessary to define this relationship: - mechanics - id - integer - name - string +```text +mechanics + id - integer + name - string - cars - id - integer - model - string - mechanic_id - integer +cars + id - integer + model - string + mechanic_id - integer - owners - id - integer - name - string - car_id - integer +owners + id - integer + name - string + car_id - integer +``` Now that we have examined the table structure for the relationship, let's define the relationship on the `Mechanic` model: - hasOneThrough(Owner::class, Car::class); - } + return $this->hasOneThrough(Owner::class, Car::class); } +} +``` The first argument passed to the `hasOneThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. @@ -502,23 +552,25 @@ return $this->throughCars()->hasOwner(); Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasOneThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: - class Mechanic extends Model +```php +class Mechanic extends Model +{ + /** + * Get the car's owner. + */ + public function carOwner(): HasOneThrough { - /** - * Get the car's owner. - */ - public function carOwner(): HasOneThrough - { - return $this->hasOneThrough( - Owner::class, - Car::class, - 'mechanic_id', // Foreign key on the cars table... - 'car_id', // Foreign key on the owners table... - 'id', // Local key on the mechanics table... - 'id' // Local key on the cars table... - ); - } + return $this->hasOneThrough( + Owner::class, + Car::class, + 'mechanic_id', // Foreign key on the cars table... + 'car_id', // Foreign key on the owners table... + 'id', // Local key on the mechanics table... + 'id' // Local key on the cars table... + ); } +} +``` Or, as discussed earlier, if the relevant relationships have already been defined on all of the models involved in the relationship, you may fluently define a "has-one-through" relationship by invoking the `through` method and supplying the names of those relationships. This approach offers the advantage of reusing the key conventions already defined on the existing relationships: @@ -535,39 +587,43 @@ return $this->throughCars()->hasOwner(); The "has-many-through" relationship provides a convenient way to access distant relations via an intermediate relation. For example, let's assume we are building a deployment platform like [Laravel Vapor](https://vapor.laravel.com). A `Project` model might access many `Deployment` models through an intermediate `Environment` model. Using this example, you could easily gather all deployments for a given project. Let's look at the tables required to define this relationship: - projects - id - integer - name - string +```text +projects + id - integer + name - string - environments - id - integer - project_id - integer - name - string +environments + id - integer + project_id - integer + name - string - deployments - id - integer - environment_id - integer - commit_hash - string +deployments + id - integer + environment_id - integer + commit_hash - string +``` Now that we have examined the table structure for the relationship, let's define the relationship on the `Project` model: - hasManyThrough(Deployment::class, Environment::class); - } + return $this->hasManyThrough(Deployment::class, Environment::class); } +} +``` The first argument passed to the `hasManyThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. @@ -588,20 +644,22 @@ Though the `Deployment` model's table does not contain a `project_id` column, th Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasManyThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: - class Project extends Model +```php +class Project extends Model +{ + public function deployments(): HasManyThrough { - public function deployments(): HasManyThrough - { - return $this->hasManyThrough( - Deployment::class, - Environment::class, - 'project_id', // Foreign key on the environments table... - 'environment_id', // Foreign key on the deployments table... - 'id', // Local key on the projects table... - 'id' // Local key on the environments table... - ); - } + return $this->hasManyThrough( + Deployment::class, + Environment::class, + 'project_id', // Foreign key on the environments table... + 'environment_id', // Foreign key on the deployments table... + 'id', // Local key on the projects table... + 'id' // Local key on the environments table... + ); } +} +``` Or, as discussed earlier, if the relevant relationships have already been defined on all of the models involved in the relationship, you may fluently define a "has-many-through" relationship by invoking the `through` method and supplying the names of those relationships. This approach offers the advantage of reusing the key conventions already defined on the existing relationships: @@ -618,47 +676,53 @@ return $this->throughEnvironments()->hasDeployments(); It's common to add additional methods to models that constrain relationships. For example, you might add a `featuredPosts` method to a `User` model which constrains the broader `posts` relationship with an additional `where` constraint: - hasMany(Post::class)->latest(); - } - - /** - * Get the user's featured posts. - */ - public function featuredPosts(): HasMany - { - return $this->posts()->where('featured', true); - } + return $this->hasMany(Post::class)->latest(); } -However, if you attempt to create a model via the `featuredPosts` method, its `featured` attribute would not be set to `true`. If you would like to create models via relationship methods and also specify attributes that should be added to all models created via that relationship, you may use the `withAttributes` method when building the relationship query: - /** * Get the user's featured posts. */ public function featuredPosts(): HasMany { - return $this->posts()->withAttributes(['featured' => true]); + return $this->posts()->where('featured', true); } +} +``` + +However, if you attempt to create a model via the `featuredPosts` method, its `featured` attribute would not be set to `true`. If you would like to create models via relationship methods and also specify attributes that should be added to all models created via that relationship, you may use the `withAttributes` method when building the relationship query: + +```php +/** + * Get the user's featured posts. + */ +public function featuredPosts(): HasMany +{ + return $this->posts()->withAttributes(['featured' => true]); +} +``` The `withAttributes` method will add `where` clause constraints to the query using the given attributes, and it will also add the given attributes to any models created via the relationship method: - $post = $user->featuredPosts()->create(['title' => 'Featured Post']); +```php +$post = $user->featuredPosts()->create(['title' => 'Featured Post']); - $post->featured; // true +$post->featured; // true +``` ## Many to Many Relationships @@ -672,85 +736,99 @@ To define this relationship, three database tables are needed: `users`, `roles`, Remember, since a role can belong to many users, we cannot simply place a `user_id` column on the `roles` table. This would mean that a role could only belong to a single user. In order to provide support for roles being assigned to multiple users, the `role_user` table is needed. We can summarize the relationship's table structure like so: - users - id - integer - name - string +```text +users + id - integer + name - string - roles - id - integer - name - string +roles + id - integer + name - string - role_user - user_id - integer - role_id - integer +role_user + user_id - integer + role_id - integer +``` #### Model Structure Many-to-many relationships are defined by writing a method that returns the result of the `belongsToMany` method. The `belongsToMany` method is provided by the `Illuminate\Database\Eloquent\Model` base class that is used by all of your application's Eloquent models. For example, let's define a `roles` method on our `User` model. The first argument passed to this method is the name of the related model class: - belongsToMany(Role::class); - } + return $this->belongsToMany(Role::class); } +} +``` Once the relationship is defined, you may access the user's roles using the `roles` dynamic relationship property: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - foreach ($user->roles as $role) { - // ... - } +foreach ($user->roles as $role) { + // ... +} +``` Since all relationships also serve as query builders, you may add further constraints to the relationship query by calling the `roles` method and continuing to chain conditions onto the query: - $roles = User::find(1)->roles()->orderBy('name')->get(); +```php +$roles = User::find(1)->roles()->orderBy('name')->get(); +``` To determine the table name of the relationship's intermediate table, Eloquent will join the two related model names in alphabetical order. However, you are free to override this convention. You may do so by passing a second argument to the `belongsToMany` method: - return $this->belongsToMany(Role::class, 'role_user'); +```php +return $this->belongsToMany(Role::class, 'role_user'); +``` In addition to customizing the name of the intermediate table, you may also customize the column names of the keys on the table by passing additional arguments to the `belongsToMany` method. The third argument is the foreign key name of the model on which you are defining the relationship, while the fourth argument is the foreign key name of the model that you are joining to: - return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id'); +```php +return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id'); +``` #### Defining the Inverse of the Relationship To define the "inverse" of a many-to-many relationship, you should define a method on the related model which also returns the result of the `belongsToMany` method. To complete our user / role example, let's define the `users` method on the `Role` model: - belongsToMany(User::class); - } + return $this->belongsToMany(User::class); } +} +``` As you can see, the relationship is defined exactly the same as its `User` model counterpart with the exception of referencing the `App\Models\User` model. Since we're reusing the `belongsToMany` method, all of the usual table and key customization options are available when defining the "inverse" of many-to-many relationships. @@ -759,23 +837,29 @@ As you can see, the relationship is defined exactly the same as its `User` model As you have already learned, working with many-to-many relations requires the presence of an intermediate table. Eloquent provides some very helpful ways of interacting with this table. For example, let's assume our `User` model has many `Role` models that it is related to. After accessing this relationship, we may access the intermediate table using the `pivot` attribute on the models: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - foreach ($user->roles as $role) { - echo $role->pivot->created_at; - } +foreach ($user->roles as $role) { + echo $role->pivot->created_at; +} +``` Notice that each `Role` model we retrieve is automatically assigned a `pivot` attribute. This attribute contains a model representing the intermediate table. By default, only the model keys will be present on the `pivot` model. If your intermediate table contains extra attributes, you must specify them when defining the relationship: - return $this->belongsToMany(Role::class)->withPivot('active', 'created_by'); +```php +return $this->belongsToMany(Role::class)->withPivot('active', 'created_by'); +``` If you would like your intermediate table to have `created_at` and `updated_at` timestamps that are automatically maintained by Eloquent, call the `withTimestamps` method when defining the relationship: - return $this->belongsToMany(Role::class)->withTimestamps(); +```php +return $this->belongsToMany(Role::class)->withTimestamps(); +``` > [!WARNING] > Intermediate tables that utilize Eloquent's automatically maintained timestamps are required to have both `created_at` and `updated_at` timestamp columns. @@ -787,61 +871,71 @@ As noted previously, attributes from the intermediate table may be accessed on m 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: - return $this->belongsToMany(Podcast::class) - ->as('subscription') - ->withTimestamps(); +```php +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: - $users = User::with('podcasts')->get(); +```php +$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 You can also filter the results returned by `belongsToMany` relationship queries using the `wherePivot`, `wherePivotIn`, `wherePivotNotIn`, `wherePivotBetween`, `wherePivotNotBetween`, `wherePivotNull`, and `wherePivotNotNull` methods when defining the relationship: - return $this->belongsToMany(Role::class) - ->wherePivot('approved', 1); +```php +return $this->belongsToMany(Role::class) + ->wherePivot('approved', 1); - return $this->belongsToMany(Role::class) - ->wherePivotIn('priority', [1, 2]); +return $this->belongsToMany(Role::class) + ->wherePivotIn('priority', [1, 2]); - return $this->belongsToMany(Role::class) - ->wherePivotNotIn('priority', [1, 2]); +return $this->belongsToMany(Role::class) + ->wherePivotNotIn('priority', [1, 2]); - return $this->belongsToMany(Podcast::class) - ->as('subscriptions') - ->wherePivotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']); +return $this->belongsToMany(Podcast::class) + ->as('subscriptions') + ->wherePivotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']); - return $this->belongsToMany(Podcast::class) - ->as('subscriptions') - ->wherePivotNotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']); +return $this->belongsToMany(Podcast::class) + ->as('subscriptions') + ->wherePivotNotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']); - return $this->belongsToMany(Podcast::class) - ->as('subscriptions') - ->wherePivotNull('expired_at'); +return $this->belongsToMany(Podcast::class) + ->as('subscriptions') + ->wherePivotNull('expired_at'); - return $this->belongsToMany(Podcast::class) - ->as('subscriptions') - ->wherePivotNotNull('expired_at'); +return $this->belongsToMany(Podcast::class) + ->as('subscriptions') + ->wherePivotNotNull('expired_at'); +``` The `wherePivot` adds a where clause constraint to the query, but does not add the specified value when creating new models via the defined relationship. If you need to both query and create relationships with a particular pivot value, you may use the `withPivotValue` method: - return $this->belongsToMany(Role::class) - ->withPivotValue('approved', 1); +```php +return $this->belongsToMany(Role::class) + ->withPivotValue('approved', 1); +``` ### Ordering Queries via Intermediate Table Columns You can order the results returned by `belongsToMany` relationship queries using the `orderByPivot` method. In the following example, we will retrieve all of the latest badges for the user: - return $this->belongsToMany(Badge::class) - ->where('rank', 'gold') - ->orderByPivot('created_at', 'desc'); +```php +return $this->belongsToMany(Badge::class) + ->where('rank', 'gold') + ->orderByPivot('created_at', 'desc'); +``` ### Defining Custom Intermediate Table Models @@ -850,36 +944,40 @@ If you would like to define a custom model to represent the intermediate table o Custom many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\Pivot` class while custom polymorphic many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\MorphPivot` class. For example, we may define a `Role` model which uses a custom `RoleUser` pivot model: - belongsToMany(User::class)->using(RoleUser::class); - } + return $this->belongsToMany(User::class)->using(RoleUser::class); } +} +``` When defining the `RoleUser` model, you should extend the `Illuminate\Database\Eloquent\Relations\Pivot` class: - [!WARNING] > Pivot models may not use the `SoftDeletes` trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model. @@ -889,12 +987,14 @@ When defining the `RoleUser` model, you should extend the `Illuminate\Database\E If you have defined a many-to-many relationship that uses a custom pivot model, and that pivot model has an auto-incrementing primary key, you should ensure your custom pivot model class defines an `incrementing` property that is set to `true`. - /** - * Indicates if the IDs are auto-incrementing. - * - * @var bool - */ - public $incrementing = true; +```php +/** + * Indicates if the IDs are auto-incrementing. + * + * @var bool + */ +public $incrementing = true; +``` ## Polymorphic Relationships @@ -909,19 +1009,21 @@ A polymorphic relationship allows the child model to belong to more than one typ A one-to-one polymorphic relation is similar to a typical one-to-one relation; however, the child model can belong to more than one type of model using a single association. For example, a blog `Post` and a `User` may share a polymorphic relation to an `Image` model. Using a one-to-one polymorphic relation allows you to have a single table of unique images that may be associated with posts and users. First, let's examine the table structure: - posts - id - integer - name - string +```text +posts + id - integer + name - string - users - id - integer - name - string +users + id - integer + name - string - images - id - integer - url - string - imageable_id - integer - imageable_type - string +images + id - integer + url - string + imageable_id - integer + imageable_type - string +``` Note the `imageable_id` and `imageable_type` columns on the `images` table. The `imageable_id` column will contain the ID value of the post or user, while the `imageable_type` column will contain the class name of the parent model. The `imageable_type` column is used by Eloquent to determine which "type" of parent model to return when accessing the `imageable` relation. In this case, the column would contain either `App\Models\Post` or `App\Models\User`. @@ -930,70 +1032,76 @@ Note the `imageable_id` and `imageable_type` columns on the `images` table. The Next, let's examine the model definitions needed to build this relationship: - morphTo(); - } + return $this->morphTo(); } +} - use Illuminate\Database\Eloquent\Model; - use Illuminate\Database\Eloquent\Relations\MorphOne; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphOne; - class Post extends Model +class Post extends Model +{ + /** + * Get the post's image. + */ + public function image(): MorphOne { - /** - * Get the post's image. - */ - public function image(): MorphOne - { - return $this->morphOne(Image::class, 'imageable'); - } + return $this->morphOne(Image::class, 'imageable'); } +} - use Illuminate\Database\Eloquent\Model; - use Illuminate\Database\Eloquent\Relations\MorphOne; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphOne; - class User extends Model +class User extends Model +{ + /** + * Get the user's image. + */ + public function image(): MorphOne { - /** - * Get the user's image. - */ - public function image(): MorphOne - { - return $this->morphOne(Image::class, 'imageable'); - } + return $this->morphOne(Image::class, 'imageable'); } +} +``` #### Retrieving the Relationship Once your database table and models are defined, you may access the relationships via your models. For example, to retrieve the image for a post, we can access the `image` dynamic relationship property: - use App\Models\Post; +```php +use App\Models\Post; - $post = Post::find(1); +$post = Post::find(1); - $image = $post->image; +$image = $post->image; +``` You may retrieve the parent of the polymorphic model by accessing the name of the method that performs the call to `morphTo`. In this case, that is the `imageable` method on the `Image` model. So, we will access that method as a dynamic relationship property: - use App\Models\Image; +```php +use App\Models\Image; - $image = Image::find(1); +$image = Image::find(1); - $imageable = $image->imageable; +$imageable = $image->imageable; +``` The `imageable` relation on the `Image` model will return either a `Post` or `User` instance, depending on which type of model owns the image. @@ -1002,13 +1110,15 @@ The `imageable` relation on the `Image` model will return either a `Post` or `Us If necessary, you may specify the name of the "id" and "type" columns utilized by your polymorphic child model. If you do so, ensure that you always pass the name of the relationship as the first argument to the `morphTo` method. Typically, this value should match the method name, so you may use PHP's `__FUNCTION__` constant: - /** - * Get the model that the image belongs to. - */ - public function imageable(): MorphTo - { - return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id'); - } +```php +/** + * Get the model that the image belongs to. + */ +public function imageable(): MorphTo +{ + return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id'); +} +``` ### One to Many (Polymorphic) @@ -1018,93 +1128,101 @@ If necessary, you may specify the name of the "id" and "type" columns utilized b A one-to-many polymorphic relation is similar to a typical one-to-many relation; however, the child model can belong to more than one type of model using a single association. For example, imagine users of your application can "comment" on posts and videos. Using polymorphic relationships, you may use a single `comments` table to contain comments for both posts and videos. First, let's examine the table structure required to build this relationship: - posts - id - integer - title - string - body - text - - videos - id - integer - title - string - url - string - - comments - id - integer - body - text - commentable_id - integer - commentable_type - string +```text +posts + id - integer + title - string + body - text + +videos + id - integer + title - string + url - string + +comments + id - integer + body - text + commentable_id - integer + commentable_type - string +``` #### Model Structure Next, let's examine the model definitions needed to build this relationship: - morphTo(); - } + return $this->morphTo(); } +} - use Illuminate\Database\Eloquent\Model; - use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphMany; - class Post extends Model +class Post extends Model +{ + /** + * Get all of the post's comments. + */ + public function comments(): MorphMany { - /** - * Get all of the post's comments. - */ - public function comments(): MorphMany - { - return $this->morphMany(Comment::class, 'commentable'); - } + return $this->morphMany(Comment::class, 'commentable'); } +} - use Illuminate\Database\Eloquent\Model; - use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphMany; - class Video extends Model +class Video extends Model +{ + /** + * Get all of the video's comments. + */ + public function comments(): MorphMany { - /** - * Get all of the video's comments. - */ - public function comments(): MorphMany - { - return $this->morphMany(Comment::class, 'commentable'); - } + return $this->morphMany(Comment::class, 'commentable'); } +} +``` #### Retrieving the Relationship Once your database table and models are defined, you may access the relationships via your model's dynamic relationship properties. For example, to access all of the comments for a post, we can use the `comments` dynamic property: - use App\Models\Post; +```php +use App\Models\Post; - $post = Post::find(1); +$post = Post::find(1); - foreach ($post->comments as $comment) { - // ... - } +foreach ($post->comments as $comment) { + // ... +} +``` You may also retrieve the parent of a polymorphic child model by accessing the name of the method that performs the call to `morphTo`. In this case, that is the `commentable` method on the `Comment` model. So, we will access that method as a dynamic relationship property in order to access the comment's parent model: - use App\Models\Comment; +```php +use App\Models\Comment; - $comment = Comment::find(1); +$comment = Comment::find(1); - $commentable = $comment->commentable; +$commentable = $comment->commentable; +``` The `commentable` relation on the `Comment` model will return either a `Post` or `Video` instance, depending on which type of model is the comment's parent. @@ -1127,16 +1245,18 @@ In the example above, an "N + 1" query problem has been introduced because, even If you would like Eloquent to automatically hydrate parent models onto their children, you may invoke the `chaperone` method when defining a `morphMany` relationship: - class Post extends Model +```php +class Post extends Model +{ + /** + * Get all of the post's comments. + */ + public function comments(): MorphMany { - /** - * Get all of the post's comments. - */ - public function comments(): MorphMany - { - return $this->morphMany(Comment::class, 'commentable')->chaperone(); - } + return $this->morphMany(Comment::class, 'commentable')->chaperone(); } +} +``` Or, if you would like to opt-in to automatic parent hydration at run time, you may invoke the `chaperone` model when eager loading the relationship: @@ -1200,22 +1320,24 @@ public function bestImage(): MorphOne Many-to-many polymorphic relations are slightly more complicated than "morph one" and "morph many" relationships. For example, a `Post` model and `Video` model could share a polymorphic relation to a `Tag` model. Using a many-to-many polymorphic relation in this situation would allow your application to have a single table of unique tags that may be associated with posts or videos. First, let's examine the table structure required to build this relationship: - posts - id - integer - name - string +```text +posts + id - integer + name - string - videos - id - integer - name - string +videos + id - integer + name - string - tags - id - integer - name - string +tags + id - integer + name - string - taggables - tag_id - integer - taggable_id - integer - taggable_type - string +taggables + tag_id - integer + taggable_id - integer + taggable_type - string +``` > [!NOTE] > Before diving into polymorphic many-to-many relationships, you may benefit from reading the documentation on typical [many-to-many relationships](#many-to-many). @@ -1227,23 +1349,25 @@ Next, we're ready to define the relationships on the models. The `Post` and `Vid The `morphToMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we will refer to the relationship as "taggable": - morphToMany(Tag::class, 'taggable'); - } + return $this->morphToMany(Tag::class, 'taggable'); } +} +``` #### Defining the Inverse of the Relationship @@ -1252,58 +1376,64 @@ Next, on the `Tag` model, you should define a method for each of its possible pa The `morphedByMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we will refer to the relationship as "taggable": - morphedByMany(Post::class, 'taggable'); - } - - /** - * Get all of the videos that are assigned this tag. - */ - public function videos(): MorphToMany - { - return $this->morphedByMany(Video::class, 'taggable'); - } + return $this->morphedByMany(Post::class, 'taggable'); } + /** + * Get all of the videos that are assigned this tag. + */ + public function videos(): MorphToMany + { + return $this->morphedByMany(Video::class, 'taggable'); + } +} +``` + #### Retrieving the Relationship Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the tags for a post, you may use the `tags` dynamic relationship property: - use App\Models\Post; +```php +use App\Models\Post; - $post = Post::find(1); +$post = Post::find(1); - foreach ($post->tags as $tag) { - // ... - } +foreach ($post->tags as $tag) { + // ... +} +``` You may retrieve the parent of a polymorphic relation from the polymorphic child model by accessing the name of the method that performs the call to `morphedByMany`. In this case, that is the `posts` or `videos` methods on the `Tag` model: - use App\Models\Tag; +```php +use App\Models\Tag; - $tag = Tag::find(1); +$tag = Tag::find(1); - foreach ($tag->posts as $post) { - // ... - } +foreach ($tag->posts as $post) { + // ... +} - foreach ($tag->videos as $video) { - // ... - } +foreach ($tag->videos as $video) { + // ... +} +``` ### Custom Polymorphic Types @@ -1312,22 +1442,26 @@ By default, Laravel will use the fully qualified class name to store the "type" For example, instead of using the model names as the "type", we may use simple strings such as `post` and `video`. By doing so, the polymorphic "type" column values in our database will remain valid even if the models are renamed: - use Illuminate\Database\Eloquent\Relations\Relation; +```php +use Illuminate\Database\Eloquent\Relations\Relation; - Relation::enforceMorphMap([ - 'post' => 'App\Models\Post', - 'video' => 'App\Models\Video', - ]); +Relation::enforceMorphMap([ + 'post' => 'App\Models\Post', + 'video' => 'App\Models\Video', +]); +``` You may call the `enforceMorphMap` method in the `boot` method of your `App\Providers\AppServiceProvider` class or create a separate service provider if you wish. You may determine the morph alias of a given model at runtime using the model's `getMorphClass` method. Conversely, you may determine the fully-qualified class name associated with a morph alias using the `Relation::getMorphedModel` method: - use Illuminate\Database\Eloquent\Relations\Relation; +```php +use Illuminate\Database\Eloquent\Relations\Relation; - $alias = $post->getMorphClass(); +$alias = $post->getMorphClass(); - $class = Relation::getMorphedModel($alias); +$class = Relation::getMorphedModel($alias); +``` > [!WARNING] > When adding a "morph map" to your existing application, every morphable `*_type` column value in your database that still contains a fully-qualified class will need to be converted to its "map" name. @@ -1339,12 +1473,14 @@ You may use the `resolveRelationUsing` method to define relations between Eloque The `resolveRelationUsing` method accepts the desired relationship name as its first argument. The second argument passed to the method should be a closure that accepts the model instance and returns a valid Eloquent relationship definition. Typically, you should configure dynamic relationships within the boot method of a [service provider](/docs/{{version}}/providers): - use App\Models\Order; - use App\Models\Customer; +```php +use App\Models\Order; +use App\Models\Customer; - Order::resolveRelationUsing('customer', function (Order $orderModel) { - return $orderModel->belongsTo(Customer::class, 'customer_id'); - }); +Order::resolveRelationUsing('customer', function (Order $orderModel) { + return $orderModel->belongsTo(Customer::class, 'customer_id'); +}); +``` > [!WARNING] > When defining dynamic relationships, always provide explicit key name arguments to the Eloquent relationship methods. @@ -1356,31 +1492,35 @@ Since all Eloquent relationships are defined via methods, you may call those met For example, imagine a blog application in which a `User` model has many associated `Post` models: - hasMany(Post::class); - } + return $this->hasMany(Post::class); } +} +``` You may query the `posts` relationship and add additional constraints to the relationship like so: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - $user->posts()->where('active', 1)->get(); +$user->posts()->where('active', 1)->get(); +``` You are able to use any of the Laravel [query builder's](/docs/{{version}}/queries) methods on the relationship, so be sure to explore the query builder documentation to learn about all of the methods that are available to you. @@ -1389,10 +1529,12 @@ You are able to use any of the Laravel [query builder's](/docs/{{version}}/queri As demonstrated in the example above, you are free to add additional constraints to relationships when querying them. However, use caution when chaining `orWhere` clauses onto a relationship, as the `orWhere` clauses will be logically grouped at the same level as the relationship constraint: - $user->posts() - ->where('active', 1) - ->orWhere('votes', '>=', 100) - ->get(); +```php +$user->posts() + ->where('active', 1) + ->orWhere('votes', '>=', 100) + ->get(); +``` The example above will generate the following SQL. As you can see, the `or` clause instructs the query to return _any_ post with greater than 100 votes. The query is no longer constrained to a specific user: @@ -1404,14 +1546,16 @@ where user_id = ? and active = 1 or votes >= 100 In most situations, you should use [logical groups](/docs/{{version}}/queries#logical-grouping) to group the conditional checks between parentheses: - use Illuminate\Database\Eloquent\Builder; - - $user->posts() - ->where(function (Builder $query) { - return $query->where('active', 1) - ->orWhere('votes', '>=', 100); - }) - ->get(); +```php +use Illuminate\Database\Eloquent\Builder; + +$user->posts() + ->where(function (Builder $query) { + return $query->where('active', 1) + ->orWhere('votes', '>=', 100); + }) + ->get(); +``` The example above will produce the following SQL. Note that the logical grouping has properly grouped the constraints and the query remains constrained to a specific user: @@ -1426,13 +1570,15 @@ where user_id = ? and (active = 1 or votes >= 100) If you do not need to add additional constraints to an Eloquent relationship query, you may access the relationship as if it were a property. For example, continuing to use our `User` and `Post` example models, we may access all of a user's posts like so: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - foreach ($user->posts as $post) { - // ... - } +foreach ($user->posts as $post) { + // ... +} +``` Dynamic relationship properties perform "lazy loading", meaning they will only load their relationship data when you actually access them. Because of this, developers often use [eager loading](#eager-loading) to pre-load relationships they know will be accessed after loading the model. Eager loading provides a significant reduction in SQL queries that must be executed to load a model's relations. @@ -1441,34 +1587,42 @@ Dynamic relationship properties perform "lazy loading", meaning they will only l When retrieving model records, you may wish to limit your results based on the existence of a relationship. For example, imagine you want to retrieve all blog posts that have at least one comment. To do so, you may pass the name of the relationship to the `has` and `orHas` methods: - use App\Models\Post; +```php +use App\Models\Post; - // Retrieve all posts that have at least one comment... - $posts = Post::has('comments')->get(); +// Retrieve all posts that have at least one comment... +$posts = Post::has('comments')->get(); +``` You may also specify an operator and count value to further customize the query: - // Retrieve all posts that have three or more comments... - $posts = Post::has('comments', '>=', 3)->get(); +```php +// Retrieve all posts that have three or more comments... +$posts = Post::has('comments', '>=', 3)->get(); +``` Nested `has` statements may be constructed using "dot" notation. For example, you may retrieve all posts that have at least one comment that has at least one image: - // Retrieve posts that have at least one comment with images... - $posts = Post::has('comments.images')->get(); +```php +// Retrieve posts that have at least one comment with images... +$posts = Post::has('comments.images')->get(); +``` If you need even more power, you may use the `whereHas` and `orWhereHas` methods to define additional query constraints on your `has` queries, such as inspecting the content of a comment: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - // Retrieve posts with at least one comment containing words like code%... - $posts = Post::whereHas('comments', function (Builder $query) { - $query->where('content', 'like', 'code%'); - })->get(); +// Retrieve posts with at least one comment containing words like code%... +$posts = Post::whereHas('comments', function (Builder $query) { + $query->where('content', 'like', 'code%'); +})->get(); - // Retrieve posts with at least ten comments containing words like code%... - $posts = Post::whereHas('comments', function (Builder $query) { - $query->where('content', 'like', 'code%'); - }, '>=', 10)->get(); +// Retrieve posts with at least ten comments containing words like code%... +$posts = Post::whereHas('comments', function (Builder $query) { + $query->where('content', 'like', 'code%'); +}, '>=', 10)->get(); +``` > [!WARNING] > Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. @@ -1478,99 +1632,117 @@ If you need even more power, you may use the `whereHas` and `orWhereHas` methods If you would like to query for a relationship's existence with a single, simple where condition attached to the relationship query, you may find it more convenient to use the `whereRelation`, `orWhereRelation`, `whereMorphRelation`, and `orWhereMorphRelation` methods. For example, we may query for all posts that have unapproved comments: - use App\Models\Post; +```php +use App\Models\Post; - $posts = Post::whereRelation('comments', 'is_approved', false)->get(); +$posts = Post::whereRelation('comments', 'is_approved', false)->get(); +``` Of course, like calls to the query builder's `where` method, you may also specify an operator: - $posts = Post::whereRelation( - 'comments', 'created_at', '>=', now()->subHour() - )->get(); +```php +$posts = Post::whereRelation( + 'comments', 'created_at', '>=', now()->subHour() +)->get(); +``` ### Querying Relationship Absence When retrieving model records, you may wish to limit your results based on the absence of a relationship. For example, imagine you want to retrieve all blog posts that **don't** have any comments. To do so, you may pass the name of the relationship to the `doesntHave` and `orDoesntHave` methods: - use App\Models\Post; +```php +use App\Models\Post; - $posts = Post::doesntHave('comments')->get(); +$posts = Post::doesntHave('comments')->get(); +``` If you need even more power, you may use the `whereDoesntHave` and `orWhereDoesntHave` methods to add additional query constraints to your `doesntHave` queries, such as inspecting the content of a comment: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - $posts = Post::whereDoesntHave('comments', function (Builder $query) { - $query->where('content', 'like', 'code%'); - })->get(); +$posts = Post::whereDoesntHave('comments', function (Builder $query) { + $query->where('content', 'like', 'code%'); +})->get(); +``` You may use "dot" notation to execute a query against a nested relationship. For example, the following query will retrieve all posts that do not have comments; however, posts that have comments from authors that are not banned will be included in the results: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - $posts = Post::whereDoesntHave('comments.author', function (Builder $query) { - $query->where('banned', 0); - })->get(); +$posts = Post::whereDoesntHave('comments.author', function (Builder $query) { + $query->where('banned', 0); +})->get(); +``` ### Querying Morph To Relationships To query the existence of "morph to" relationships, you may use the `whereHasMorph` and `whereDoesntHaveMorph` methods. These methods accept the name of the relationship as their first argument. Next, the methods accept the names of the related models that you wish to include in the query. Finally, you may provide a closure which customizes the relationship query: - use App\Models\Comment; - use App\Models\Post; - use App\Models\Video; - use Illuminate\Database\Eloquent\Builder; - - // Retrieve comments associated to posts or videos with a title like code%... - $comments = Comment::whereHasMorph( - 'commentable', - [Post::class, Video::class], - function (Builder $query) { - $query->where('title', 'like', 'code%'); - } - )->get(); - - // Retrieve comments associated to posts with a title not like code%... - $comments = Comment::whereDoesntHaveMorph( - 'commentable', - Post::class, - function (Builder $query) { - $query->where('title', 'like', 'code%'); - } - )->get(); +```php +use App\Models\Comment; +use App\Models\Post; +use App\Models\Video; +use Illuminate\Database\Eloquent\Builder; + +// Retrieve comments associated to posts or videos with a title like code%... +$comments = Comment::whereHasMorph( + 'commentable', + [Post::class, Video::class], + function (Builder $query) { + $query->where('title', 'like', 'code%'); + } +)->get(); + +// Retrieve comments associated to posts with a title not like code%... +$comments = Comment::whereDoesntHaveMorph( + 'commentable', + Post::class, + function (Builder $query) { + $query->where('title', 'like', 'code%'); + } +)->get(); +``` You may occasionally need to add query constraints based on the "type" of the related polymorphic model. The closure passed to the `whereHasMorph` method may receive a `$type` value as its second argument. This argument allows you to inspect the "type" of the query that is being built: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - $comments = Comment::whereHasMorph( - 'commentable', - [Post::class, Video::class], - function (Builder $query, string $type) { - $column = $type === Post::class ? 'content' : 'title'; +$comments = Comment::whereHasMorph( + 'commentable', + [Post::class, Video::class], + function (Builder $query, string $type) { + $column = $type === Post::class ? 'content' : 'title'; - $query->where($column, 'like', 'code%'); - } - )->get(); + $query->where($column, 'like', 'code%'); + } +)->get(); +``` Sometimes you may want to query for the children of a "morph to" relationship's parent. You may accomplish this using the `whereMorphedTo` and `whereNotMorphedTo` methods, which will automatically determine the proper morph type mapping for the given model. These methods accept the name of the `morphTo` relationship as their first argument and the related parent model as their second argument: - $comments = Comment::whereMorphedTo('commentable', $post) - ->orWhereMorphedTo('commentable', $video) - ->get(); +```php +$comments = Comment::whereMorphedTo('commentable', $post) + ->orWhereMorphedTo('commentable', $video) + ->get(); +``` #### Querying All Related Models Instead of passing an array of possible polymorphic models, you may provide `*` as a wildcard value. This will instruct Laravel to retrieve all of the possible polymorphic types from the database. Laravel will execute an additional query in order to perform this operation: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - $comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) { - $query->where('title', 'like', 'foo%'); - })->get(); +$comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) { + $query->where('title', 'like', 'foo%'); +})->get(); +``` ## Aggregating Related Models @@ -1580,95 +1752,115 @@ Instead of passing an array of possible polymorphic models, you may provide `*` Sometimes you may want to count the number of related models for a given relationship without actually loading the models. To accomplish this, you may use the `withCount` method. The `withCount` method will place a `{relation}_count` attribute on the resulting models: - use App\Models\Post; +```php +use App\Models\Post; - $posts = Post::withCount('comments')->get(); +$posts = Post::withCount('comments')->get(); - foreach ($posts as $post) { - echo $post->comments_count; - } +foreach ($posts as $post) { + echo $post->comments_count; +} +``` By passing an array to the `withCount` method, you may add the "counts" for multiple relations as well as add additional constraints to the queries: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - $posts = Post::withCount(['votes', 'comments' => function (Builder $query) { - $query->where('content', 'like', 'code%'); - }])->get(); +$posts = Post::withCount(['votes', 'comments' => function (Builder $query) { + $query->where('content', 'like', 'code%'); +}])->get(); - echo $posts[0]->votes_count; - echo $posts[0]->comments_count; +echo $posts[0]->votes_count; +echo $posts[0]->comments_count; +``` You may also alias the relationship count result, allowing multiple counts on the same relationship: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - $posts = Post::withCount([ - 'comments', - 'comments as pending_comments_count' => function (Builder $query) { - $query->where('approved', false); - }, - ])->get(); +$posts = Post::withCount([ + 'comments', + 'comments as pending_comments_count' => function (Builder $query) { + $query->where('approved', false); + }, +])->get(); - echo $posts[0]->comments_count; - echo $posts[0]->pending_comments_count; +echo $posts[0]->comments_count; +echo $posts[0]->pending_comments_count; +``` #### Deferred Count Loading Using the `loadCount` method, you may load a relationship count after the parent model has already been retrieved: - $book = Book::first(); +```php +$book = Book::first(); - $book->loadCount('genres'); +$book->loadCount('genres'); +``` If you need to set additional query constraints on the count query, you may pass an array keyed by the relationships you wish to count. The array values should be closures which receive the query builder instance: - $book->loadCount(['reviews' => function (Builder $query) { - $query->where('rating', 5); - }]) +```php +$book->loadCount(['reviews' => function (Builder $query) { + $query->where('rating', 5); +}]) +``` #### Relationship Counting and Custom Select Statements If you're combining `withCount` with a `select` statement, ensure that you call `withCount` after the `select` method: - $posts = Post::select(['title', 'body']) - ->withCount('comments') - ->get(); +```php +$posts = Post::select(['title', 'body']) + ->withCount('comments') + ->get(); +``` ### Other Aggregate Functions In addition to the `withCount` method, Eloquent provides `withMin`, `withMax`, `withAvg`, `withSum`, and `withExists` methods. These methods will place a `{relation}_{function}_{column}` attribute on your resulting models: - use App\Models\Post; +```php +use App\Models\Post; - $posts = Post::withSum('comments', 'votes')->get(); +$posts = Post::withSum('comments', 'votes')->get(); - foreach ($posts as $post) { - echo $post->comments_sum_votes; - } +foreach ($posts as $post) { + echo $post->comments_sum_votes; +} +``` If you wish to access the result of the aggregate function using another name, you may specify your own alias: - $posts = Post::withSum('comments as total_comments', 'votes')->get(); +```php +$posts = Post::withSum('comments as total_comments', 'votes')->get(); - foreach ($posts as $post) { - echo $post->total_comments; - } +foreach ($posts as $post) { + echo $post->total_comments; +} +``` Like the `loadCount` method, deferred versions of these methods are also available. These additional aggregate operations may be performed on Eloquent models that have already been retrieved: - $post = Post::first(); +```php +$post = Post::first(); - $post->loadSum('comments', 'votes'); +$post->loadSum('comments', 'votes'); +``` If you're combining these aggregate methods with a `select` statement, ensure that you call the aggregate methods after the `select` method: - $posts = Post::select(['title', 'body']) - ->withExists('comments') - ->get(); +```php +$posts = Post::select(['title', 'body']) + ->withExists('comments') + ->get(); +``` ### Counting Related Models on Morph To Relationships @@ -1679,70 +1871,80 @@ In this example, let's assume that `Photo` and `Post` models may create `Activit Now, let's imagine we want to retrieve `ActivityFeed` instances and eager load the `parentable` parent models for each `ActivityFeed` instance. In addition, we want to retrieve the number of tags that are associated with each parent photo and the number of comments that are associated with each parent post: - use Illuminate\Database\Eloquent\Relations\MorphTo; +```php +use Illuminate\Database\Eloquent\Relations\MorphTo; - $activities = ActivityFeed::with([ - 'parentable' => function (MorphTo $morphTo) { - $morphTo->morphWithCount([ - Photo::class => ['tags'], - Post::class => ['comments'], - ]); - }])->get(); +$activities = ActivityFeed::with([ + 'parentable' => function (MorphTo $morphTo) { + $morphTo->morphWithCount([ + Photo::class => ['tags'], + Post::class => ['comments'], + ]); + }])->get(); +``` #### Deferred Count Loading Let's assume we have already retrieved a set of `ActivityFeed` models and now we would like to load the nested relationship counts for the various `parentable` models associated with the activity feeds. You may use the `loadMorphCount` method to accomplish this: - $activities = ActivityFeed::with('parentable')->get(); +```php +$activities = ActivityFeed::with('parentable')->get(); - $activities->loadMorphCount('parentable', [ - Photo::class => ['tags'], - Post::class => ['comments'], - ]); +$activities->loadMorphCount('parentable', [ + Photo::class => ['tags'], + Post::class => ['comments'], +]); +``` ## Eager Loading When accessing Eloquent relationships as properties, the related models are "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model. Eager loading alleviates the "N + 1" query problem. To illustrate the N + 1 query problem, consider a `Book` model that "belongs to" to an `Author` model: - belongsTo(Author::class); - } + return $this->belongsTo(Author::class); } +} +``` Now, let's retrieve all books and their authors: - use App\Models\Book; +```php +use App\Models\Book; - $books = Book::all(); +$books = Book::all(); - foreach ($books as $book) { - echo $book->author->name; - } +foreach ($books as $book) { + echo $book->author->name; +} +``` This loop will execute one query to retrieve all of the books within the database table, then another query for each book in order to retrieve the book's author. So, if we have 25 books, the code above would run 26 queries: one for the original book, and 25 additional queries to retrieve the author of each book. Thankfully, we can use eager loading to reduce this operation to just two queries. When building a query, you may specify which relationships should be eager loaded using the `with` method: - $books = Book::with('author')->get(); +```php +$books = Book::with('author')->get(); - foreach ($books as $book) { - echo $book->author->name; - } +foreach ($books as $book) { + echo $book->author->name; +} +``` For this operation, only two queries will be executed - one query to retrieve all of the books and one query to retrieve all of the authors for all of the books: @@ -1757,66 +1959,78 @@ select * from authors where id in (1, 2, 3, 4, 5, ...) Sometimes you may need to eager load several different relationships. To do so, just pass an array of relationships to the `with` method: - $books = Book::with(['author', 'publisher'])->get(); +```php +$books = Book::with(['author', 'publisher'])->get(); +``` #### Nested Eager Loading To eager load a relationship's relationships, you may use "dot" syntax. For example, let's eager load all of the book's authors and all of the author's personal contacts: - $books = Book::with('author.contacts')->get(); +```php +$books = Book::with('author.contacts')->get(); +``` Alternatively, you may specify nested eager loaded relationships by providing a nested array to the `with` method, which can be convenient when eager loading multiple nested relationships: - $books = Book::with([ - 'author' => [ - 'contacts', - 'publisher', - ], - ])->get(); +```php +$books = Book::with([ + 'author' => [ + 'contacts', + 'publisher', + ], +])->get(); +``` #### Nested Eager Loading `morphTo` Relationships If you would like to eager load a `morphTo` relationship, as well as nested relationships on the various entities that may be returned by that relationship, you may use the `with` method in combination with the `morphTo` relationship's `morphWith` method. To help illustrate this method, let's consider the following model: - morphTo(); - } + return $this->morphTo(); } +} +``` In this example, let's assume `Event`, `Photo`, and `Post` models may create `ActivityFeed` models. Additionally, let's assume that `Event` models belong to a `Calendar` model, `Photo` models are associated with `Tag` models, and `Post` models belong to an `Author` model. Using these model definitions and relationships, we may retrieve `ActivityFeed` model instances and eager load all `parentable` models and their respective nested relationships: - use Illuminate\Database\Eloquent\Relations\MorphTo; +```php +use Illuminate\Database\Eloquent\Relations\MorphTo; - $activities = ActivityFeed::query() - ->with(['parentable' => function (MorphTo $morphTo) { - $morphTo->morphWith([ - Event::class => ['calendar'], - Photo::class => ['tags'], - Post::class => ['author'], - ]); - }])->get(); +$activities = ActivityFeed::query() + ->with(['parentable' => function (MorphTo $morphTo) { + $morphTo->morphWith([ + Event::class => ['calendar'], + Photo::class => ['tags'], + Post::class => ['author'], + ]); + }])->get(); +``` #### Eager Loading Specific Columns You may not always need every column from the relationships you are retrieving. For this reason, Eloquent allows you to specify which columns of the relationship you would like to retrieve: - $books = Book::with('author:id,name,book_id')->get(); +```php +$books = Book::with('author:id,name,book_id')->get(); +``` > [!WARNING] > When using this feature, you should always include the `id` column and any relevant foreign key columns in the list of columns you wish to retrieve. @@ -1826,82 +2040,94 @@ You may not always need every column from the relationships you are retrieving. Sometimes you might want to always load some relationships when retrieving a model. To accomplish this, you may define a `$with` property on the model: - belongsTo(Author::class); + } - class Book extends Model + /** + * Get the genre of the book. + */ + public function genre(): BelongsTo { - /** - * The relationships that should always be loaded. - * - * @var array - */ - protected $with = ['author']; - - /** - * Get the author that wrote the book. - */ - public function author(): BelongsTo - { - return $this->belongsTo(Author::class); - } - - /** - * Get the genre of the book. - */ - public function genre(): BelongsTo - { - return $this->belongsTo(Genre::class); - } + return $this->belongsTo(Genre::class); } +} +``` If you would like to remove an item from the `$with` property for a single query, you may use the `without` method: - $books = Book::without('author')->get(); +```php +$books = Book::without('author')->get(); +``` If you would like to override all items within the `$with` property for a single query, you may use the `withOnly` method: - $books = Book::withOnly('genre')->get(); +```php +$books = Book::withOnly('genre')->get(); +``` ### Constraining Eager Loads Sometimes you may wish to eager load a relationship but also specify additional query conditions for the eager loading query. You can accomplish this by passing an array of relationships to the `with` method where the array key is a relationship name and the array value is a closure that adds additional constraints to the eager loading query: - use App\Models\User; - use Illuminate\Contracts\Database\Eloquent\Builder; +```php +use App\Models\User; +use Illuminate\Contracts\Database\Eloquent\Builder; - $users = User::with(['posts' => function (Builder $query) { - $query->where('title', 'like', '%code%'); - }])->get(); +$users = User::with(['posts' => function (Builder $query) { + $query->where('title', 'like', '%code%'); +}])->get(); +``` In this example, Eloquent will only eager load posts where the post's `title` column contains the word `code`. You may call other [query builder](/docs/{{version}}/queries) methods to further customize the eager loading operation: - $users = User::with(['posts' => function (Builder $query) { - $query->orderBy('created_at', 'desc'); - }])->get(); +```php +$users = User::with(['posts' => function (Builder $query) { + $query->orderBy('created_at', 'desc'); +}])->get(); +``` #### Constraining Eager Loading of `morphTo` Relationships If you are eager loading a `morphTo` relationship, Eloquent will run multiple queries to fetch each type of related model. You may add additional constraints to each of these queries using the `MorphTo` relation's `constrain` method: - use Illuminate\Database\Eloquent\Relations\MorphTo; +```php +use Illuminate\Database\Eloquent\Relations\MorphTo; - $comments = Comment::with(['commentable' => function (MorphTo $morphTo) { - $morphTo->constrain([ - Post::class => function ($query) { - $query->whereNull('hidden_at'); - }, - Video::class => function ($query) { - $query->where('type', 'educational'); - }, - ]); - }])->get(); +$comments = Comment::with(['commentable' => function (MorphTo $morphTo) { + $morphTo->constrain([ + Post::class => function ($query) { + $query->whereNull('hidden_at'); + }, + Video::class => function ($query) { + $query->where('type', 'educational'); + }, + ]); +}])->get(); +``` In this example, Eloquent will only eager load posts that have not been hidden and videos that have a `type` value of "educational". @@ -1910,34 +2136,42 @@ In this example, Eloquent will only eager load posts that have not been hidden a You may sometimes find yourself needing to check for the existence of a relationship while simultaneously loading the relationship based on the same conditions. For example, you may wish to only retrieve `User` models that have child `Post` models matching a given query condition while also eager loading the matching posts. You may accomplish this using the `withWhereHas` method: - use App\Models\User; +```php +use App\Models\User; - $users = User::withWhereHas('posts', function ($query) { - $query->where('featured', true); - })->get(); +$users = User::withWhereHas('posts', function ($query) { + $query->where('featured', true); +})->get(); +``` ### Lazy Eager Loading Sometimes you may need to eager load a relationship after the parent model has already been retrieved. For example, this may be useful if you need to dynamically decide whether to load related models: - use App\Models\Book; +```php +use App\Models\Book; - $books = Book::all(); +$books = Book::all(); - if ($someCondition) { - $books->load('author', 'publisher'); - } +if ($someCondition) { + $books->load('author', 'publisher'); +} +``` If you need to set additional query constraints on the eager loading query, you may pass an array keyed by the relationships you wish to load. The array values should be closure instances which receive the query instance: - $author->load(['books' => function (Builder $query) { - $query->orderBy('published_date', 'asc'); - }]); +```php +$author->load(['books' => function (Builder $query) { + $query->orderBy('published_date', 'asc'); +}]); +``` To load a relationship only when it has not already been loaded, use the `loadMissing` method: - $book->loadMissing('author'); +```php +$book->loadMissing('author'); +``` #### Nested Lazy Eager Loading and `morphTo` @@ -1946,33 +2180,37 @@ If you would like to eager load a `morphTo` relationship, as well as nested rela This method accepts the name of the `morphTo` relationship as its first argument, and an array of model / relationship pairs as its second argument. To help illustrate this method, let's consider the following model: - morphTo(); - } + return $this->morphTo(); } +} +``` In this example, let's assume `Event`, `Photo`, and `Post` models may create `ActivityFeed` models. Additionally, let's assume that `Event` models belong to a `Calendar` model, `Photo` models are associated with `Tag` models, and `Post` models belong to an `Author` model. Using these model definitions and relationships, we may retrieve `ActivityFeed` model instances and eager load all `parentable` models and their respective nested relationships: - $activities = ActivityFeed::with('parentable') - ->get() - ->loadMorph('parentable', [ - Event::class => ['calendar'], - Photo::class => ['tags'], - Post::class => ['author'], - ]); +```php +$activities = ActivityFeed::with('parentable') + ->get() + ->loadMorph('parentable', [ + Event::class => ['calendar'], + Photo::class => ['tags'], + Post::class => ['author'], + ]); +``` ### Preventing Lazy Loading @@ -2013,85 +2251,101 @@ Model::handleLazyLoadingViolationUsing(function (Model $model, string $relation) Eloquent provides convenient methods for adding new models to relationships. For example, perhaps you need to add a new comment to a post. Instead of manually setting the `post_id` attribute on the `Comment` model you may insert the comment using the relationship's `save` method: - use App\Models\Comment; - use App\Models\Post; +```php +use App\Models\Comment; +use App\Models\Post; - $comment = new Comment(['message' => 'A new comment.']); +$comment = new Comment(['message' => 'A new comment.']); - $post = Post::find(1); +$post = Post::find(1); - $post->comments()->save($comment); +$post->comments()->save($comment); +``` Note that we did not access the `comments` relationship as a dynamic property. Instead, we called the `comments` method to obtain an instance of the relationship. The `save` method will automatically add the appropriate `post_id` value to the new `Comment` model. If you need to save multiple related models, you may use the `saveMany` method: - $post = Post::find(1); +```php +$post = Post::find(1); - $post->comments()->saveMany([ - new Comment(['message' => 'A new comment.']), - new Comment(['message' => 'Another new comment.']), - ]); +$post->comments()->saveMany([ + new Comment(['message' => 'A new comment.']), + new Comment(['message' => 'Another new comment.']), +]); +``` The `save` and `saveMany` methods will persist the given model instances, but will not add the newly persisted models to any in-memory relationships that are already loaded onto the parent model. If you plan on accessing the relationship after using the `save` or `saveMany` methods, you may wish to use the `refresh` method to reload the model and its relationships: - $post->comments()->save($comment); +```php +$post->comments()->save($comment); - $post->refresh(); +$post->refresh(); - // All comments, including the newly saved comment... - $post->comments; +// All comments, including the newly saved comment... +$post->comments; +``` #### Recursively Saving Models and Relationships If you would like to `save` your model and all of its associated relationships, you may use the `push` method. In this example, the `Post` model will be saved as well as its comments and the comment's authors: - $post = Post::find(1); +```php +$post = Post::find(1); - $post->comments[0]->message = 'Message'; - $post->comments[0]->author->name = 'Author Name'; +$post->comments[0]->message = 'Message'; +$post->comments[0]->author->name = 'Author Name'; - $post->push(); +$post->push(); +``` The `pushQuietly` method may be used to save a model and its associated relationships without raising any events: - $post->pushQuietly(); +```php +$post->pushQuietly(); +``` ### The `create` Method In addition to the `save` and `saveMany` methods, you may also use the `create` method, which accepts an array of attributes, creates a model, and inserts it into the database. The difference between `save` and `create` is that `save` accepts a full Eloquent model instance while `create` accepts a plain PHP `array`. The newly created model will be returned by the `create` method: - use App\Models\Post; +```php +use App\Models\Post; - $post = Post::find(1); +$post = Post::find(1); - $comment = $post->comments()->create([ - 'message' => 'A new comment.', - ]); +$comment = $post->comments()->create([ + 'message' => 'A new comment.', +]); +``` You may use the `createMany` method to create multiple related models: - $post = Post::find(1); +```php +$post = Post::find(1); - $post->comments()->createMany([ - ['message' => 'A new comment.'], - ['message' => 'Another new comment.'], - ]); +$post->comments()->createMany([ + ['message' => 'A new comment.'], + ['message' => 'Another new comment.'], +]); +``` The `createQuietly` and `createManyQuietly` methods may be used to create a model(s) without dispatching any events: - $user = User::find(1); +```php +$user = User::find(1); - $user->posts()->createQuietly([ - 'title' => 'Post title.', - ]); +$user->posts()->createQuietly([ + 'title' => 'Post title.', +]); - $user->posts()->createManyQuietly([ - ['title' => 'First post.'], - ['title' => 'Second post.'], - ]); +$user->posts()->createManyQuietly([ + ['title' => 'First post.'], + ['title' => 'Second post.'], +]); +``` You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods to [create and update models on relationships](/docs/{{version}}/eloquent#upserts). @@ -2103,19 +2357,23 @@ You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCr If you would like to assign a child model to a new parent model, you may use the `associate` method. In this example, the `User` model defines a `belongsTo` relationship to the `Account` model. This `associate` method will set the foreign key on the child model: - use App\Models\Account; +```php +use App\Models\Account; - $account = Account::find(10); +$account = Account::find(10); - $user->account()->associate($account); +$user->account()->associate($account); - $user->save(); +$user->save(); +``` To remove a parent model from a child model, you may use the `dissociate` method. This method will set the relationship's foreign key to `null`: - $user->account()->dissociate(); +```php +$user->account()->dissociate(); - $user->save(); +$user->save(); +``` ### Many to Many Relationships @@ -2125,78 +2383,100 @@ To remove a parent model from a child model, you may use the `dissociate` method Eloquent also provides methods to make working with many-to-many relationships more convenient. For example, let's imagine a user can have many roles and a role can have many users. You may use the `attach` method to attach a role to a user by inserting a record in the relationship's intermediate table: - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - $user->roles()->attach($roleId); +$user->roles()->attach($roleId); +``` When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table: - $user->roles()->attach($roleId, ['expires' => $expires]); +```php +$user->roles()->attach($roleId, ['expires' => $expires]); +``` Sometimes it may be necessary to remove a role from a user. To remove a many-to-many relationship record, use the `detach` method. The `detach` method will delete the appropriate record out of the intermediate table; however, both models will remain in the database: - // Detach a single role from the user... - $user->roles()->detach($roleId); +```php +// Detach a single role from the user... +$user->roles()->detach($roleId); - // Detach all roles from the user... - $user->roles()->detach(); +// Detach all roles from the user... +$user->roles()->detach(); +``` For convenience, `attach` and `detach` also accept arrays of IDs as input: - $user = User::find(1); +```php +$user = User::find(1); - $user->roles()->detach([1, 2, 3]); +$user->roles()->detach([1, 2, 3]); - $user->roles()->attach([ - 1 => ['expires' => $expires], - 2 => ['expires' => $expires], - ]); +$user->roles()->attach([ + 1 => ['expires' => $expires], + 2 => ['expires' => $expires], +]); +``` #### Syncing Associations You may also use the `sync` method to construct many-to-many associations. The `sync` method accepts an array of IDs to place on the intermediate table. Any IDs that are not in the given array will be removed from the intermediate table. So, after this operation is complete, only the IDs in the given array will exist in the intermediate table: - $user->roles()->sync([1, 2, 3]); +```php +$user->roles()->sync([1, 2, 3]); +``` You may also pass additional intermediate table values with the IDs: - $user->roles()->sync([1 => ['expires' => true], 2, 3]); +```php +$user->roles()->sync([1 => ['expires' => true], 2, 3]); +``` If you would like to insert the same intermediate table values with each of the synced model IDs, you may use the `syncWithPivotValues` method: - $user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]); +```php +$user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]); +``` If you do not want to detach existing IDs that are missing from the given array, you may use the `syncWithoutDetaching` method: - $user->roles()->syncWithoutDetaching([1, 2, 3]); +```php +$user->roles()->syncWithoutDetaching([1, 2, 3]); +``` #### Toggling Associations The many-to-many relationship also provides a `toggle` method which "toggles" the attachment status of the given related model IDs. If the given ID is currently attached, it will be detached. Likewise, if it is currently detached, it will be attached: - $user->roles()->toggle([1, 2, 3]); +```php +$user->roles()->toggle([1, 2, 3]); +``` You may also pass additional intermediate table values with the IDs: - $user->roles()->toggle([ - 1 => ['expires' => true], - 2 => ['expires' => true], - ]); +```php +$user->roles()->toggle([ + 1 => ['expires' => true], + 2 => ['expires' => true], +]); +``` #### Updating a Record on the Intermediate Table If you need to update an existing row in your relationship's intermediate table, you may use the `updateExistingPivot` method. This method accepts the intermediate record foreign key and an array of attributes to update: - $user = User::find(1); +```php +$user = User::find(1); - $user->roles()->updateExistingPivot($roleId, [ - 'active' => false, - ]); +$user->roles()->updateExistingPivot($roleId, [ + 'active' => false, +]); +``` ## Touching Parent Timestamps @@ -2205,30 +2485,32 @@ When a model defines a `belongsTo` or `belongsToMany` relationship to another mo For example, when a `Comment` model is updated, you may want to automatically "touch" the `updated_at` timestamp of the owning `Post` so that it is set to the current date and time. To accomplish this, you may add a `touches` property to your child model containing the names of the relationships that should have their `updated_at` timestamps updated when the child model is updated: - belongsTo(Post::class); - } + return $this->belongsTo(Post::class); } +} +``` > [!WARNING] > Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. diff --git a/eloquent-resources.md b/eloquent-resources.md index e57209bf81e..065ca219ecc 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -49,54 +49,60 @@ php artisan make:resource UserCollection 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. 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: - use App\Http\Resources\UserResource; - use App\Models\User; +```php +use App\Http\Resources\UserResource; +use App\Models\User; - Route::get('/user/{id}', function (string $id) { - return new UserResource(User::findOrFail($id)); - }); +Route::get('/user/{id}', function (string $id) { + return new UserResource(User::findOrFail($id)); +}); +``` ### Resource Collections If you are returning a collection of resources or a paginated response, you should use the `collection` method provided by your resource class when creating the resource instance in your route or controller: - use App\Http\Resources\UserResource; - use App\Models\User; +```php +use App\Http\Resources\UserResource; +use App\Models\User; - Route::get('/users', function () { - return UserResource::collection(User::all()); - }); +Route::get('/users', function () { + 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: @@ -106,69 +112,77 @@ 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: - + */ + 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 +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 When returning a resource collection from a route, Laravel resets the collection's keys so that they are in numerical order. However, you may add a `preserveKeys` property to your resource class indicating whether a collection's original keys should be preserved: - keyBy->id); - }); +Route::get('/users', function () { + return UserResource::collection(User::all()->keyBy->id); +}); +``` #### Customizing the Underlying Resource Class @@ -177,21 +191,23 @@ Typically, the `$this->collection` property of a resource collection is automati For example, `UserCollection` will attempt to map the given user instances into the `UserResource` resource. To customize this behavior, you may override the `$collects` property of your resource collection: - ## Writing Resources @@ -201,49 +217,16 @@ For example, `UserCollection` will attempt to map the given user instances into Resources only need to transform a given model into an array. So, each resource contains a `toArray` method which translates your model's attributes into an API friendly array that can be returned from your application's routes or controllers: - - */ - 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, - ]; - } - } - -Once a resource has been defined, it may be returned directly from a route or controller: - - use App\Http\Resources\UserResource; - use App\Models\User; - - Route::get('/user/{id}', function (string $id) { - return new UserResource(User::findOrFail($id)); - }); - - -#### Relationships +```php + $this->id, 'name' => $this->name, 'email' => $this->email, - 'posts' => PostResource::collection($this->posts), 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; } +} +``` + +Once a resource has been defined, it may be returned directly from a route or controller: + +```php +use App\Http\Resources\UserResource; +use App\Models\User; + +Route::get('/user/{id}', function (string $id) { + return new UserResource(User::findOrFail($id)); +}); +``` + + +#### Relationships + +If you would like to include related resources in your response, you may add them to the array returned by your resource's `toArray` method. In this example, we will use the `PostResource` resource's `collection` method to add the user's blog posts to the resource response: + +```php +use App\Http\Resources\PostResource; +use Illuminate\Http\Request; + +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'posts' => PostResource::collection($this->posts), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; +} +``` > [!NOTE] > If you would like to include relationships only when they have already been loaded, check out the documentation on [conditional relationships](#conditional-relationships). @@ -269,48 +291,54 @@ If you would like to include related resources in your response, you may add the While resources transform a single model into an array, resource collections transform a collection of models into an array. However, it is not absolutely necessary to define a resource collection class for each one of your models since all resources provide a `collection` method to generate an "ad-hoc" resource collection on the fly: - use App\Http\Resources\UserResource; - use App\Models\User; +```php +use App\Http\Resources\UserResource; +use App\Models\User; - Route::get('/users', function () { - return UserResource::collection(User::all()); - }); +Route::get('/users', function () { + return UserResource::collection(User::all()); +}); +``` However, if you need to customize the meta data returned with the collection, it is necessary to define your own resource collection: - + */ + 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', + ], + ]; } +} +``` Like singular resources, resource collections may be returned directly from routes or controllers: - use App\Http\Resources\UserCollection; - use App\Models\User; +```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()); +}); +``` ### Data Wrapping @@ -336,31 +364,33 @@ By default, your outermost resource is wrapped in a `data` key when the resource If you would like to disable the wrapping of the outermost resource, you should invoke the `withoutWrapping` method on the base `Illuminate\Http\Resources\Json\JsonResource` class. Typically, you should call this method from your `AppServiceProvider` or another [service provider](/docs/{{version}}/providers) that is loaded on every request to your application: - [!WARNING] > The `withoutWrapping` method only affects the outermost response and will not remove `data` keys that you manually add to your own resource collections. @@ -372,24 +402,26 @@ You have total freedom to determine how your resource's relationships are wrappe You may be wondering if this will cause your outermost resource to be wrapped in two `data` keys. Don't worry, Laravel will never let your resources be accidentally double-wrapped, so you don't have to be concerned about the nesting level of the resource collection you are transforming: - + */ + 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]; - } + return ['data' => $this->collection]; } +} +``` #### Data Wrapping and Pagination @@ -433,12 +465,14 @@ When returning paginated collections via a resource response, Laravel will wrap You may pass a Laravel paginator instance to the `collection` method of a resource or to a custom resource collection: - use App\Http\Resources\UserCollection; - use App\Models\User; +```php +use App\Http\Resources\UserCollection; +use App\Models\User; - Route::get('/users', function () { - return new UserCollection(User::paginate()); - }); +Route::get('/users', function () { + return new UserCollection(User::paginate()); +}); +``` Paginated responses always contain `meta` and `links` keys with information about the paginator's state: @@ -479,83 +513,95 @@ Paginated responses always contain `meta` and `links` keys with information abou If you would like to customize the information included in the `links` or `meta` keys of the pagination response, you may define a `paginationInformation` method on the resource. This method will receive the `$paginated` data and the array of `$default` information, which is an array containing the `links` and `meta` keys: - /** - * Customize the pagination information for the resource. - * - * @param \Illuminate\Http\Request $request - * @param array $paginated - * @param array $default - * @return array - */ - public function paginationInformation($request, $paginated, $default) - { - $default['links']['custom'] = 'https://example.com/'; +```php +/** + * Customize the pagination information for the resource. + * + * @param \Illuminate\Http\Request $request + * @param array $paginated + * @param array $default + * @return array + */ +public function paginationInformation($request, $paginated, $default) +{ + $default['links']['custom'] = 'https://example.com/'; - return $default; - } + return $default; +} +``` ### Conditional Attributes Sometimes you may wish to only include an attribute in a resource response if a given condition is met. For example, you may wish to only include a value if the current user is an "administrator". Laravel provides a variety of helper methods to assist you in this situation. The `when` method may be used to conditionally add an attribute to a resource response: - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'email' => $this->email, - 'secret' => $this->when($request->user()->isAdmin(), 'secret-value'), - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - ]; - } +```php +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'secret' => $this->when($request->user()->isAdmin(), 'secret-value'), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; +} +``` In this example, the `secret` key will only be returned in the final resource response if the authenticated user's `isAdmin` method returns `true`. If the method returns `false`, the `secret` key will be removed from the resource response before it is sent to the client. The `when` method allows you to expressively define your resources without resorting to conditional statements when building the array. The `when` method also accepts a closure as its second argument, allowing you to calculate the resulting value only if the given condition is `true`: - 'secret' => $this->when($request->user()->isAdmin(), function () { - return 'secret-value'; - }), +```php +'secret' => $this->when($request->user()->isAdmin(), function () { + return 'secret-value'; +}), +``` The `whenHas` method may be used to include an attribute if it is actually present on the underlying model: - 'name' => $this->whenHas('name'), +```php +'name' => $this->whenHas('name'), +``` Additionally, the `whenNotNull` method may be used to include an attribute in the resource response if the attribute is not null: - 'name' => $this->whenNotNull($this->name), +```php +'name' => $this->whenNotNull($this->name), +``` #### Merging Conditional Attributes Sometimes you may have several attributes that should only be included in the resource response based on the same condition. In this case, you may use the `mergeWhen` method to include the attributes in the response only when the given condition is `true`: - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'email' => $this->email, - $this->mergeWhen($request->user()->isAdmin(), [ - 'first-secret' => 'value', - 'second-secret' => 'value', - ]), - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - ]; - } +```php +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + $this->mergeWhen($request->user()->isAdmin(), [ + 'first-secret' => 'value', + 'second-secret' => 'value', + ]), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; +} +``` Again, if the given condition is `false`, these attributes will be removed from the resource response before it is sent to the client. @@ -569,24 +615,26 @@ In addition to conditionally loading attributes, you may conditionally include r The `whenLoaded` method may be used to conditionally load a relationship. In order to avoid unnecessarily loading relationships, this method accepts the name of the relationship instead of the relationship itself: - use App\Http\Resources\PostResource; - - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'email' => $this->email, - 'posts' => PostResource::collection($this->whenLoaded('posts')), - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - ]; - } +```php +use App\Http\Resources\PostResource; + +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'posts' => PostResource::collection($this->whenLoaded('posts')), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; +} +``` In this example, if the relationship has not been loaded, the `posts` key will be removed from the resource response before it is sent to the client. @@ -595,26 +643,30 @@ In this example, if the relationship has not been loaded, the `posts` key will b In addition to conditionally including relationships, you may conditionally include relationship "counts" on your resource responses based on if the relationship's count has been loaded on the model: - new UserResource($user->loadCount('posts')); +```php +new UserResource($user->loadCount('posts')); +``` The `whenCounted` method may be used to conditionally include a relationship's count in your resource response. This method avoids unnecessarily including the attribute if the relationships' count is not present: - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'email' => $this->email, - 'posts_count' => $this->whenCounted('posts'), - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - ]; - } +```php +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'posts_count' => $this->whenCounted('posts'), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; +} +``` In this example, if the `posts` relationship's count has not been loaded, the `posts_count` key will be removed from the resource response before it is sent to the client. @@ -632,65 +684,73 @@ Other types of aggregates, such as `avg`, `sum`, `min`, and `max` may also be co In addition to conditionally including relationship information in your resource responses, you may conditionally include data from the intermediate tables of many-to-many relationships using the `whenPivotLoaded` method. The `whenPivotLoaded` method accepts the name of the pivot table as its first argument. The second argument should be a closure that returns the value to be returned if the pivot information is available on the model: - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'expires_at' => $this->whenPivotLoaded('role_user', function () { - return $this->pivot->expires_at; - }), - ]; - } +```php +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'id' => $this->id, + 'name' => $this->name, + 'expires_at' => $this->whenPivotLoaded('role_user', function () { + return $this->pivot->expires_at; + }), + ]; +} +``` If your relationship is using a [custom intermediate table model](/docs/{{version}}/eloquent-relationships#defining-custom-intermediate-table-models), you may pass an instance of the intermediate table model as the first argument to the `whenPivotLoaded` method: - 'expires_at' => $this->whenPivotLoaded(new Membership, function () { - return $this->pivot->expires_at; - }), +```php +'expires_at' => $this->whenPivotLoaded(new Membership, function () { + return $this->pivot->expires_at; +}), +``` If your intermediate table is using an accessor other than `pivot`, you may use the `whenPivotLoadedAs` method: - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'name' => $this->name, - 'expires_at' => $this->whenPivotLoadedAs('subscription', 'role_user', function () { - return $this->subscription->expires_at; - }), - ]; - } +```php +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'id' => $this->id, + 'name' => $this->name, + 'expires_at' => $this->whenPivotLoadedAs('subscription', 'role_user', function () { + return $this->subscription->expires_at; + }), + ]; +} +``` ### Adding Meta Data Some JSON API standards require the addition of meta data to your resource and resource collections responses. This often includes things like `links` to the resource or related resources, or meta data about the resource itself. If you need to return additional meta data about a resource, include it in your `toArray` method. For example, you might include `links` information when transforming a resource collection: - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'data' => $this->collection, - 'links' => [ - 'self' => 'link-value', - ], - ]; - } +```php +/** + * Transform the resource into an array. + * + * @return array + */ +public function toArray(Request $request): array +{ + return [ + 'data' => $this->collection, + 'links' => [ + 'self' => 'link-value', + ], + ]; +} +``` When returning additional meta data from your resources, you never have to worry about accidentally overriding the `links` or `meta` keys that are automatically added by Laravel when returning paginated responses. Any additional `links` you define will be merged with the links provided by the paginator. @@ -699,101 +759,111 @@ When returning additional meta data from your resources, you never have to worry Sometimes you may wish to only include certain meta data with a resource response if the resource is the outermost resource being returned. Typically, this includes meta information about the response as a whole. To define this meta data, add a `with` method to your resource class. This method should return an array of meta data to be included with the resource response only when the resource is the outermost resource being transformed: - + */ + public function toArray(Request $request): array { - /** - * Transform the resource collection into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return parent::toArray($request); - } + return parent::toArray($request); + } - /** - * Get additional data that should be returned with the resource array. - * - * @return array - */ - public function with(Request $request): array - { - return [ - 'meta' => [ - 'key' => 'value', - ], - ]; - } + /** + * Get additional data that should be returned with the resource array. + * + * @return array + */ + public function with(Request $request): array + { + return [ + 'meta' => [ + 'key' => 'value', + ], + ]; } +} +``` #### Adding Meta Data When Constructing Resources You may also add top-level data when constructing resource instances in your route or controller. The `additional` method, which is available on all resources, accepts an array of data that should be added to the resource response: - return (new UserCollection(User::all()->load('roles'))) - ->additional(['meta' => [ - 'key' => 'value', - ]]); +```php +return (new UserCollection(User::all()->load('roles'))) + ->additional(['meta' => [ + 'key' => 'value', + ]]); +``` ## Resource Responses As you have already read, resources may be returned directly from routes and controllers: - use App\Http\Resources\UserResource; - use App\Models\User; +```php +use App\Http\Resources\UserResource; +use App\Models\User; - Route::get('/user/{id}', function (string $id) { - return new UserResource(User::findOrFail($id)); - }); +Route::get('/user/{id}', function (string $id) { + return new UserResource(User::findOrFail($id)); +}); +``` However, sometimes you may need to customize the outgoing HTTP response before it is sent to the client. There are two ways to accomplish this. First, you may chain the `response` method onto the resource. This method will return an `Illuminate\Http\JsonResponse` instance, giving you full control over the response's headers: - use App\Http\Resources\UserResource; - use App\Models\User; - - Route::get('/user', function () { - return (new UserResource(User::find(1))) - ->response() - ->header('X-Value', 'True'); - }); +```php +use App\Http\Resources\UserResource; +use App\Models\User; + +Route::get('/user', function () { + return (new UserResource(User::find(1))) + ->response() + ->header('X-Value', 'True'); +}); +``` Alternatively, you may define a `withResponse` method within the resource itself. This method will be called when the resource is returned as the outermost resource in a response: - + */ + public function toArray(Request $request): array { - /** - * Transform the resource into an array. - * - * @return array - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - ]; - } + return [ + 'id' => $this->id, + ]; + } - /** - * Customize the outgoing response for the resource. - */ - public function withResponse(Request $request, JsonResponse $response): void - { - $response->header('X-Value', 'True'); - } + /** + * Customize the outgoing response for the resource. + */ + public function withResponse(Request $request, JsonResponse $response): void + { + $response->header('X-Value', 'True'); } +} +``` diff --git a/eloquent-serialization.md b/eloquent-serialization.md index 45f4df9cce7..5488da39372 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -24,46 +24,58 @@ When building APIs using Laravel, you will often need to convert your models and To convert a model and its loaded [relationships](/docs/{{version}}/eloquent-relationships) to an array, you should use the `toArray` method. This method is recursive, so all attributes and all relations (including the relations of relations) will be converted to arrays: - use App\Models\User; +```php +use App\Models\User; - $user = User::with('roles')->first(); +$user = User::with('roles')->first(); - return $user->toArray(); +return $user->toArray(); +``` The `attributesToArray` method may be used to convert a model's attributes to an array but not its relationships: - $user = User::first(); +```php +$user = User::first(); - return $user->attributesToArray(); +return $user->attributesToArray(); +``` You may also convert entire [collections](/docs/{{version}}/eloquent-collections) of models to arrays by calling the `toArray` method on the collection instance: - $users = User::all(); +```php +$users = User::all(); - return $users->toArray(); +return $users->toArray(); +``` ### Serializing to JSON To convert a model to JSON, you should use the `toJson` method. Like `toArray`, the `toJson` method is recursive, so all attributes and relations will be converted to JSON. You may also specify any JSON encoding options that are [supported by PHP](https://secure.php.net/manual/en/function.json-encode.php): - use App\Models\User; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - return $user->toJson(); +return $user->toJson(); - return $user->toJson(JSON_PRETTY_PRINT); +return $user->toJson(JSON_PRETTY_PRINT); +``` Alternatively, you may cast a model or collection to a string, which will automatically call the `toJson` method on the model or collection: - return (string) User::find(1); +```php +return (string) User::find(1); +``` Since models and collections are converted to JSON when cast to a string, you can return Eloquent objects directly from your application's routes or controllers. Laravel will automatically serialize your Eloquent models and collections to JSON when they are returned from routes or controllers: - Route::get('/users', function () { - return User::all(); - }); +```php +Route::get('/users', function () { + return User::all(); +}); +``` #### Relationships @@ -75,102 +87,116 @@ When an Eloquent model is converted to JSON, its loaded relationships will autom Sometimes you may wish to limit the attributes, such as passwords, that are included in your model's array or JSON representation. To do so, add a `$hidden` property to your model. Attributes that are listed in the `$hidden` property's array will not be included in the serialized representation of your model: - - */ - protected $hidden = ['password']; - } +class User extends Model +{ + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = ['password']; +} +``` > [!NOTE] > To hide relationships, add the relationship's method name to your Eloquent model's `$hidden` property. Alternatively, you may use the `visible` property to define an "allow list" of attributes that should be included in your model's array and JSON representation. All attributes that are not present in the `$visible` array will be hidden when the model is converted to an array or JSON: - #### Temporarily Modifying Attribute Visibility If you would like to make some typically hidden attributes visible on a given model instance, you may use the `makeVisible` method. The `makeVisible` method returns the model instance: - return $user->makeVisible('attribute')->toArray(); +```php +return $user->makeVisible('attribute')->toArray(); +``` Likewise, if you would like to hide some attributes that are typically visible, you may use the `makeHidden` method. - return $user->makeHidden('attribute')->toArray(); +```php +return $user->makeHidden('attribute')->toArray(); +``` If you wish to temporarily override all of the visible or hidden attributes, you may use the `setVisible` and `setHidden` methods respectively: - return $user->setVisible(['id', 'name'])->toArray(); +```php +return $user->setVisible(['id', 'name'])->toArray(); - return $user->setHidden(['email', 'password', 'remember_token'])->toArray(); +return $user->setHidden(['email', 'password', 'remember_token'])->toArray(); +``` ## Appending Values to JSON Occasionally, when converting models to arrays or JSON, you may wish to add attributes that do not have a corresponding column in your database. To do so, first define an [accessor](/docs/{{version}}/eloquent-mutators) for the value: - 'yes', - ); - } + return new Attribute( + get: fn () => 'yes', + ); } +} +``` If you would like the accessor to always be appended to your model's array and JSON representations, you may add the attribute name to the `appends` property of your model. Note that attribute names are typically referenced using their "snake case" serialized representation, even though the accessor's PHP method is defined using "camel case": - append('is_admin')->toArray(); +```php +return $user->append('is_admin')->toArray(); - return $user->setAppends(['is_admin'])->toArray(); +return $user->setAppends(['is_admin'])->toArray(); +``` ## Date Serialization @@ -191,23 +219,27 @@ At runtime, you may instruct a model instance to append additional attributes us You may customize the default serialization format by overriding the `serializeDate` method. This method does not affect how your dates are formatted for storage in the database: - /** - * Prepare a date for array / JSON serialization. - */ - protected function serializeDate(DateTimeInterface $date): string - { - return $date->format('Y-m-d'); - } +```php +/** + * Prepare a date for array / JSON serialization. + */ +protected function serializeDate(DateTimeInterface $date): string +{ + return $date->format('Y-m-d'); +} +``` #### Customizing the Date Format per Attribute You may customize the serialization format of individual Eloquent date attributes by specifying the date format in the model's [cast declarations](/docs/{{version}}/eloquent-mutators#attribute-casting): - protected function casts(): array - { - return [ - 'birthday' => 'date:Y-m-d', - 'joined_at' => 'datetime:Y-m-d H:00', - ]; - } +```php +protected function casts(): array +{ + return [ + 'birthday' => 'date:Y-m-d', + 'joined_at' => 'datetime:Y-m-d H:00', + ]; +} +``` diff --git a/eloquent.md b/eloquent.md index 74504364dda..ab6c8ff076d 100644 --- a/eloquent.md +++ b/eloquent.md @@ -114,16 +114,18 @@ php artisan model:show Flight Models generated by the `make:model` command will be placed in the `app/Models` directory. Let's examine a basic model class and discuss some of Eloquent's key conventions: - ### Table Names @@ -132,70 +134,78 @@ After glancing at the example above, you may have noticed that we did not tell E If your model's corresponding database table does not fit this convention, you may manually specify the model's table name by defining a `table` property on the model: - ### Primary Keys Eloquent will also assume that each model's corresponding database table has a primary key column named `id`. If necessary, you may define a protected `$primaryKey` property on your model to specify a different column that serves as your model's primary key: - #### "Composite" Primary Keys @@ -209,157 +219,175 @@ Instead of using auto-incrementing integers as your Eloquent model's primary key If you would like a model to use a UUID key instead of an auto-incrementing integer key, you may use the `Illuminate\Database\Eloquent\Concerns\HasUuids` trait on the model. Of course, you should ensure that the model has a [UUID equivalent primary key column](/docs/{{version}}/migrations#column-method-uuid): - use Illuminate\Database\Eloquent\Concerns\HasUuids; - use Illuminate\Database\Eloquent\Model; +```php +use Illuminate\Database\Eloquent\Concerns\HasUuids; +use Illuminate\Database\Eloquent\Model; - class Article extends Model - { - use HasUuids; +class Article extends Model +{ + use HasUuids; - // ... - } + // ... +} - $article = Article::create(['title' => 'Traveling to Europe']); +$article = Article::create(['title' => 'Traveling to Europe']); - $article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5" +$article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5" +``` By default, The `HasUuids` trait will generate ["ordered" UUIDs](/docs/{{version}}/strings#method-str-ordered-uuid) for your models. These UUIDs are more efficient for indexed database storage because they can be sorted lexicographically. You can override the UUID generation process for a given model by defining a `newUniqueId` method on the model. In addition, you may specify which columns should receive UUIDs by defining a `uniqueIds` method on the model: - use Ramsey\Uuid\Uuid; +```php +use Ramsey\Uuid\Uuid; - /** - * Generate a new UUID for the model. - */ - public function newUniqueId(): string - { - return (string) Uuid::uuid4(); - } +/** + * Generate a new UUID for the model. + */ +public function newUniqueId(): string +{ + return (string) Uuid::uuid4(); +} - /** - * Get the columns that should receive a unique identifier. - * - * @return array - */ - public function uniqueIds(): array - { - return ['id', 'discount_code']; - } +/** + * Get the columns that should receive a unique identifier. + * + * @return array + */ +public function uniqueIds(): array +{ + return ['id', 'discount_code']; +} +``` 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 +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']); +$article = Article::create(['title' => 'Traveling to Asia']); - $article->id; // "01gd4d3tgrrfqeda94gdbtdk5c" +$article->id; // "01gd4d3tgrrfqeda94gdbtdk5c" +``` ### Timestamps By default, Eloquent expects `created_at` and `updated_at` columns to exist on your model's corresponding database table. Eloquent will automatically set these column's values when models are created or updated. If you do not want these columns to be automatically managed by Eloquent, you should define a `$timestamps` property on your model with a value of `false`: - $post->increment('reads')); +```php +Model::withoutTimestamps(fn () => $post->increment('reads')); +``` ### Database Connections By default, all Eloquent models will use the default database connection that is configured for your application. If you would like to specify a different connection that should be used when interacting with a particular model, you should define a `$connection` property on the model: - ### Default Attribute Values By default, a newly instantiated model instance will not contain any attribute values. If you would like to define the default values for some of your model's attributes, you may define an `$attributes` property on your model. Attribute values placed in the `$attributes` array should be in their raw, "storable" format as if they were just read from the database: - '[]', - 'delayed' => false, - ]; - } +class Flight extends Model +{ + /** + * The model's default values for attributes. + * + * @var array + */ + protected $attributes = [ + 'options' => '[]', + 'delayed' => false, + ]; +} +``` ### Configuring Eloquent Strictness @@ -391,21 +419,25 @@ Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction()); Once you have created a model and [its associated database table](/docs/{{version}}/migrations#generating-migrations), you are ready to start retrieving data from your database. You can think of each Eloquent model as a powerful [query builder](/docs/{{version}}/queries) allowing you to fluently query the database table associated with the model. The model's `all` method will retrieve all of the records from the model's associated database table: - use App\Models\Flight; +```php +use App\Models\Flight; - foreach (Flight::all() as $flight) { - echo $flight->name; - } +foreach (Flight::all() as $flight) { + echo $flight->name; +} +``` #### Building Queries The Eloquent `all` method will return all of the results in the model's table. However, since each Eloquent model serves as a [query builder](/docs/{{version}}/queries), you may add additional constraints to queries and then invoke the `get` method to retrieve the results: - $flights = Flight::where('active', 1) - ->orderBy('name') - ->take(10) - ->get(); +```php +$flights = Flight::where('active', 1) + ->orderBy('name') + ->take(10) + ->get(); +``` > [!NOTE] > Since Eloquent models are query builders, you should review all of the methods provided by Laravel's [query builder](/docs/{{version}}/queries). You may use any of these methods when writing your Eloquent queries. @@ -415,19 +447,23 @@ The Eloquent `all` method will return all of the results in the model's table. H If you already have an instance of an Eloquent model that was retrieved from the database, you can "refresh" the model using the `fresh` and `refresh` methods. The `fresh` method will re-retrieve the model from the database. The existing model instance will not be affected: - $flight = Flight::where('number', 'FR 900')->first(); +```php +$flight = Flight::where('number', 'FR 900')->first(); - $freshFlight = $flight->fresh(); +$freshFlight = $flight->fresh(); +``` The `refresh` method will re-hydrate the existing model using fresh data from the database. In addition, all of its loaded relationships will be refreshed as well: - $flight = Flight::where('number', 'FR 900')->first(); +```php +$flight = Flight::where('number', 'FR 900')->first(); - $flight->number = 'FR 456'; +$flight->number = 'FR 456'; - $flight->refresh(); +$flight->refresh(); - $flight->number; // "FR 900" +$flight->number; // "FR 900" +``` ### Collections @@ -565,69 +601,81 @@ Eloquent also offers advanced subquery support, which allows you to pull informa Using the subquery functionality available to the query builder's `select` and `addSelect` methods, we can select all of the `destinations` and the name of the flight that most recently arrived at that destination using a single query: - use App\Models\Destination; - use App\Models\Flight; +```php +use App\Models\Destination; +use App\Models\Flight; - return Destination::addSelect(['last_flight' => Flight::select('name') - ->whereColumn('destination_id', 'destinations.id') - ->orderByDesc('arrived_at') - ->limit(1) - ])->get(); +return Destination::addSelect(['last_flight' => Flight::select('name') + ->whereColumn('destination_id', 'destinations.id') + ->orderByDesc('arrived_at') + ->limit(1) +])->get(); +``` #### Subquery Ordering In addition, the query builder's `orderBy` function supports subqueries. Continuing to use our flight example, we may use this functionality to sort all destinations based on when the last flight arrived at that destination. Again, this may be done while executing a single database query: - return Destination::orderByDesc( - Flight::select('arrived_at') - ->whereColumn('destination_id', 'destinations.id') - ->orderByDesc('arrived_at') - ->limit(1) - )->get(); +```php +return Destination::orderByDesc( + Flight::select('arrived_at') + ->whereColumn('destination_id', 'destinations.id') + ->orderByDesc('arrived_at') + ->limit(1) +)->get(); +``` ## Retrieving Single Models / Aggregates In addition to retrieving all of the records matching a given query, you may also retrieve single records using the `find`, `first`, or `firstWhere` methods. Instead of returning a collection of models, these methods return a single model instance: - use App\Models\Flight; +```php +use App\Models\Flight; - // Retrieve a model by its primary key... - $flight = Flight::find(1); +// Retrieve a model by its primary key... +$flight = Flight::find(1); - // Retrieve the first model matching the query constraints... - $flight = Flight::where('active', 1)->first(); +// Retrieve the first model matching the query constraints... +$flight = Flight::where('active', 1)->first(); - // Alternative to retrieving the first model matching the query constraints... - $flight = Flight::firstWhere('active', 1); +// Alternative to retrieving the first model matching the query constraints... +$flight = Flight::firstWhere('active', 1); +``` Sometimes you may wish to perform some other action if no results are found. The `findOr` and `firstOr` methods will return a single model instance or, if no results are found, execute the given closure. The value returned by the closure will be considered the result of the method: - $flight = Flight::findOr(1, function () { - // ... - }); +```php +$flight = Flight::findOr(1, function () { + // ... +}); - $flight = Flight::where('legs', '>', 3)->firstOr(function () { - // ... - }); +$flight = Flight::where('legs', '>', 3)->firstOr(function () { + // ... +}); +``` #### Not Found Exceptions Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes or controllers. The `findOrFail` and `firstOrFail` methods will retrieve the first result of the query; however, if no result is found, an `Illuminate\Database\Eloquent\ModelNotFoundException` will be thrown: - $flight = Flight::findOrFail(1); +```php +$flight = Flight::findOrFail(1); - $flight = Flight::where('legs', '>', 3)->firstOrFail(); +$flight = Flight::where('legs', '>', 3)->firstOrFail(); +``` If the `ModelNotFoundException` is not caught, a 404 HTTP response is automatically sent back to the client: - use App\Models\Flight; +```php +use App\Models\Flight; - Route::get('/api/flights/{id}', function (string $id) { - return Flight::findOrFail($id); - }); +Route::get('/api/flights/{id}', function (string $id) { + return Flight::findOrFail($id); +}); +``` ### Retrieving or Creating Models @@ -636,38 +684,42 @@ The `firstOrCreate` method will attempt to locate a database record using the gi The `firstOrNew` method, like `firstOrCreate`, will attempt to locate a record in the database matching the given attributes. However, if a model is not found, a new model instance will be returned. Note that the model returned by `firstOrNew` has not yet been persisted to the database. You will need to manually call the `save` method to persist it: - use App\Models\Flight; - - // Retrieve flight by name or create it if it doesn't exist... - $flight = Flight::firstOrCreate([ - 'name' => 'London to Paris' - ]); - - // Retrieve flight by name or create it with the name, delayed, and arrival_time attributes... - $flight = Flight::firstOrCreate( - ['name' => 'London to Paris'], - ['delayed' => 1, 'arrival_time' => '11:30'] - ); - - // Retrieve flight by name or instantiate a new Flight instance... - $flight = Flight::firstOrNew([ - 'name' => 'London to Paris' - ]); +```php +use App\Models\Flight; - // Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes... - $flight = Flight::firstOrNew( - ['name' => 'Tokyo to Sydney'], - ['delayed' => 1, 'arrival_time' => '11:30'] - ); +// Retrieve flight by name or create it if it doesn't exist... +$flight = Flight::firstOrCreate([ + 'name' => 'London to Paris' +]); + +// Retrieve flight by name or create it with the name, delayed, and arrival_time attributes... +$flight = Flight::firstOrCreate( + ['name' => 'London to Paris'], + ['delayed' => 1, 'arrival_time' => '11:30'] +); + +// Retrieve flight by name or instantiate a new Flight instance... +$flight = Flight::firstOrNew([ + 'name' => 'London to Paris' +]); + +// Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes... +$flight = Flight::firstOrNew( + ['name' => 'Tokyo to Sydney'], + ['delayed' => 1, 'arrival_time' => '11:30'] +); +``` ### Retrieving Aggregates When interacting with Eloquent models, you may also use the `count`, `sum`, `max`, and other [aggregate methods](/docs/{{version}}/queries#aggregates) provided by the Laravel [query builder](/docs/{{version}}/queries). As you might expect, these methods return a scalar value instead of an Eloquent model instance: - $count = Flight::where('active', 1)->count(); +```php +$count = Flight::where('active', 1)->count(); - $max = Flight::where('active', 1)->max('price'); +$max = Flight::where('active', 1)->max('price'); +``` ## Inserting and Updating Models @@ -677,43 +729,47 @@ When interacting with Eloquent models, you may also use the `count`, `sum`, `max Of course, when using Eloquent, we don't only need to retrieve models from the database. We also need to insert new records. Thankfully, Eloquent makes it simple. To insert a new record into the database, you should instantiate a new model instance and set attributes on the model. Then, call the `save` method on the model instance: - name = $request->name; + $flight->name = $request->name; - $flight->save(); + $flight->save(); - return redirect('/flights'); - } + return redirect('/flights'); } +} +``` In this example, we assign the `name` field from the incoming HTTP request to the `name` attribute of the `App\Models\Flight` model instance. When we call the `save` method, a record will be inserted into the database. The model's `created_at` and `updated_at` timestamps will automatically be set when the `save` method is called, so there is no need to set them manually. Alternatively, you may use the `create` method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the `create` method: - use App\Models\Flight; +```php +use App\Models\Flight; - $flight = Flight::create([ - 'name' => 'London to Paris', - ]); +$flight = Flight::create([ + 'name' => 'London to Paris', +]); +``` However, before using the `create` method, you will need to specify either a `fillable` or `guarded` property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default. To learn more about mass assignment, please consult the [mass assignment documentation](#mass-assignment). @@ -722,31 +778,37 @@ However, before using the `create` method, you will need to specify either a `fi The `save` method may also be used to update models that already exist in the database. To update a model, you should retrieve it and set any attributes you wish to update. Then, you should call the model's `save` method. Again, the `updated_at` timestamp will automatically be updated, so there is no need to manually set its value: - use App\Models\Flight; +```php +use App\Models\Flight; - $flight = Flight::find(1); +$flight = Flight::find(1); - $flight->name = 'Paris to London'; +$flight->name = 'Paris to London'; - $flight->save(); +$flight->save(); +``` Occasionally, you may need to update an existing model or create a new model if no matching model exists. Like the `firstOrCreate` method, the `updateOrCreate` method persists the model, so there's no need to manually call the `save` method. In the example below, if a flight exists with a `departure` location of `Oakland` and a `destination` location of `San Diego`, its `price` and `discounted` columns will be updated. If no such flight exists, a new flight will be created which has the attributes resulting from merging the first argument array with the second argument array: - $flight = Flight::updateOrCreate( - ['departure' => 'Oakland', 'destination' => 'San Diego'], - ['price' => 99, 'discounted' => 1] - ); +```php +$flight = Flight::updateOrCreate( + ['departure' => 'Oakland', 'destination' => 'San Diego'], + ['price' => 99, 'discounted' => 1] +); +``` #### Mass Updates Updates can also be performed against models that match a given query. In this example, all flights that are `active` and have a `destination` of `San Diego` will be marked as delayed: - Flight::where('active', 1) - ->where('destination', 'San Diego') - ->update(['delayed' => 1]); +```php +Flight::where('active', 1) + ->where('destination', 'San Diego') + ->update(['delayed' => 1]); +``` The `update` method expects an array of column and value pairs representing the columns that should be updated. The `update` method returns the number of affected rows. @@ -760,72 +822,80 @@ Eloquent provides the `isDirty`, `isClean`, and `wasChanged` methods to examine The `isDirty` method determines if any of the model's attributes have been changed since the model was retrieved. You may pass a specific attribute name or an array of attributes to the `isDirty` method to determine if any of the attributes are "dirty". The `isClean` method will determine if an attribute has remained unchanged since the model was retrieved. This method also accepts an optional attribute argument: - use App\Models\User; +```php +use App\Models\User; - $user = User::create([ - 'first_name' => 'Taylor', - 'last_name' => 'Otwell', - 'title' => 'Developer', - ]); +$user = User::create([ + 'first_name' => 'Taylor', + 'last_name' => 'Otwell', + 'title' => 'Developer', +]); - $user->title = 'Painter'; +$user->title = 'Painter'; - $user->isDirty(); // true - $user->isDirty('title'); // true - $user->isDirty('first_name'); // false - $user->isDirty(['first_name', 'title']); // true +$user->isDirty(); // true +$user->isDirty('title'); // true +$user->isDirty('first_name'); // false +$user->isDirty(['first_name', 'title']); // true - $user->isClean(); // false - $user->isClean('title'); // false - $user->isClean('first_name'); // true - $user->isClean(['first_name', 'title']); // false +$user->isClean(); // false +$user->isClean('title'); // false +$user->isClean('first_name'); // true +$user->isClean(['first_name', 'title']); // false - $user->save(); +$user->save(); - $user->isDirty(); // false - $user->isClean(); // true +$user->isDirty(); // false +$user->isClean(); // true +``` The `wasChanged` method determines if any attributes were changed when the model was last saved within the current request cycle. If needed, you may pass an attribute name to see if a particular attribute was changed: - $user = User::create([ - 'first_name' => 'Taylor', - 'last_name' => 'Otwell', - 'title' => 'Developer', - ]); +```php +$user = User::create([ + 'first_name' => 'Taylor', + 'last_name' => 'Otwell', + 'title' => 'Developer', +]); - $user->title = 'Painter'; +$user->title = 'Painter'; - $user->save(); +$user->save(); - $user->wasChanged(); // true - $user->wasChanged('title'); // true - $user->wasChanged(['title', 'slug']); // true - $user->wasChanged('first_name'); // false - $user->wasChanged(['first_name', 'title']); // true +$user->wasChanged(); // true +$user->wasChanged('title'); // true +$user->wasChanged(['title', 'slug']); // true +$user->wasChanged('first_name'); // false +$user->wasChanged(['first_name', 'title']); // true +``` The `getOriginal` method returns an array containing the original attributes of the model regardless of any changes to the model since it was retrieved. If needed, you may pass a specific attribute name to get the original value of a particular attribute: - $user = User::find(1); +```php +$user = User::find(1); - $user->name; // John - $user->email; // john@example.com +$user->name; // John +$user->email; // john@example.com - $user->name = "Jack"; - $user->name; // Jack +$user->name = "Jack"; +$user->name; // Jack - $user->getOriginal('name'); // John - $user->getOriginal(); // Array of original attributes... +$user->getOriginal('name'); // John +$user->getOriginal(); // Array of original attributes... +``` ### Mass Assignment You may use the `create` method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the method: - use App\Models\Flight; +```php +use App\Models\Flight; - $flight = Flight::create([ - 'name' => 'London to Paris', - ]); +$flight = Flight::create([ + 'name' => 'London to Paris', +]); +``` However, before using the `create` method, you will need to specify either a `fillable` or `guarded` property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default. @@ -833,55 +903,65 @@ A mass assignment vulnerability occurs when a user passes an unexpected HTTP req So, to get started, you should define which model attributes you want to make mass assignable. You may do this using the `$fillable` property on the model. For example, let's make the `name` attribute of our `Flight` model mass assignable: - - */ - protected $fillable = ['name']; - } +class Flight extends Model +{ + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = ['name']; +} +``` Once you have specified which attributes are mass assignable, you may use the `create` method to insert a new record in the database. The `create` method returns the newly created model instance: - $flight = Flight::create(['name' => 'London to Paris']); +```php +$flight = Flight::create(['name' => 'London to Paris']); +``` If you already have a model instance, you may use the `fill` method to populate it with an array of attributes: - $flight->fill(['name' => 'Amsterdam to Frankfurt']); +```php +$flight->fill(['name' => 'Amsterdam to Frankfurt']); +``` #### Mass Assignment and JSON Columns When assigning JSON columns, each column's mass assignable key must be specified in your model's `$fillable` array. For security, Laravel does not support updating nested JSON attributes when using the `guarded` property: - /** - * The attributes that are mass assignable. - * - * @var array - */ - protected $fillable = [ - 'options->enabled', - ]; +```php +/** + * The attributes that are mass assignable. + * + * @var array + */ +protected $fillable = [ + 'options->enabled', +]; +``` #### Allowing Mass Assignment If you would like to make all of your attributes mass assignable, you may define your model's `$guarded` property as an empty array. If you choose to unguard your model, you should take special care to always hand-craft the arrays passed to Eloquent's `fill`, `create`, and `update` methods: - /** - * The attributes that aren't mass assignable. - * - * @var array|bool - */ - protected $guarded = []; +```php +/** + * The attributes that aren't mass assignable. + * + * @var array|bool + */ +protected $guarded = []; +``` #### Mass Assignment Exceptions @@ -890,25 +970,29 @@ By default, attributes that are not included in the `$fillable` array are silent If you wish, you may instruct Laravel to throw an exception when attempting to fill an unfillable attribute by invoking the `preventSilentlyDiscardingAttributes` method. Typically, this method should be invoked in the `boot` method of your application's `AppServiceProvider` class: - use Illuminate\Database\Eloquent\Model; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Model::preventSilentlyDiscardingAttributes($this->app->isLocal()); - } +```php +use Illuminate\Database\Eloquent\Model; - -### Upserts +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Model::preventSilentlyDiscardingAttributes($this->app->isLocal()); +} +``` + + +### Upserts Eloquent's `upsert` method may be used to update or create records in a single, atomic operation. 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 the columns that should be updated if a matching record already exists in the database. The `upsert` method will automatically set the `created_at` and `updated_at` timestamps if timestamps are enabled on the model: - Flight::upsert([ - ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], - ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] - ], uniqueBy: ['departure', 'destination'], update: ['price']); +```php +Flight::upsert([ + ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], + ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] +], uniqueBy: ['departure', 'destination'], update: ['price']); +``` > [!WARNING] > All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MariaDB and MySQL database drivers ignore the second argument of the `upsert` method and always use the "primary" and "unique" indexes of the table to detect existing records. @@ -918,28 +1002,34 @@ Eloquent's `upsert` method may be used to update or create records in a single, To delete a model, you may call the `delete` method on the model instance: - use App\Models\Flight; +```php +use App\Models\Flight; - $flight = Flight::find(1); +$flight = Flight::find(1); - $flight->delete(); +$flight->delete(); +``` #### Deleting an Existing Model by its Primary Key In the example above, we are retrieving the model from the database before calling the `delete` method. However, if you know the primary key of the model, you may delete the model without explicitly retrieving it by calling the `destroy` method. In addition to accepting the single primary key, the `destroy` method will accept multiple primary keys, an array of primary keys, or a [collection](/docs/{{version}}/collections) of primary keys: - Flight::destroy(1); +```php +Flight::destroy(1); - Flight::destroy(1, 2, 3); +Flight::destroy(1, 2, 3); - Flight::destroy([1, 2, 3]); +Flight::destroy([1, 2, 3]); - Flight::destroy(collect([1, 2, 3])); +Flight::destroy(collect([1, 2, 3])); +``` If you are utilizing [soft deleting models](#soft-deleting), you may permanently delete models via the `forceDestroy` method: - Flight::forceDestroy(1); +```php +Flight::forceDestroy(1); +``` > [!WARNING] > The `destroy` method loads each model individually and calls the `delete` method so that the `deleting` and `deleted` events are properly dispatched for each model. @@ -949,11 +1039,15 @@ If you are utilizing [soft deleting models](#soft-deleting), you may permanently Of course, you may build an Eloquent query to delete all models matching your query's criteria. In this example, we will delete all flights that are marked as inactive. Like mass updates, mass deletes will not dispatch model events for the models that are deleted: - $deleted = Flight::where('active', 0)->delete(); +```php +$deleted = Flight::where('active', 0)->delete(); +``` To delete all models in a table, you should execute a query without adding any conditions: - $deleted = Flight::query()->delete(); +```php +$deleted = Flight::query()->delete(); +``` > [!WARNING] > When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement. @@ -963,69 +1057,85 @@ To delete all models in a table, you should execute a query without adding any c In addition to actually removing records from your database, Eloquent can also "soft delete" models. When models are soft deleted, they are not actually removed from your database. Instead, a `deleted_at` attribute is set on the model indicating the date and time at which the model was "deleted". To enable soft deletes for a model, add the `Illuminate\Database\Eloquent\SoftDeletes` trait to the model: - [!NOTE] > The `SoftDeletes` trait will automatically cast the `deleted_at` attribute to a `DateTime` / `Carbon` instance for you. You should also add the `deleted_at` column to your database table. The Laravel [schema builder](/docs/{{version}}/migrations) contains a helper method to create this column: - use Illuminate\Database\Schema\Blueprint; - use Illuminate\Support\Facades\Schema; +```php +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; - Schema::table('flights', function (Blueprint $table) { - $table->softDeletes(); - }); +Schema::table('flights', function (Blueprint $table) { + $table->softDeletes(); +}); - Schema::table('flights', function (Blueprint $table) { - $table->dropSoftDeletes(); - }); +Schema::table('flights', function (Blueprint $table) { + $table->dropSoftDeletes(); +}); +``` Now, when you call the `delete` method on the model, the `deleted_at` column will be set to the current date and time. However, the model's database record will be left in the table. When querying a model that uses soft deletes, the soft deleted models will automatically be excluded from all query results. To determine if a given model instance has been soft deleted, you may use the `trashed` method: - if ($flight->trashed()) { - // ... - } +```php +if ($flight->trashed()) { + // ... +} +``` #### Restoring Soft Deleted Models Sometimes you may wish to "un-delete" a soft deleted model. To restore a soft deleted model, you may call the `restore` method on a model instance. The `restore` method will set the model's `deleted_at` column to `null`: - $flight->restore(); +```php +$flight->restore(); +``` You may also use the `restore` method in a query to restore multiple models. Again, like other "mass" operations, this will not dispatch any model events for the models that are restored: - Flight::withTrashed() - ->where('airline_id', 1) - ->restore(); +```php +Flight::withTrashed() + ->where('airline_id', 1) + ->restore(); +``` The `restore` method may also be used when building [relationship](/docs/{{version}}/eloquent-relationships) queries: - $flight->history()->restore(); +```php +$flight->history()->restore(); +``` #### Permanently Deleting Models Sometimes you may need to truly remove a model from your database. You may use the `forceDelete` method to permanently remove a soft deleted model from the database table: - $flight->forceDelete(); +```php +$flight->forceDelete(); +``` You may also use the `forceDelete` method when building Eloquent relationship queries: - $flight->history()->forceDelete(); +```php +$flight->history()->forceDelete(); +``` ### Querying Soft Deleted Models @@ -1035,78 +1145,94 @@ You may also use the `forceDelete` method when building Eloquent relationship qu As noted above, soft deleted models will automatically be excluded from query results. However, you may force soft deleted models to be included in a query's results by calling the `withTrashed` method on the query: - use App\Models\Flight; +```php +use App\Models\Flight; - $flights = Flight::withTrashed() - ->where('account_id', 1) - ->get(); +$flights = Flight::withTrashed() + ->where('account_id', 1) + ->get(); +``` The `withTrashed` method may also be called when building a [relationship](/docs/{{version}}/eloquent-relationships) query: - $flight->history()->withTrashed()->get(); +```php +$flight->history()->withTrashed()->get(); +``` #### Retrieving Only Soft Deleted Models The `onlyTrashed` method will retrieve **only** soft deleted models: - $flights = Flight::onlyTrashed() - ->where('airline_id', 1) - ->get(); +```php +$flights = Flight::onlyTrashed() + ->where('airline_id', 1) + ->get(); +``` ## Pruning Models Sometimes you may want to periodically delete models that are no longer needed. To accomplish this, you may add the `Illuminate\Database\Eloquent\Prunable` or `Illuminate\Database\Eloquent\MassPrunable` trait to the models you would like to periodically prune. After adding one of the traits to the model, implement a `prunable` method which returns an Eloquent query builder that resolves the models that are no longer needed: - subMonth()); - } - } +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Prunable; -When marking models as `Prunable`, you may also define a `pruning` method on the model. This method will be called before the model is deleted. This method can be useful for deleting any additional resources associated with the model, such as stored files, before the model is permanently removed from the database: +class Flight extends Model +{ + use Prunable; /** - * Prepare the model for pruning. + * Get the prunable model query. */ - protected function pruning(): void + public function prunable(): Builder { - // ... + return static::where('created_at', '<=', now()->subMonth()); } +} +``` + +When marking models as `Prunable`, you may also define a `pruning` method on the model. This method will be called before the model is deleted. This method can be useful for deleting any additional resources associated with the model, such as stored files, before the model is permanently removed from the database: + +```php +/** + * Prepare the model for pruning. + */ +protected function pruning(): void +{ + // ... +} +``` After configuring your prunable model, you should schedule the `model:prune` Artisan command in your application's `routes/console.php` file. You are free to choose the appropriate interval at which this command should be run: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('model:prune')->daily(); +Schedule::command('model:prune')->daily(); +``` Behind the scenes, the `model:prune` command will automatically detect "Prunable" models within your application's `app/Models` directory. If your models are in a different location, you may use the `--model` option to specify the model class names: - Schedule::command('model:prune', [ - '--model' => [Address::class, Flight::class], - ])->daily(); +```php +Schedule::command('model:prune', [ + '--model' => [Address::class, Flight::class], +])->daily(); +``` If you wish to exclude certain models from being pruned while pruning all other detected models, you may use the `--except` option: - Schedule::command('model:prune', [ - '--except' => [Address::class, Flight::class], - ])->daily(); +```php +Schedule::command('model:prune', [ + '--except' => [Address::class, Flight::class], +])->daily(); +``` You may test your `prunable` query by executing the `model:prune` command with the `--pretend` option. When pretending, the `model:prune` command will simply report how many records would be pruned if the command were to actually run: @@ -1122,61 +1248,67 @@ php artisan model:prune --pretend When models are marked with the `Illuminate\Database\Eloquent\MassPrunable` trait, models are deleted from the database using mass-deletion queries. Therefore, the `pruning` method will not be invoked, nor will the `deleting` and `deleted` model events be dispatched. This is because the models are never actually retrieved before deletion, thus making the pruning process much more efficient: - subMonth()); - } + return static::where('created_at', '<=', now()->subMonth()); } +} +``` ## Replicating Models You may create an unsaved copy of an existing model instance using the `replicate` method. This method is particularly useful when you have model instances that share many of the same attributes: - use App\Models\Address; +```php +use App\Models\Address; - $shipping = Address::create([ - 'type' => 'shipping', - 'line_1' => '123 Example Street', - 'city' => 'Victorville', - 'state' => 'CA', - 'postcode' => '90001', - ]); +$shipping = Address::create([ + 'type' => 'shipping', + 'line_1' => '123 Example Street', + 'city' => 'Victorville', + 'state' => 'CA', + 'postcode' => '90001', +]); - $billing = $shipping->replicate()->fill([ - 'type' => 'billing' - ]); +$billing = $shipping->replicate()->fill([ + 'type' => 'billing' +]); - $billing->save(); +$billing->save(); +``` To exclude one or more attributes from being replicated to the new model, you may pass an array to the `replicate` method: - $flight = Flight::create([ - 'destination' => 'LAX', - 'origin' => 'LHR', - 'last_flown' => '2020-03-04 11:00:00', - 'last_pilot_id' => 747, - ]); - - $flight = $flight->replicate([ - 'last_flown', - 'last_pilot_id' - ]); +```php +$flight = Flight::create([ + 'destination' => 'LAX', + 'origin' => 'LHR', + 'last_flown' => '2020-03-04 11:00:00', + 'last_pilot_id' => 747, +]); + +$flight = $flight->replicate([ + 'last_flown', + 'last_pilot_id' +]); +``` ## Query Scopes @@ -1200,24 +1332,26 @@ php artisan make:scope AncientScope Writing a global scope is simple. First, use the `make:scope` command to generate a class that implements the `Illuminate\Database\Eloquent\Scope` interface. The `Scope` interface requires you to implement one method: `apply`. The `apply` method may add `where` constraints or other types of clauses to the query as needed: - where('created_at', '<', now()->subYears(2000)); - } + $builder->where('created_at', '<', now()->subYears(2000)); } +} +``` > [!NOTE] > If your global scope is adding columns to the select clause of the query, you should use the `addSelect` method instead of `select`. This will prevent the unintentional replacement of the query's existing select clause. @@ -1227,38 +1361,42 @@ Writing a global scope is simple. First, use the `make:scope` command to generat To assign a global scope to a model, you may simply place the `ScopedBy` attribute on the model: - where('created_at', '<', now()->subYears(2000)); - }); - } + static::addGlobalScope('ancient', function (Builder $builder) { + $builder->where('created_at', '<', now()->subYears(2000)); + }); } +} +``` #### Removing Global Scopes If you would like to remove a global scope for a given query, you may use the `withoutGlobalScope` method. This method accepts the class name of the global scope as its only argument: - User::withoutGlobalScope(AncientScope::class)->get(); +```php +User::withoutGlobalScope(AncientScope::class)->get(); +``` Or, if you defined the global scope using a closure, you should pass the string name that you assigned to the global scope: - User::withoutGlobalScope('ancient')->get(); +```php +User::withoutGlobalScope('ancient')->get(); +``` If you would like to remove several or even all of the query's global scopes, you may use the `withoutGlobalScopes` method: - // Remove all of the global scopes... - User::withoutGlobalScopes()->get(); +```php +// Remove all of the global scopes... +User::withoutGlobalScopes()->get(); - // Remove some of the global scopes... - User::withoutGlobalScopes([ - FirstScope::class, SecondScope::class - ])->get(); +// Remove some of the global scopes... +User::withoutGlobalScopes([ + FirstScope::class, SecondScope::class +])->get(); +``` ### Local Scopes @@ -1319,127 +1465,147 @@ Local scopes allow you to define common sets of query constraints that you may e Scopes should always return the same query builder instance or `void`: - where('votes', '>', 100); + } - class User extends Model + /** + * Scope a query to only include active users. + */ + public function scopeActive(Builder $query): void { - /** - * Scope a query to only include popular users. - */ - public function scopePopular(Builder $query): void - { - $query->where('votes', '>', 100); - } - - /** - * Scope a query to only include active users. - */ - public function scopeActive(Builder $query): void - { - $query->where('active', 1); - } + $query->where('active', 1); } +} +``` #### Utilizing a Local Scope Once the scope has been defined, you may call the scope methods when querying the model. However, you should not include the `scope` prefix when calling the method. You can even chain calls to various scopes: - use App\Models\User; +```php +use App\Models\User; - $users = User::popular()->active()->orderBy('created_at')->get(); +$users = User::popular()->active()->orderBy('created_at')->get(); +``` Combining multiple Eloquent model scopes via an `or` query operator may require the use of closures to achieve the correct [logical grouping](/docs/{{version}}/queries#logical-grouping): - $users = User::popular()->orWhere(function (Builder $query) { - $query->active(); - })->get(); +```php +$users = User::popular()->orWhere(function (Builder $query) { + $query->active(); +})->get(); +``` However, since this can be cumbersome, Laravel provides a "higher order" `orWhere` method that allows you to fluently chain scopes together without the use of closures: - $users = User::popular()->orWhere->active()->get(); +```php +$users = User::popular()->orWhere->active()->get(); +``` #### Dynamic Scopes Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope method's signature. Scope parameters should be defined after the `$query` parameter: - where('type', $type); - } + $query->where('type', $type); } +} +``` Once the expected arguments have been added to your scope method's signature, you may pass the arguments when calling the scope: - $users = User::ofType('admin')->get(); +```php +$users = User::ofType('admin')->get(); +``` ### Pending Attributes If you would like to use scopes to create models that have the same attributes as those used to constrain the scope, you may use the `withAttributes` method when building the scope query: - withAttributes([ - 'hidden' => true, - ]); - } + $query->withAttributes([ + 'hidden' => true, + ]); } +} +``` The `withAttributes` method will add `where` clause constraints to the query using the given attributes, and it will also add the given attributes to any models created via the scope: - $draft = Post::draft()->create(['title' => 'In Progress']); +```php +$draft = Post::draft()->create(['title' => 'In Progress']); - $draft->hidden; // true +$draft->hidden; // true +``` ## Comparing Models Sometimes you may need to determine if two models are the "same" or not. The `is` and `isNot` methods may be used to quickly verify two models have the same primary key, table, and database connection or not: - if ($post->is($anotherPost)) { - // ... - } +```php +if ($post->is($anotherPost)) { + // ... +} - if ($post->isNot($anotherPost)) { - // ... - } +if ($post->isNot($anotherPost)) { + // ... +} +``` The `is` and `isNot` methods are also available when using the `belongsTo`, `hasOne`, `morphTo`, and `morphOne` [relationships](/docs/{{version}}/eloquent-relationships). This method is particularly helpful when you would like to compare a related model without issuing a query to retrieve that model: - if ($post->author()->is($user)) { - // ... - } +```php +if ($post->author()->is($user)) { + // ... +} +``` ## Events @@ -1453,29 +1619,31 @@ The `retrieved` event will dispatch when an existing model is retrieved from the To start listening to model events, define a `$dispatchesEvents` property on your Eloquent model. This property maps various points of the Eloquent model's lifecycle to your own [event classes](/docs/{{version}}/events). Each model event class should expect to receive an instance of the affected model via its constructor: - - */ - protected $dispatchesEvents = [ - 'saved' => UserSaved::class, - 'deleted' => UserDeleted::class, - ]; - } +class User extends Authenticatable +{ + use Notifiable; + + /** + * The event map for the model. + * + * @var array + */ + protected $dispatchesEvents = [ + 'saved' => UserSaved::class, + 'deleted' => UserDeleted::class, + ]; +} +``` After defining and mapping your Eloquent events, you may use [event listeners](/docs/{{version}}/events#defining-listeners) to handle the events. @@ -1487,32 +1655,36 @@ After defining and mapping your Eloquent events, you may use [event listeners](/ Instead of using custom event classes, you may register closures that execute when various model events are dispatched. Typically, you should register these closures in the `booted` method of your model: - ### Observers @@ -1528,78 +1700,84 @@ 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: - [!NOTE] > There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. @@ -1609,50 +1787,58 @@ Or, you may manually register an observer by invoking the `observe` method on th When models are being created within a database transaction, you may want to instruct an observer to only execute its event handlers after the database transaction is committed. You may accomplish this by implementing the `ShouldHandleEventsAfterCommit` interface on your observer. If a database transaction is not in progress, the event handlers will execute immediately: - ### Muting Events You may occasionally need to temporarily "mute" all events fired by a model. You may achieve this using the `withoutEvents` method. The `withoutEvents` method accepts a closure as its only argument. Any code executed within this closure will not dispatch model events, and any value returned by the closure will be returned by the `withoutEvents` method: - use App\Models\User; +```php +use App\Models\User; - $user = User::withoutEvents(function () { - User::findOrFail(1)->delete(); +$user = User::withoutEvents(function () { + User::findOrFail(1)->delete(); - return User::find(2); - }); + return User::find(2); +}); +``` #### Saving a Single Model Without Events Sometimes you may wish to "save" a given model without dispatching any events. You may accomplish this using the `saveQuietly` method: - $user = User::findOrFail(1); +```php +$user = User::findOrFail(1); - $user->name = 'Victoria Faith'; +$user->name = 'Victoria Faith'; - $user->saveQuietly(); +$user->saveQuietly(); +``` You may also "update", "delete", "soft delete", "restore", and "replicate" a given model without dispatching any events: - $user->deleteQuietly(); - $user->forceDeleteQuietly(); - $user->restoreQuietly(); +```php +$user->deleteQuietly(); +$user->forceDeleteQuietly(); +$user->restoreQuietly(); +``` diff --git a/encryption.md b/encryption.md index 49beb8cd179..6123a6ce6bc 100644 --- a/encryption.md +++ b/encryption.md @@ -39,39 +39,43 @@ This approach to graceful decryption allows users to keep using your application You may encrypt a value using the `encryptString` method provided by the `Crypt` facade. All encrypted values are encrypted using OpenSSL and the AES-256-CBC cipher. Furthermore, all encrypted values are signed with a message authentication code (MAC). The integrated message authentication code will prevent the decryption of any values that have been tampered with by malicious users: - user()->fill([ - 'token' => Crypt::encryptString($request->token), - ])->save(); - - return redirect('/secrets'); - } + $request->user()->fill([ + 'token' => Crypt::encryptString($request->token), + ])->save(); + + return redirect('/secrets'); } +} +``` #### Decrypting a Value You may decrypt values using the `decryptString` method provided by the `Crypt` facade. If the value cannot be properly decrypted, such as when the message authentication code is invalid, an `Illuminate\Contracts\Encryption\DecryptException` will be thrown: - use Illuminate\Contracts\Encryption\DecryptException; - use Illuminate\Support\Facades\Crypt; +```php +use Illuminate\Contracts\Encryption\DecryptException; +use Illuminate\Support\Facades\Crypt; - try { - $decrypted = Crypt::decryptString($encryptedValue); - } catch (DecryptException $e) { - // ... - } +try { + $decrypted = Crypt::decryptString($encryptedValue); +} catch (DecryptException $e) { + // ... +} +``` diff --git a/errors.md b/errors.md index 15c743d1fc6..c55d258cf15 100644 --- a/errors.md +++ b/errors.md @@ -36,23 +36,27 @@ In Laravel, exception reporting is used to log exceptions or send them to an ext If you need to report different types of exceptions in different ways, you may use the `report` exception method in your application's `bootstrap/app.php` to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will determine what type of exception the closure reports by examining the type-hint of the closure: - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->report(function (InvalidOrderException $e) { - // ... - }); - }) +```php +->withExceptions(function (Exceptions $exceptions) { + $exceptions->report(function (InvalidOrderException $e) { + // ... + }); +}) +``` When you register a custom exception reporting callback using the `report` method, Laravel will still log the exception using the default logging configuration for the application. If you wish to stop the propagation of the exception to the default logging stack, you may use the `stop` method when defining your reporting callback or return `false` from the callback: - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->report(function (InvalidOrderException $e) { - // ... - })->stop(); +```php +->withExceptions(function (Exceptions $exceptions) { + $exceptions->report(function (InvalidOrderException $e) { + // ... + })->stop(); - $exceptions->report(function (InvalidOrderException $e) { - return false; - }); - }) + $exceptions->report(function (InvalidOrderException $e) { + return false; + }); +}) +``` > [!NOTE] > To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). @@ -62,53 +66,59 @@ When you register a custom exception reporting callback using the `report` metho If available, Laravel automatically adds the current user's ID to every exception's log message as contextual data. You may define your own global contextual data using the `context` exception method in your application's `bootstrap/app.php` file. This information will be included in every exception's log message written by your application: - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->context(fn () => [ - 'foo' => 'bar', - ]); - }) +```php +->withExceptions(function (Exceptions $exceptions) { + $exceptions->context(fn () => [ + 'foo' => 'bar', + ]); +}) +``` #### Exception Log Context While adding context to every log message can be useful, sometimes a particular exception may have unique context that you would like to include in your logs. By defining a `context` method on one of your application's exceptions, you may specify any data relevant to that exception that should be added to the exception's log entry: - - */ - public function context(): array - { - return ['order_id' => $this->orderId]; - } + /** + * Get the exception's context information. + * + * @return array + */ + public function context(): array + { + return ['order_id' => $this->orderId]; } +} +``` #### The `report` Helper Sometimes you may need to report an exception but continue handling the current request. The `report` helper function allows you to quickly report an exception without rendering an error page to the user: - public function isValid(string $value): bool - { - try { - // Validate the value... - } catch (Throwable $e) { - report($e); +```php +public function isValid(string $value): bool +{ + try { + // Validate the value... + } catch (Throwable $e) { + report($e); - return false; - } + return false; } +} +``` #### Deduplicating Reported Exceptions @@ -117,9 +127,11 @@ If you are using the `report` function throughout your application, you may occa If you would like to ensure that a single instance of an exception is only ever reported once, you may invoke the `dontReportDuplicates` exception method in your application's `bootstrap/app.php` file: - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->dontReportDuplicates(); - }) +```php +->withExceptions(function (Exceptions $exceptions) { + $exceptions->dontReportDuplicates(); +}) +``` Now, when the `report` helper is called with the same instance of an exception, only the first call will be reported: @@ -147,25 +159,29 @@ As noted above, even when you register a custom exception reporting callback usi To accomplish this, you may use the `level` exception method in your application's `bootstrap/app.php` file. This method receives the exception type as its first argument and the log level as its second argument: - use PDOException; - use Psr\Log\LogLevel; +```php +use PDOException; +use Psr\Log\LogLevel; - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->level(PDOException::class, LogLevel::CRITICAL); - }) +->withExceptions(function (Exceptions $exceptions) { + $exceptions->level(PDOException::class, LogLevel::CRITICAL); +}) +``` ### Ignoring Exceptions by Type When building your application, there will be some types of exceptions you never want to report. To ignore these exceptions, you may use the `dontReport` exception method in your application's `bootstrap/app.php` file. Any class provided to this method will never be reported; however, they may still have custom rendering logic: - use App\Exceptions\InvalidOrderException; +```php +use App\Exceptions\InvalidOrderException; - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->dontReport([ - InvalidOrderException::class, - ]); - }) +->withExceptions(function (Exceptions $exceptions) { + $exceptions->dontReport([ + InvalidOrderException::class, + ]); +}) +``` Alternatively, you may simply "mark" an exception class with the `Illuminate\Contracts\Debug\ShouldntReport` interface. When an exception is marked with this interface, it will never be reported by Laravel's exception handler: @@ -185,11 +201,13 @@ class PodcastProcessingException extends Exception implements ShouldntReport Internally, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP errors or 419 HTTP responses generated by invalid CSRF tokens. If you would like to instruct Laravel to stop ignoring a given type of exception, you may use the `stopIgnoring` exception method in your application's `bootstrap/app.php` file: - use Symfony\Component\HttpKernel\Exception\HttpException; +```php +use Symfony\Component\HttpKernel\Exception\HttpException; - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->stopIgnoring(HttpException::class); - }) +->withExceptions(function (Exceptions $exceptions) { + $exceptions->stopIgnoring(HttpException::class); +}) +``` ### Rendering Exceptions @@ -198,131 +216,145 @@ By default, the Laravel exception handler will convert exceptions into an HTTP r The closure passed to the `render` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will determine what type of exception the closure renders by examining the type-hint of the closure: - use App\Exceptions\InvalidOrderException; - use Illuminate\Http\Request; - - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->render(function (InvalidOrderException $e, Request $request) { - return response()->view('errors.invalid-order', status: 500); - }); - }) +```php +use App\Exceptions\InvalidOrderException; +use Illuminate\Http\Request; + +->withExceptions(function (Exceptions $exceptions) { + $exceptions->render(function (InvalidOrderException $e, Request $request) { + return response()->view('errors.invalid-order', status: 500); + }); +}) +``` You may also use the `render` method to override the rendering behavior for built-in Laravel or Symfony exceptions such as `NotFoundHttpException`. If the closure given to the `render` method does not return a value, Laravel's default exception rendering will be utilized: - use Illuminate\Http\Request; - use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->render(function (NotFoundHttpException $e, Request $request) { - if ($request->is('api/*')) { - return response()->json([ - 'message' => 'Record not found.' - ], 404); - } - }); - }) +```php +use Illuminate\Http\Request; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +->withExceptions(function (Exceptions $exceptions) { + $exceptions->render(function (NotFoundHttpException $e, Request $request) { + if ($request->is('api/*')) { + return response()->json([ + 'message' => 'Record not found.' + ], 404); + } + }); +}) +``` #### Rendering Exceptions as JSON When rendering an exception, Laravel will automatically determine if the exception should be rendered as an HTML or JSON response based on the `Accept` header of the request. If you would like to customize how Laravel determines whether to render HTML or JSON exception responses, you may utilize the `shouldRenderJsonWhen` method: - use Illuminate\Http\Request; - use Throwable; +```php +use Illuminate\Http\Request; +use Throwable; - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) { - if ($request->is('admin/*')) { - return true; - } +->withExceptions(function (Exceptions $exceptions) { + $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) { + if ($request->is('admin/*')) { + return true; + } - return $request->expectsJson(); - }); - }) + return $request->expectsJson(); + }); +}) +``` #### Customizing the Exception Response Rarely, you may need to customize the entire HTTP response rendered by Laravel's exception handler. To accomplish this, you may register a response customization closure using the `respond` method: - use Symfony\Component\HttpFoundation\Response; - - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->respond(function (Response $response) { - if ($response->getStatusCode() === 419) { - return back()->with([ - 'message' => 'The page expired, please try again.', - ]); - } +```php +use Symfony\Component\HttpFoundation\Response; + +->withExceptions(function (Exceptions $exceptions) { + $exceptions->respond(function (Response $response) { + if ($response->getStatusCode() === 419) { + return back()->with([ + 'message' => 'The page expired, please try again.', + ]); + } - return $response; - }); - }) + return $response; + }); +}) +``` ### Reportable and Renderable Exceptions Instead of defining custom reporting and rendering behavior in your application's `bootstrap/app.php` file, you may define `report` and `render` methods directly on your application's exceptions. When these methods exist, they will automatically be called by the framework: - [!NOTE] > You may type-hint any required dependencies of the `report` method and they will automatically be injected into the method by Laravel's [service container](/docs/{{version}}/container). @@ -333,88 +365,102 @@ If your application reports a very large number of exceptions, you may want to t To take a random sample rate of exceptions, you may use the `throttle` exception method in your application's `bootstrap/app.php` file. The `throttle` method receives a closure that should return a `Lottery` instance: - use Illuminate\Support\Lottery; - use Throwable; - - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable $e) { - return Lottery::odds(1, 1000); - }); - }) +```php +use Illuminate\Support\Lottery; +use Throwable; + +->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable $e) { + return Lottery::odds(1, 1000); + }); +}) +``` It is also possible to conditionally sample based on the exception type. If you would like to only sample instances of a specific exception class, you may return a `Lottery` instance only for that class: - use App\Exceptions\ApiMonitoringException; - use Illuminate\Support\Lottery; - use Throwable; +```php +use App\Exceptions\ApiMonitoringException; +use Illuminate\Support\Lottery; +use Throwable; - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable $e) { - if ($e instanceof ApiMonitoringException) { - return Lottery::odds(1, 1000); - } - }); - }) +->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable $e) { + if ($e instanceof ApiMonitoringException) { + return Lottery::odds(1, 1000); + } + }); +}) +``` You may also rate limit exceptions logged or sent to an external error tracking service by returning a `Limit` instance instead of a `Lottery`. This is useful if you want to protect against sudden bursts of exceptions flooding your logs, for example, when a third-party service used by your application is down: - use Illuminate\Broadcasting\BroadcastException; - use Illuminate\Cache\RateLimiting\Limit; - use Throwable; - - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable $e) { - if ($e instanceof BroadcastException) { - return Limit::perMinute(300); - } - }); - }) +```php +use Illuminate\Broadcasting\BroadcastException; +use Illuminate\Cache\RateLimiting\Limit; +use Throwable; + +->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable $e) { + if ($e instanceof BroadcastException) { + return Limit::perMinute(300); + } + }); +}) +``` By default, limits will use the exception's class as the rate limit key. You can customize this by specifying your own key using the `by` method on the `Limit`: - use Illuminate\Broadcasting\BroadcastException; - use Illuminate\Cache\RateLimiting\Limit; - use Throwable; - - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable $e) { - if ($e instanceof BroadcastException) { - return Limit::perMinute(300)->by($e->getMessage()); - } - }); - }) +```php +use Illuminate\Broadcasting\BroadcastException; +use Illuminate\Cache\RateLimiting\Limit; +use Throwable; + +->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable $e) { + if ($e instanceof BroadcastException) { + return Limit::perMinute(300)->by($e->getMessage()); + } + }); +}) +``` Of course, you may return a mixture of `Lottery` and `Limit` instances for different exceptions: - use App\Exceptions\ApiMonitoringException; - use Illuminate\Broadcasting\BroadcastException; - use Illuminate\Cache\RateLimiting\Limit; - use Illuminate\Support\Lottery; - use Throwable; - - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->throttle(function (Throwable $e) { - return match (true) { - $e instanceof BroadcastException => Limit::perMinute(300), - $e instanceof ApiMonitoringException => Lottery::odds(1, 1000), - default => Limit::none(), - }; - }); - }) +```php +use App\Exceptions\ApiMonitoringException; +use Illuminate\Broadcasting\BroadcastException; +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Support\Lottery; +use Throwable; + +->withExceptions(function (Exceptions $exceptions) { + $exceptions->throttle(function (Throwable $e) { + return match (true) { + $e instanceof BroadcastException => Limit::perMinute(300), + $e instanceof ApiMonitoringException => Lottery::odds(1, 1000), + default => Limit::none(), + }; + }); +}) +``` ## HTTP Exceptions Some exceptions describe HTTP error codes from the server. For example, this may be a "page not found" error (404), an "unauthorized error" (401), or even a developer generated 500 error. In order to generate such a response from anywhere in your application, you may use the `abort` helper: - abort(404); +```php +abort(404); +``` ### Custom HTTP Error Pages Laravel makes it easy to display custom error pages for various HTTP status codes. For example, to customize the error page for 404 HTTP status codes, create a `resources/views/errors/404.blade.php` view template. This view will be rendered for all 404 errors generated by your application. The views within this directory should be named to match the HTTP status code they correspond to. The `Symfony\Component\HttpKernel\Exception\HttpException` instance raised by the `abort` function will be passed to the view as an `$exception` variable: -

    {{ $exception->getMessage() }}

    +```blade +

    {{ $exception->getMessage() }}

    +``` You may publish Laravel's default error page templates using the `vendor:publish` Artisan command. Once the templates have been published, you may customize them to your liking: diff --git a/events.md b/events.md index b0379bb4bfb..4f98b892cef 100644 --- a/events.md +++ b/events.md @@ -55,40 +55,48 @@ php artisan make:listener By default, Laravel will automatically find and register your event listeners by scanning your application's `Listeners` directory. When Laravel finds any listener class method that begins with `handle` or `__invoke`, Laravel will register those methods as event listeners for the event that is type-hinted in the method's signature: - use App\Events\PodcastProcessed; - - class SendPodcastNotification - { - /** - * Handle the given event. - */ - public function handle(PodcastProcessed $event): void - { - // ... - } - } - -You may listen to multiple events using PHP's union types: +```php +use App\Events\PodcastProcessed; +class SendPodcastNotification +{ /** * Handle the given event. */ - public function handle(PodcastProcessed|PodcastPublished $event): void + public function handle(PodcastProcessed $event): void { // ... } +} +``` + +You may listen to multiple events using PHP's union types: + +```php +/** + * Handle the given event. + */ +public function handle(PodcastProcessed|PodcastPublished $event): void +{ + // ... +} +``` If you plan to store your listeners in a different directory or within multiple directories, you may instruct Laravel to scan those directories using the `withEvents` method in your application's `bootstrap/app.php` file: - ->withEvents(discover: [ - __DIR__.'/../app/Domain/Orders/Listeners', - ]) +```php +->withEvents(discover: [ + __DIR__.'/../app/Domain/Orders/Listeners', +]) +``` You may scan for listeners in multiple similar directories using the `*` character as a wildcard: - ->withEvents(discover: [ - __DIR__.'/../app/Domain/*/Listeners', - ]) +```php +->withEvents(discover: [ + __DIR__.'/../app/Domain/*/Listeners', +]) +``` The `event:list` command may be used to list all of the listeners registered within your application: @@ -106,20 +114,22 @@ To give your application a speed boost, you should cache a manifest of all of yo Using the `Event` facade, you may manually register events and their corresponding listeners within the `boot` method of your application's `AppServiceProvider`: - use App\Domain\Orders\Events\PodcastProcessed; - use App\Domain\Orders\Listeners\SendPodcastNotification; - use Illuminate\Support\Facades\Event; +```php +use App\Domain\Orders\Events\PodcastProcessed; +use App\Domain\Orders\Listeners\SendPodcastNotification; +use Illuminate\Support\Facades\Event; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Event::listen( - PodcastProcessed::class, - SendPodcastNotification::class, - ); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Event::listen( + PodcastProcessed::class, + SendPodcastNotification::class, + ); +} +``` The `event:list` command may be used to list all of the listeners registered within your application: @@ -132,91 +142,103 @@ php artisan event:list Typically, listeners are defined as classes; however, you may also manually register closure-based event listeners in the `boot` method of your application's `AppServiceProvider`: - use App\Events\PodcastProcessed; - use Illuminate\Support\Facades\Event; +```php +use App\Events\PodcastProcessed; +use Illuminate\Support\Facades\Event; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Event::listen(function (PodcastProcessed $event) { - // ... - }); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Event::listen(function (PodcastProcessed $event) { + // ... + }); +} +``` #### Queueable Anonymous Event Listeners When registering closure based event listeners, you may wrap the listener closure within the `Illuminate\Events\queueable` function to instruct Laravel to execute the listener using the [queue](/docs/{{version}}/queues): - use App\Events\PodcastProcessed; - use function Illuminate\Events\queueable; - use Illuminate\Support\Facades\Event; +```php +use App\Events\PodcastProcessed; +use function Illuminate\Events\queueable; +use Illuminate\Support\Facades\Event; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Event::listen(queueable(function (PodcastProcessed $event) { - // ... - })); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Event::listen(queueable(function (PodcastProcessed $event) { + // ... + })); +} +``` Like queued jobs, you may use the `onConnection`, `onQueue`, and `delay` methods to customize the execution of the queued listener: - Event::listen(queueable(function (PodcastProcessed $event) { - // ... - })->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10))); +```php +Event::listen(queueable(function (PodcastProcessed $event) { + // ... +})->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. This closure will receive the event instance and the `Throwable` instance that caused the listener's failure: - use App\Events\PodcastProcessed; - use function Illuminate\Events\queueable; - use Illuminate\Support\Facades\Event; - use Throwable; +```php +use App\Events\PodcastProcessed; +use function Illuminate\Events\queueable; +use Illuminate\Support\Facades\Event; +use Throwable; - Event::listen(queueable(function (PodcastProcessed $event) { - // ... - })->catch(function (PodcastProcessed $event, Throwable $e) { - // The queued listener failed... - })); +Event::listen(queueable(function (PodcastProcessed $event) { + // ... +})->catch(function (PodcastProcessed $event, Throwable $e) { + // The queued listener failed... +})); +``` #### Wildcard Event Listeners You may also register listeners using the `*` character as a wildcard parameter, allowing you to catch multiple events on the same listener. Wildcard listeners receive the event name as their first argument and the entire event data array as their second argument: - Event::listen('event.*', function (string $eventName, array $data) { - // ... - }); +```php +Event::listen('event.*', function (string $eventName, array $data) { + // ... +}); +``` ## Defining Events An event class is essentially a data container which holds the information related to the event. For example, let's assume an `App\Events\OrderShipped` event receives an [Eloquent ORM](/docs/{{version}}/eloquent) object: - order... - } + // Access the order using $event->order... } +} +``` > [!NOTE] > Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. @@ -262,17 +286,19 @@ Queueing listeners can be beneficial if your listener is going to perform a slow To specify that a listener should be queued, add the `ShouldQueue` interface to the listener class. Listeners generated by the `make:listener` Artisan commands already have this interface imported into the current namespace so you can use it immediately: - highPriority ? 0 : 60; - } + public $delay = 60; +} +``` + +If you would like to define the listener's queue connection, queue name, or delay at runtime, you may define `viaConnection`, `viaQueue`, or `withDelay` methods on the listener: + +```php +/** + * Get the name of the listener's queue connection. + */ +public function viaConnection(): string +{ + return 'sqs'; +} + +/** + * Get the name of the listener's queue. + */ +public function viaQueue(): string +{ + return 'listeners'; +} + +/** + * Get the number of seconds before the job should be processed. + */ +public function withDelay(OrderShipped $event): int +{ + return $event->highPriority ? 0 : 60; +} +``` #### Conditionally Queueing Listeners Sometimes, you may need to determine whether a listener should be queued based on some data that are only available at runtime. To accomplish this, a `shouldQueue` method may be added to a listener to determine whether the listener should be queued. If the `shouldQueue` method returns `false`, the listener will not be queued: - order->subtotal >= 5000; - } + /** + * Determine whether the listener should be queued. + */ + public function shouldQueue(OrderCreated $event): bool + { + return $event->order->subtotal >= 5000; } +} +``` ### Manually Interacting With the Queue If you need to manually access the listener's underlying queue job's `delete` and `release` methods, you may do so using the `Illuminate\Queue\InteractsWithQueue` trait. This trait is imported by default on generated listeners and provides access to these methods: - release(30); - } + if (true) { + $this->release(30); } } +} +``` ### Queued Event Listeners and Database Transactions @@ -404,17 +438,19 @@ When queued listeners are dispatched within database transactions, they may be p If your queue connection's `after_commit` configuration option is set to `false`, you may still indicate that a particular queued listener should be dispatched after all open database transactions have been committed by implementing the `ShouldQueueAfterCommit` interface on the listener class: - [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -424,35 +460,37 @@ If your queue connection's `after_commit` configuration option is set to `false` Sometimes your queued event listeners may fail. If the queued listener exceeds the maximum number of attempts as defined by your queue worker, the `failed` method will be called on your listener. The `failed` method receives the event instance and the `Throwable` that caused the failure: - #### Specifying Queued Listener Maximum Attempts @@ -461,109 +499,123 @@ If one of your queued listeners is encountering an error, you likely do not want You may define a `$tries` property on your listener class to specify how many times the listener may be attempted before it is considered to have failed: - addMinutes(5); - } +/** + * Determine the time at which the listener should timeout. + */ +public function retryUntil(): DateTime +{ + return now()->addMinutes(5); +} +``` #### Specifying Queued Listener Backoff If you would like to configure how many seconds Laravel should wait before retrying a listener that has encountered an exception, you may do so by defining a `backoff` property on your listener class: - /** - * The number of seconds to wait before retrying the queued listener. - * - * @var int - */ - public $backoff = 3; +```php +/** + * The number of seconds to wait before retrying the queued listener. + * + * @var int + */ +public $backoff = 3; +``` If you require more complex logic for determining the listeners's backoff time, you may define a `backoff` method on your listener class: - /** - * Calculate the number of seconds to wait before retrying the queued listener. - */ - public function backoff(): int - { - return 3; - } +```php +/** + * Calculate the number of seconds to wait before retrying the queued listener. + */ +public function backoff(): int +{ + 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 second for the first retry, 5 seconds for the second retry, 10 seconds for the third retry, and 10 seconds for every subsequent retry if there are more attempts remaining: - /** - * Calculate the number of seconds to wait before retrying the queued listener. - * - * @return array - */ - public function backoff(): array - { - return [1, 5, 10]; - } +```php +/** + * Calculate the number of seconds to wait before retrying the queued listener. + * + * @return array + */ +public function backoff(): array +{ + return [1, 5, 10]; +} +``` ## Dispatching Events To dispatch an event, you may call the static `dispatch` method on the event. This method is made available on the event by the `Illuminate\Foundation\Events\Dispatchable` trait. Any arguments passed to the `dispatch` method will be passed to the event's constructor: - order_id); + $order = Order::findOrFail($request->order_id); - // Order shipment logic... + // Order shipment logic... - OrderShipped::dispatch($order); + OrderShipped::dispatch($order); - return redirect('/orders'); - } + return redirect('/orders'); } +} +``` If you would like to conditionally dispatch an event, you may use the `dispatchIf` and `dispatchUnless` methods: - OrderShipped::dispatchIf($condition, $order); +```php +OrderShipped::dispatchIf($condition, $order); - OrderShipped::dispatchUnless($condition, $order); +OrderShipped::dispatchUnless($condition, $order); +``` > [!NOTE] > When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) make it a cinch. @@ -575,27 +627,29 @@ Sometimes, you may want to instruct Laravel to only dispatch an event after the This interface instructs Laravel to not dispatch the event until the current database transaction is committed. If the transaction fails, the event will be discarded. If no database transaction is in progress when the event is dispatched, the event will be dispatched immediately: - ## Event Subscribers @@ -605,102 +659,108 @@ This interface instructs Laravel to not dispatch the event until the current dat Event subscribers are classes that may subscribe to multiple events from within the subscriber class itself, allowing you to define several event handlers within a single class. Subscribers should define a `subscribe` method, which will be passed an event dispatcher instance. You may call the `listen` method on the given dispatcher to register event listeners: - listen( - Login::class, - [UserEventSubscriber::class, 'handleUserLogin'] - ); - - $events->listen( - Logout::class, - [UserEventSubscriber::class, 'handleUserLogout'] - ); - } + $events->listen( + Login::class, + [UserEventSubscriber::class, 'handleUserLogin'] + ); + + $events->listen( + Logout::class, + [UserEventSubscriber::class, 'handleUserLogout'] + ); } +} +``` If your event listener methods are defined within the subscriber itself, you may find it more convenient to return an array of events and method names from the subscriber's `subscribe` method. Laravel will automatically determine the subscriber's class name when registering the event listeners: - + */ + public function subscribe(Dispatcher $events): array { - /** - * Handle user login events. - */ - public function handleUserLogin(Login $event): void {} - - /** - * Handle user logout events. - */ - public function handleUserLogout(Logout $event): void {} - - /** - * Register the listeners for the subscriber. - * - * @return array - */ - public function subscribe(Dispatcher $events): array - { - return [ - Login::class => 'handleUserLogin', - Logout::class => 'handleUserLogout', - ]; - } + return [ + Login::class => 'handleUserLogin', + Logout::class => 'handleUserLogout', + ]; } +} +``` ### Registering Event Subscribers After writing the subscriber, Laravel will automatically register handler methods within the subscriber if they follow Laravel's [event discovery conventions](#event-discovery). Otherwise, you may manually register your subscriber using the `subscribe` method of the `Event` facade. Typically, this should be done within the `boot` method of your application's `AppServiceProvider`: - ## Testing @@ -773,16 +833,20 @@ class ExampleTest extends TestCase 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; - }); +```php +Event::assertDispatched(function (OrderShipped $event) use ($order) { + return $event->order->id === $order->id; +}); +``` If you would simply like to assert that an event listener is listening to a given event, you may use the `assertListening` method: - Event::assertListening( - OrderShipped::class, - SendShipmentNotification::class - ); +```php +Event::assertListening( + OrderShipped::class, + SendShipmentNotification::class +); +``` > [!WARNING] > 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. @@ -828,9 +892,11 @@ public function test_orders_can_be_processed(): void You may fake all events except for a set of specified events using the `except` method: - Event::fake()->except([ - OrderCreated::class, - ]); +```php +Event::fake()->except([ + OrderCreated::class, +]); +``` ### Scoped Event Fakes diff --git a/facades.md b/facades.md index a8621e52502..d48b3188f26 100644 --- a/facades.md +++ b/facades.md @@ -17,12 +17,14 @@ Laravel facades serve as "static proxies" to underlying classes in the service c All of Laravel's facades are defined in the `Illuminate\Support\Facades` namespace. So, we can easily access a facade like so: - use Illuminate\Support\Facades\Cache; - use Illuminate\Support\Facades\Route; +```php +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Route; - Route::get('/cache', function () { - return Cache::get('key'); - }); +Route::get('/cache', function () { + return Cache::get('key'); +}); +``` Throughout the Laravel documentation, many of the examples will use facades to demonstrate various features of the framework. @@ -33,19 +35,21 @@ To complement facades, Laravel offers a variety of global "helper functions" tha For example, instead of using the `Illuminate\Support\Facades\Response` facade to generate a JSON response, we may simply use the `response` function. Because helper functions are globally available, you do not need to import any classes in order to use them: - use Illuminate\Support\Facades\Response; +```php +use Illuminate\Support\Facades\Response; - Route::get('/users', function () { - return Response::json([ - // ... - ]); - }); +Route::get('/users', function () { + return Response::json([ + // ... + ]); +}); - Route::get('/users', function () { - return response()->json([ - // ... - ]); - }); +Route::get('/users', function () { + return response()->json([ + // ... + ]); +}); +``` ## When to Utilize Facades @@ -61,11 +65,13 @@ One of the primary benefits of dependency injection is the ability to swap imple Typically, it would not be possible to mock or stub a truly static class method. However, since facades use dynamic methods to proxy method calls to objects resolved from the service container, we actually can test facades just as we would test an injected class instance. For example, given the following route: - use Illuminate\Support\Facades\Cache; +```php +use Illuminate\Support\Facades\Cache; - Route::get('/cache', function () { - return Cache::get('key'); - }); +Route::get('/cache', function () { + return Cache::get('key'); +}); +``` Using Laravel's facade testing methods, we can write the following test to verify that the `Cache::get` method was called with the argument we expected: @@ -106,33 +112,39 @@ public function test_basic_example(): void In addition to facades, Laravel includes a variety of "helper" functions which can perform common tasks like generating views, firing events, dispatching jobs, or sending HTTP responses. Many of these helper functions perform the same function as a corresponding facade. For example, this facade call and helper call are equivalent: - return Illuminate\Support\Facades\View::make('profile'); +```php +return Illuminate\Support\Facades\View::make('profile'); - return view('profile'); +return view('profile'); +``` There is absolutely no practical difference between facades and helper functions. When using helper functions, you may still test them exactly as you would the corresponding facade. For example, given the following route: - Route::get('/cache', function () { - return cache('key'); - }); +```php +Route::get('/cache', function () { + return cache('key'); +}); +``` The `cache` helper is going to call the `get` method on the class underlying the `Cache` facade. So, even though we are using the helper function, we can write the following test to verify that the method was called with the argument we expected: - use Illuminate\Support\Facades\Cache; +```php +use Illuminate\Support\Facades\Cache; - /** - * A basic functional test example. - */ - public function test_basic_example(): void - { - Cache::shouldReceive('get') - ->with('key') - ->andReturn('value'); +/** + * A basic functional test example. + */ +public function test_basic_example(): void +{ + Cache::shouldReceive('get') + ->with('key') + ->andReturn('value'); - $response = $this->get('/cache'); + $response = $this->get('/cache'); - $response->assertSee('value'); - } + $response->assertSee('value'); +} +``` ## How Facades Work @@ -141,41 +153,45 @@ In a Laravel application, a facade is a class that provides access to an object The `Facade` base class makes use of the `__callStatic()` magic-method to defer calls from your facade to an object resolved from the container. In the example below, a call is made to the Laravel cache system. By glancing at this code, one might assume that the static `get` method is being called on the `Cache` class: - $user]); - } + $user = Cache::get('user:'.$id); + + return view('profile', ['user' => $user]); } +} +``` Notice that near the top of the file we are "importing" the `Cache` facade. This facade serves as a proxy for accessing the underlying implementation of the `Illuminate\Contracts\Cache\Factory` interface. Any calls we make using the facade will be passed to the underlying instance of Laravel's cache service. If we look at that `Illuminate\Support\Facades\Cache` class, you'll see that there is no static method `get`: - class Cache extends Facade +```php +class Cache extends Facade +{ + /** + * Get the registered name of the component. + */ + protected static function getFacadeAccessor(): string { - /** - * Get the registered name of the component. - */ - protected static function getFacadeAccessor(): string - { - return 'cache'; - } + return 'cache'; } +} +``` Instead, the `Cache` facade extends the base `Facade` class and defines the method `getFacadeAccessor()`. This method's job is to return the name of a service container binding. When a user references any static method on the `Cache` facade, Laravel resolves the `cache` binding from the [service container](/docs/{{version}}/container) and runs the requested method (in this case, `get`) against that object. @@ -184,50 +200,54 @@ Instead, the `Cache` facade extends the base `Facade` class and defines the meth Using real-time facades, you may treat any class in your application as if it was a facade. To illustrate how this can be used, let's first examine some code that does not use real-time facades. For example, let's assume our `Podcast` model has a `publish` method. However, in order to publish the podcast, we need to inject a `Publisher` instance: - update(['publishing' => now()]); - - $publisher->publish($this); - } + $this->update(['publishing' => now()]); + + $publisher->publish($this); } +} +``` Injecting a publisher implementation into the method allows us to easily test the method in isolation since we can mock the injected publisher. However, it requires us to always pass a publisher instance each time we call the `publish` method. Using real-time facades, we can maintain the same testability while not being required to explicitly pass a `Publisher` instance. To generate a real-time facade, prefix the namespace of the imported class with `Facades`: - update(['publishing' => now()]); - - $publisher->publish($this); // [tl! remove] - Publisher::publish($this); // [tl! add] - } + $this->update(['publishing' => now()]); + + $publisher->publish($this); // [tl! remove] + Publisher::publish($this); // [tl! add] } +} +``` When the real-time facade is used, the publisher implementation will be resolved out of the service container using the portion of the interface or class name that appears after the `Facades` prefix. When testing, we can use Laravel's built-in facade testing helpers to mock this method call: diff --git a/filesystem.md b/filesystem.md index dd302ead5e5..d50402e30f8 100644 --- a/filesystem.md +++ b/filesystem.md @@ -45,9 +45,11 @@ The `local` driver interacts with files stored locally on the server running the When using the `local` driver, all file operations are relative to the `root` directory defined in your `filesystems` configuration file. By default, this value is set to the `storage/app/private` directory. Therefore, the following method would write to `storage/app/private/example.txt`: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - Storage::disk('local')->put('example.txt', 'Contents'); +Storage::disk('local')->put('example.txt', 'Contents'); +``` ### The Public Disk @@ -64,14 +66,18 @@ php artisan storage:link Once a file has been stored and the symbolic link has been created, you can create a URL to the files using the `asset` helper: - echo asset('storage/file.txt'); +```php +echo asset('storage/file.txt'); +``` You may configure additional symbolic links in your `filesystems` configuration file. Each of the configured links will be created when you run the `storage:link` command: - 'links' => [ - public_path('storage') => storage_path('app/public'), - public_path('images') => storage_path('app/images'), - ], +```php +'links' => [ + public_path('storage') => storage_path('app/public'), + public_path('images') => storage_path('app/images'), +], +``` The `storage:unlink` command may be used to destroy your configured symbolic links: @@ -114,19 +120,21 @@ 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 +'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 @@ -139,31 +147,33 @@ 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 +'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 @@ -207,7 +217,9 @@ By default, your application's `filesystems` configuration file contains a disk Typically, after updating the disk's credentials to match the credentials of the service you are planning to use, you only need to update the value of the `endpoint` configuration option. This option's value is typically defined via the `AWS_ENDPOINT` environment variable: - 'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000/'), +```php +'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000/'), +``` #### MinIO @@ -226,13 +238,17 @@ AWS_URL=http://localhost:9000/local The `Storage` facade may be used to interact with any of your configured disks. For example, you may use the `put` method on the facade to store an avatar on the default disk. If you call methods on the `Storage` facade without first calling the `disk` method, the method will automatically be passed to the default disk: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - Storage::put('avatars/1', $content); +Storage::put('avatars/1', $content); +``` If your application interacts with multiple disks, you may use the `disk` method on the `Storage` facade to work with files on a particular disk: - Storage::disk('s3')->put('avatars/1', $content); +```php +Storage::disk('s3')->put('avatars/1', $content); +``` ### On-Demand Disks @@ -255,41 +271,53 @@ $disk->put('image.jpg', $content); The `get` method may be used to retrieve the contents of a file. The raw string contents of the file will be returned by the method. Remember, all file paths should be specified relative to the disk's "root" location: - $contents = Storage::get('file.jpg'); +```php +$contents = Storage::get('file.jpg'); +``` If the file you are retrieving contains JSON, you may use the `json` method to retrieve the file and decode its contents: - $orders = Storage::json('orders.json'); +```php +$orders = Storage::json('orders.json'); +``` The `exists` method may be used to determine if a file exists on the disk: - if (Storage::disk('s3')->exists('file.jpg')) { - // ... - } +```php +if (Storage::disk('s3')->exists('file.jpg')) { + // ... +} +``` The `missing` method may be used to determine if a file is missing from the disk: - if (Storage::disk('s3')->missing('file.jpg')) { - // ... - } +```php +if (Storage::disk('s3')->missing('file.jpg')) { + // ... +} +``` ### Downloading Files 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 Storage::download('file.jpg'); +```php +return Storage::download('file.jpg'); - return Storage::download('file.jpg', $name, $headers); +return Storage::download('file.jpg', $name, $headers); +``` ### File URLs You may use the `url` method to get the URL for a given file. If you are using the `local` driver, this will typically just prepend `/storage` to the given path and return a relative URL to the file. If you are using the `s3` driver, the fully qualified remote URL will be returned: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - $url = Storage::url('https://github.com/file.jpg'); +$url = Storage::url('https://github.com/file.jpg'); +``` When using the `local` driver, all files that should be publicly accessible should be placed in the `storage/app/public` directory. Furthermore, you should [create a symbolic link](#the-public-disk) at `public/storage` which points to the `storage/app/public` directory. @@ -301,24 +329,28 @@ When using the `local` driver, all files that should be publicly accessible shou If you would like to modify the host for URLs generated using the `Storage` facade, you may add or change the `url` option in the disk's configuration array: - 'public' => [ - 'driver' => 'local', - 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', - 'visibility' => 'public', - 'throw' => false, - ], +```php +'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, +], +``` ### Temporary URLs Using the `temporaryUrl` method, you may create temporary URLs to files stored using the `local` and `s3` drivers. This method accepts a path and a `DateTime` instance specifying when the URL should expire: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - $url = Storage::temporaryUrl( - 'file.jpg', now()->addMinutes(5) - ); +$url = Storage::temporaryUrl( + 'file.jpg', now()->addMinutes(5) +); +``` #### Enabling Local Temporary URLs @@ -339,47 +371,51 @@ If you started developing your application before support for temporary URLs was If you need to specify additional [S3 request parameters](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html#RESTObjectGET-requests), you may pass the array of request parameters as the third argument to the `temporaryUrl` method: - $url = Storage::temporaryUrl( - 'file.jpg', - now()->addMinutes(5), - [ - 'ResponseContentType' => 'application/octet-stream', - 'ResponseContentDisposition' => 'attachment; filename=file2.jpg', - ] - ); +```php +$url = Storage::temporaryUrl( + 'file.jpg', + now()->addMinutes(5), + [ + 'ResponseContentType' => 'application/octet-stream', + 'ResponseContentDisposition' => 'attachment; filename=file2.jpg', + ] +); +``` #### Customizing Temporary URLs If you need to customize how temporary URLs are created for a specific storage disk, you can use the `buildTemporaryUrlsUsing` method. For example, this can be useful if you have a controller that allows you to download files stored via a disk that doesn't typically support temporary URLs. Usually, this method should be called from the `boot` method of a service provider: - buildTemporaryUrlsUsing( - function (string $path, DateTime $expiration, array $options) { - return URL::temporarySignedRoute( - 'files.download', - $expiration, - array_merge($options, ['path' => $path]) - ); - } - ); - } + Storage::disk('local')->buildTemporaryUrlsUsing( + function (string $path, DateTime $expiration, array $options) { + return URL::temporarySignedRoute( + 'files.download', + $expiration, + array_merge($options, ['path' => $path]) + ); + } + ); } +} +``` #### Temporary Upload URLs @@ -389,11 +425,13 @@ If you need to customize how temporary URLs are created for a specific storage d If you need to generate a temporary URL that can be used to upload a file directly from your client-side application, you may use the `temporaryUploadUrl` method. This method accepts a path and a `DateTime` instance specifying when the URL should expire. The `temporaryUploadUrl` method returns an associative array which may be destructured into the upload URL and the headers that should be included with the upload request: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - ['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl( - 'file.jpg', now()->addMinutes(5) - ); +['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl( + 'file.jpg', now()->addMinutes(5) +); +``` This method is primarily useful in serverless environments that require the client-side application to directly upload files to a cloud storage system such as Amazon S3. @@ -402,138 +440,168 @@ This method is primarily useful in serverless environments that require the clie In addition to reading and writing files, Laravel can also provide information about the files themselves. For example, the `size` method may be used to get the size of a file in bytes: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - $size = Storage::size('file.jpg'); +$size = Storage::size('file.jpg'); +``` The `lastModified` method returns the UNIX timestamp of the last time the file was modified: - $time = Storage::lastModified('file.jpg'); +```php +$time = Storage::lastModified('file.jpg'); +``` The MIME type of a given file may be obtained via the `mimeType` method: - $mime = Storage::mimeType('file.jpg'); +```php +$mime = Storage::mimeType('file.jpg'); +``` #### File Paths You may use the `path` method to get the path for a given file. If you are using the `local` driver, this will return the absolute path to the file. If you are using the `s3` driver, this method will return the relative path to the file in the S3 bucket: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - $path = Storage::path('file.jpg'); +$path = Storage::path('file.jpg'); +``` ## Storing Files The `put` method may be used to store file contents on a disk. You may also pass a PHP `resource` to the `put` method, which will use Flysystem's underlying stream support. Remember, all file paths should be specified relative to the "root" location configured for the disk: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - Storage::put('file.jpg', $contents); +Storage::put('file.jpg', $contents); - Storage::put('file.jpg', $resource); +Storage::put('file.jpg', $resource); +``` #### Failed Writes If the `put` method (or other "write" operations) is unable to write the file to disk, `false` will be returned: - if (! Storage::put('file.jpg', $contents)) { - // The file could not be written to disk... - } +```php +if (! Storage::put('file.jpg', $contents)) { + // The file could not be written to disk... +} +``` If you wish, you may define the `throw` option within your filesystem disk's configuration array. When this option is defined as `true`, "write" methods such as `put` will throw an instance of `League\Flysystem\UnableToWriteFile` when write operations fail: - 'public' => [ - 'driver' => 'local', - // ... - 'throw' => true, - ], +```php +'public' => [ + 'driver' => 'local', + // ... + 'throw' => true, +], +``` ### Prepending and Appending To Files The `prepend` and `append` methods allow you to write to the beginning or end of a file: - Storage::prepend('file.log', 'Prepended Text'); +```php +Storage::prepend('file.log', 'Prepended Text'); - Storage::append('file.log', 'Appended Text'); +Storage::append('file.log', 'Appended Text'); +``` ### Copying and Moving Files The `copy` method may be used to copy an existing file to a new location on the disk, while the `move` method may be used to rename or move an existing file to a new location: - Storage::copy('old/file.jpg', 'new/file.jpg'); +```php +Storage::copy('old/file.jpg', 'new/file.jpg'); - Storage::move('old/file.jpg', 'new/file.jpg'); +Storage::move('old/file.jpg', 'new/file.jpg'); +``` ### Automatic Streaming Streaming files to storage offers significantly reduced memory usage. If you would like Laravel to automatically manage streaming a given file to your storage location, you may use the `putFile` or `putFileAs` method. This method accepts either an `Illuminate\Http\File` or `Illuminate\Http\UploadedFile` instance and will automatically stream the file to your desired location: - use Illuminate\Http\File; - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Http\File; +use Illuminate\Support\Facades\Storage; - // Automatically generate a unique ID for filename... - $path = Storage::putFile('photos', new File('/path/to/photo')); +// Automatically generate a unique ID for filename... +$path = Storage::putFile('photos', new File('/path/to/photo')); - // Manually specify a filename... - $path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg'); +// Manually specify a filename... +$path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg'); +``` There are a few important things to note about the `putFile` method. Note that we only specified a directory name and not a filename. By default, the `putFile` method will generate a unique ID to serve as the filename. The file's extension will be determined by examining the file's MIME type. The path to the file will be returned by the `putFile` method so you can store the path, including the generated filename, in your database. The `putFile` and `putFileAs` methods also accept an argument to specify the "visibility" of the stored file. This is particularly useful if you are storing the file on a cloud disk such as Amazon S3 and would like the file to be publicly accessible via generated URLs: - Storage::putFile('photos', new File('/path/to/photo'), 'public'); +```php +Storage::putFile('photos', new File('/path/to/photo'), 'public'); +``` ### File Uploads In web applications, one of the most common use-cases for storing files is storing user uploaded files such as photos and documents. Laravel makes it very easy to store uploaded files using the `store` method on an uploaded file instance. Call the `store` method with the path at which you wish to store the uploaded file: - file('avatar')->store('avatars'); - - return $path; - } + $path = $request->file('avatar')->store('avatars'); + + return $path; } +} +``` There are a few important things to note about this example. Note that we only specified a directory name, not a filename. By default, the `store` method will generate a unique ID to serve as the filename. The file's extension will be determined by examining the file's MIME type. The path to the file will be returned by the `store` method so you can store the path, including the generated filename, in your database. You may also call the `putFile` method on the `Storage` facade to perform the same file storage operation as the example above: - $path = Storage::putFile('avatars', $request->file('avatar')); +```php +$path = Storage::putFile('avatars', $request->file('avatar')); +``` #### Specifying a File Name If you do not want a filename to be automatically assigned to your stored file, you may use the `storeAs` method, which receives the path, the filename, and the (optional) disk as its arguments: - $path = $request->file('avatar')->storeAs( - 'avatars', $request->user()->id - ); +```php +$path = $request->file('avatar')->storeAs( + 'avatars', $request->user()->id +); +``` You may also use the `putFileAs` method on the `Storage` facade, which will perform the same file storage operation as the example above: - $path = Storage::putFileAs( - 'avatars', $request->file('avatar'), $request->user()->id - ); +```php +$path = Storage::putFileAs( + 'avatars', $request->file('avatar'), $request->user()->id +); +``` > [!WARNING] > Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. @@ -543,34 +611,42 @@ You may also use the `putFileAs` method on the `Storage` facade, which will perf By default, this uploaded file's `store` method will use your default disk. If you would like to specify another disk, pass the disk name as the second argument to the `store` method: - $path = $request->file('avatar')->store( - 'avatars/'.$request->user()->id, 's3' - ); +```php +$path = $request->file('avatar')->store( + 'avatars/'.$request->user()->id, 's3' +); +``` If you are using the `storeAs` method, you may pass the disk name as the third argument to the method: - $path = $request->file('avatar')->storeAs( - 'avatars', - $request->user()->id, - 's3' - ); +```php +$path = $request->file('avatar')->storeAs( + 'avatars', + $request->user()->id, + 's3' +); +``` #### Other Uploaded File Information If you would like to get the original name and extension of the uploaded file, you may do so using the `getClientOriginalName` and `getClientOriginalExtension` methods: - $file = $request->file('avatar'); +```php +$file = $request->file('avatar'); - $name = $file->getClientOriginalName(); - $extension = $file->getClientOriginalExtension(); +$name = $file->getClientOriginalName(); +$extension = $file->getClientOriginalExtension(); +``` However, keep in mind that the `getClientOriginalName` and `getClientOriginalExtension` methods are considered unsafe, as the file name and extension may be tampered with by a malicious user. For this reason, you should typically prefer the `hashName` and `extension` methods to get a name and an extension for the given file upload: - $file = $request->file('avatar'); +```php +$file = $request->file('avatar'); - $name = $file->hashName(); // Generate a unique, random name... - $extension = $file->extension(); // Determine the file's extension based on the file's MIME type... +$name = $file->hashName(); // Generate a unique, random name... +$extension = $file->extension(); // Determine the file's extension based on the file's MIME type... +``` ### File Visibility @@ -579,63 +655,81 @@ In Laravel's Flysystem integration, "visibility" is an abstraction of file permi You can set the visibility when writing the file via the `put` method: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - Storage::put('file.jpg', $contents, 'public'); +Storage::put('file.jpg', $contents, 'public'); +``` If the file has already been stored, its visibility can be retrieved and set via the `getVisibility` and `setVisibility` methods: - $visibility = Storage::getVisibility('file.jpg'); +```php +$visibility = Storage::getVisibility('file.jpg'); - Storage::setVisibility('file.jpg', 'public'); +Storage::setVisibility('file.jpg', 'public'); +``` When interacting with uploaded files, you may use the `storePublicly` and `storePubliclyAs` methods to store the uploaded file with `public` visibility: - $path = $request->file('avatar')->storePublicly('avatars', 's3'); +```php +$path = $request->file('avatar')->storePublicly('avatars', 's3'); - $path = $request->file('avatar')->storePubliclyAs( - 'avatars', - $request->user()->id, - 's3' - ); +$path = $request->file('avatar')->storePubliclyAs( + 'avatars', + $request->user()->id, + 's3' +); +``` #### Local Files and Visibility When using the `local` driver, `public` [visibility](#file-visibility) translates to `0755` permissions for directories and `0644` permissions for files. You can modify the permissions mappings in your application's `filesystems` configuration file: - 'local' => [ - 'driver' => 'local', - 'root' => storage_path('app'), - 'permissions' => [ - 'file' => [ - 'public' => 0644, - 'private' => 0600, - ], - 'dir' => [ - 'public' => 0755, - 'private' => 0700, - ], +```php + +``````php +'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'permissions' => [ + 'file' => [ + 'public' => 0644, + 'private' => 0600, + ], + 'dir' => [ + 'public' => 0755, + 'private' => 0700, ], - 'throw' => false, ], + 'throw' => false, +], +``` ## Deleting Files The `delete` method accepts a single filename or an array of files to delete: - use Illuminate\Support\Facades\Storage; +```php - Storage::delete('file.jpg'); +``````php +use Illuminate\Support\Facades\Storage; - Storage::delete(['file.jpg', 'file2.jpg']); +Storage::delete('file.jpg'); + +Storage::delete(['file.jpg', 'file2.jpg']); +``` If necessary, you may specify the disk that the file should be deleted from: - use Illuminate\Support\Facades\Storage; +```php - Storage::disk('s3')->delete('path/file.jpg'); +``````php +use Illuminate\Support\Facades\Storage; + +Storage::disk('s3')->delete('path/file.jpg'); +``` ## Directories @@ -645,34 +739,42 @@ If necessary, you may specify the disk that the file should be deleted from: The `files` method returns an array of all of the files in a given directory. If you would like to retrieve a list of all files within a given directory including all subdirectories, you may use the `allFiles` method: - use Illuminate\Support\Facades\Storage; +```php +use Illuminate\Support\Facades\Storage; - $files = Storage::files($directory); +$files = Storage::files($directory); - $files = Storage::allFiles($directory); +$files = Storage::allFiles($directory); +``` #### Get All Directories Within a Directory The `directories` method returns an array of all the directories within a given directory. Additionally, you may use the `allDirectories` method to get a list of all directories within a given directory and all of its subdirectories: - $directories = Storage::directories($directory); +```php +$directories = Storage::directories($directory); - $directories = Storage::allDirectories($directory); +$directories = Storage::allDirectories($directory); +``` #### Create a Directory The `makeDirectory` method will create the given directory, including any needed subdirectories: - Storage::makeDirectory($directory); +```php +Storage::makeDirectory($directory); +``` #### Delete a Directory Finally, the `deleteDirectory` method may be used to remove a directory and all of its files: - Storage::deleteDirectory($directory); +```php +Storage::deleteDirectory($directory); +``` ## Testing @@ -764,46 +866,48 @@ 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: - user()->fill([ - 'password' => Hash::make($request->newPassword) - ])->save(); - - return redirect('/profile'); - } + // Validate the new password length... + + $request->user()->fill([ + 'password' => Hash::make($request->newPassword) + ])->save(); + + return redirect('/profile'); } +} +``` #### Adjusting The Bcrypt Work Factor If you are using the Bcrypt algorithm, the `make` method allows you to manage the work factor of the algorithm using the `rounds` option; however, the default work factor managed by Laravel is acceptable for most applications: - $hashed = Hash::make('password', [ - 'rounds' => 12, - ]); +```php +$hashed = Hash::make('password', [ + 'rounds' => 12, +]); +``` #### Adjusting The Argon2 Work Factor If you are using the Argon2 algorithm, the `make` method allows you to manage the work factor of the algorithm using the `memory`, `time`, and `threads` options; however, the default values managed by Laravel are acceptable for most applications: - $hashed = Hash::make('password', [ - 'memory' => 1024, - 'time' => 2, - 'threads' => 2, - ]); +```php +$hashed = Hash::make('password', [ + 'memory' => 1024, + 'time' => 2, + 'threads' => 2, +]); +``` > [!NOTE] > For more information on these options, please refer to the [official PHP documentation regarding Argon hashing](https://secure.php.net/manual/en/function.password-hash.php). @@ -87,18 +93,22 @@ If you are using the Argon2 algorithm, the `make` method allows you to manage th The `check` method provided by the `Hash` facade allows you to verify that a given plain-text string corresponds to a given hash: - if (Hash::check('plain-text', $hashedPassword)) { - // The passwords match... - } +```php +if (Hash::check('plain-text', $hashedPassword)) { + // The passwords match... +} +``` ### Determining if a Password Needs to be Rehashed The `needsRehash` method provided by the `Hash` facade allows you to determine if the work factor used by the hasher has changed since the password was hashed. Some applications choose to perform this check during the application's authentication process: - if (Hash::needsRehash($hashed)) { - $hashed = Hash::make('plain-text'); - } +```php +if (Hash::needsRehash($hashed)) { + $hashed = Hash::make('plain-text'); +} +``` ## Hash Algorithm Verification diff --git a/helpers.md b/helpers.md index 40a271b0ded..df7d42e52f4 100644 --- a/helpers.md +++ b/helpers.md @@ -216,705 +216,799 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct The `Arr::accessible` method determines if the given value is array accessible: - use Illuminate\Support\Arr; - use Illuminate\Support\Collection; +```php +use Illuminate\Support\Arr; +use Illuminate\Support\Collection; - $isAccessible = Arr::accessible(['a' => 1, 'b' => 2]); +$isAccessible = Arr::accessible(['a' => 1, 'b' => 2]); - // true +// true - $isAccessible = Arr::accessible(new Collection); +$isAccessible = Arr::accessible(new Collection); - // true +// true - $isAccessible = Arr::accessible('abc'); +$isAccessible = Arr::accessible('abc'); - // false +// false - $isAccessible = Arr::accessible(new stdClass); +$isAccessible = Arr::accessible(new stdClass); - // false +// false +``` #### `Arr::add()` {.collection-method} The `Arr::add` method adds a given key / value pair to an array if the given key doesn't already exist in the array or is set to `null`: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = Arr::add(['name' => 'Desk'], 'price', 100); +$array = Arr::add(['name' => 'Desk'], 'price', 100); - // ['name' => 'Desk', 'price' => 100] +// ['name' => 'Desk', 'price' => 100] - $array = Arr::add(['name' => 'Desk', 'price' => null], 'price', 100); +$array = Arr::add(['name' => 'Desk', 'price' => null], 'price', 100); - // ['name' => 'Desk', 'price' => 100] +// ['name' => 'Desk', 'price' => 100] +``` #### `Arr::collapse()` {.collection-method} The `Arr::collapse` method collapses an array of arrays into a single array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = Arr::collapse([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); +$array = Arr::collapse([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); - // [1, 2, 3, 4, 5, 6, 7, 8, 9] +// [1, 2, 3, 4, 5, 6, 7, 8, 9] +``` #### `Arr::crossJoin()` {.collection-method} The `Arr::crossJoin` method cross joins the given arrays, returning a Cartesian product with all possible permutations: - use Illuminate\Support\Arr; - - $matrix = Arr::crossJoin([1, 2], ['a', 'b']); - - /* - [ - [1, 'a'], - [1, 'b'], - [2, 'a'], - [2, 'b'], - ] - */ - - $matrix = Arr::crossJoin([1, 2], ['a', 'b'], ['I', 'II']); - - /* - [ - [1, 'a', 'I'], - [1, 'a', 'II'], - [1, 'b', 'I'], - [1, 'b', 'II'], - [2, 'a', 'I'], - [2, 'a', 'II'], - [2, 'b', 'I'], - [2, 'b', 'II'], - ] - */ +```php +use Illuminate\Support\Arr; + +$matrix = Arr::crossJoin([1, 2], ['a', 'b']); + +/* + [ + [1, 'a'], + [1, 'b'], + [2, 'a'], + [2, 'b'], + ] +*/ + +$matrix = Arr::crossJoin([1, 2], ['a', 'b'], ['I', 'II']); + +/* + [ + [1, 'a', 'I'], + [1, 'a', 'II'], + [1, 'b', 'I'], + [1, 'b', 'II'], + [2, 'a', 'I'], + [2, 'a', 'II'], + [2, 'b', 'I'], + [2, 'b', 'II'], + ] +*/ +``` #### `Arr::divide()` {.collection-method} The `Arr::divide` method returns two arrays: one containing the keys and the other containing the values of the given array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - [$keys, $values] = Arr::divide(['name' => 'Desk']); +[$keys, $values] = Arr::divide(['name' => 'Desk']); - // $keys: ['name'] +// $keys: ['name'] - // $values: ['Desk'] +// $values: ['Desk'] +``` #### `Arr::dot()` {.collection-method} The `Arr::dot` method flattens a multi-dimensional array into a single level array that uses "dot" notation to indicate depth: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['products' => ['desk' => ['price' => 100]]]; +$array = ['products' => ['desk' => ['price' => 100]]]; - $flattened = Arr::dot($array); +$flattened = Arr::dot($array); - // ['products.desk.price' => 100] +// ['products.desk.price' => 100] +``` #### `Arr::except()` {.collection-method} The `Arr::except` method removes the given key / value pairs from an array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['name' => 'Desk', 'price' => 100]; +$array = ['name' => 'Desk', 'price' => 100]; - $filtered = Arr::except($array, ['price']); +$filtered = Arr::except($array, ['price']); - // ['name' => 'Desk'] +// ['name' => 'Desk'] +``` #### `Arr::exists()` {.collection-method} The `Arr::exists` method checks that the given key exists in the provided array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['name' => 'John Doe', 'age' => 17]; +$array = ['name' => 'John Doe', 'age' => 17]; - $exists = Arr::exists($array, 'name'); +$exists = Arr::exists($array, 'name'); - // true +// true - $exists = Arr::exists($array, 'salary'); +$exists = Arr::exists($array, 'salary'); - // false +// false +``` #### `Arr::first()` {.collection-method} The `Arr::first` method returns the first element of an array passing a given truth test: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [100, 200, 300]; +$array = [100, 200, 300]; - $first = Arr::first($array, function (int $value, int $key) { - return $value >= 150; - }); +$first = Arr::first($array, function (int $value, int $key) { + return $value >= 150; +}); - // 200 +// 200 +``` A default value may also be passed as the third parameter to the method. This value will be returned if no value passes the truth test: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $first = Arr::first($array, $callback, $default); +$first = Arr::first($array, $callback, $default); +``` #### `Arr::flatten()` {.collection-method} The `Arr::flatten` method flattens a multi-dimensional array into a single level array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['name' => 'Joe', 'languages' => ['PHP', 'Ruby']]; +$array = ['name' => 'Joe', 'languages' => ['PHP', 'Ruby']]; - $flattened = Arr::flatten($array); +$flattened = Arr::flatten($array); - // ['Joe', 'PHP', 'Ruby'] +// ['Joe', 'PHP', 'Ruby'] +``` #### `Arr::forget()` {.collection-method} The `Arr::forget` method removes a given key / value pair from a deeply nested array using "dot" notation: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['products' => ['desk' => ['price' => 100]]]; +$array = ['products' => ['desk' => ['price' => 100]]]; - Arr::forget($array, 'products.desk'); +Arr::forget($array, 'products.desk'); - // ['products' => []] +// ['products' => []] +``` #### `Arr::get()` {.collection-method} The `Arr::get` method retrieves a value from a deeply nested array using "dot" notation: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['products' => ['desk' => ['price' => 100]]]; +$array = ['products' => ['desk' => ['price' => 100]]]; - $price = Arr::get($array, 'products.desk.price'); +$price = Arr::get($array, 'products.desk.price'); - // 100 +// 100 +``` The `Arr::get` method also accepts a default value, which will be returned if the specified key is not present in the array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $discount = Arr::get($array, 'products.desk.discount', 0); +$discount = Arr::get($array, 'products.desk.discount', 0); - // 0 +// 0 +``` #### `Arr::has()` {.collection-method} The `Arr::has` method checks whether a given item or items exists in an array using "dot" notation: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['product' => ['name' => 'Desk', 'price' => 100]]; +$array = ['product' => ['name' => 'Desk', 'price' => 100]]; - $contains = Arr::has($array, 'product.name'); +$contains = Arr::has($array, 'product.name'); - // true +// true - $contains = Arr::has($array, ['product.price', 'product.discount']); +$contains = Arr::has($array, ['product.price', 'product.discount']); - // false +// false +``` #### `Arr::hasAny()` {.collection-method} The `Arr::hasAny` method checks whether any item in a given set exists in an array using "dot" notation: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['product' => ['name' => 'Desk', 'price' => 100]]; +$array = ['product' => ['name' => 'Desk', 'price' => 100]]; - $contains = Arr::hasAny($array, 'product.name'); +$contains = Arr::hasAny($array, 'product.name'); - // true +// true - $contains = Arr::hasAny($array, ['product.name', 'product.discount']); +$contains = Arr::hasAny($array, ['product.name', 'product.discount']); - // true +// true - $contains = Arr::hasAny($array, ['category', 'product.discount']); +$contains = Arr::hasAny($array, ['category', 'product.discount']); - // false +// false +``` #### `Arr::isAssoc()` {.collection-method} The `Arr::isAssoc` method returns `true` if the given array is an associative array. An array is considered "associative" if it doesn't have sequential numerical keys beginning with zero: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $isAssoc = Arr::isAssoc(['product' => ['name' => 'Desk', 'price' => 100]]); +$isAssoc = Arr::isAssoc(['product' => ['name' => 'Desk', 'price' => 100]]); - // true +// true - $isAssoc = Arr::isAssoc([1, 2, 3]); +$isAssoc = Arr::isAssoc([1, 2, 3]); - // false +// false +``` #### `Arr::isList()` {.collection-method} The `Arr::isList` method returns `true` if the given array's keys are sequential integers beginning from zero: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $isList = Arr::isList(['foo', 'bar', 'baz']); +$isList = Arr::isList(['foo', 'bar', 'baz']); - // true +// true - $isList = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); +$isList = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); - // false +// false +``` #### `Arr::join()` {.collection-method} The `Arr::join` method joins array elements with a string. Using this method's second argument, you may also specify the joining string for the final element of the array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['Tailwind', 'Alpine', 'Laravel', 'Livewire']; +$array = ['Tailwind', 'Alpine', 'Laravel', 'Livewire']; - $joined = Arr::join($array, ', '); +$joined = Arr::join($array, ', '); - // Tailwind, Alpine, Laravel, Livewire +// Tailwind, Alpine, Laravel, Livewire - $joined = Arr::join($array, ', ', ' and '); +$joined = Arr::join($array, ', ', ' and '); - // Tailwind, Alpine, Laravel and Livewire +// Tailwind, Alpine, Laravel and Livewire +``` #### `Arr::keyBy()` {.collection-method} The `Arr::keyBy` method keys the array by the given key. If multiple items have the same key, only the last one will appear in the new array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - ['product_id' => 'prod-100', 'name' => 'Desk'], - ['product_id' => 'prod-200', 'name' => 'Chair'], - ]; +$array = [ + ['product_id' => 'prod-100', 'name' => 'Desk'], + ['product_id' => 'prod-200', 'name' => 'Chair'], +]; - $keyed = Arr::keyBy($array, 'product_id'); +$keyed = Arr::keyBy($array, 'product_id'); - /* - [ - 'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'], - 'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'], - ] - */ +/* + [ + 'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'], + 'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'], + ] +*/ +``` #### `Arr::last()` {.collection-method} The `Arr::last` method returns the last element of an array passing a given truth test: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [100, 200, 300, 110]; +$array = [100, 200, 300, 110]; - $last = Arr::last($array, function (int $value, int $key) { - return $value >= 150; - }); +$last = Arr::last($array, function (int $value, int $key) { + return $value >= 150; +}); - // 300 +// 300 +``` A default value may be passed as the third argument to the method. This value will be returned if no value passes the truth test: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $last = Arr::last($array, $callback, $default); +$last = Arr::last($array, $callback, $default); +``` #### `Arr::map()` {.collection-method} The `Arr::map` method iterates through the array and passes each value and key to the given callback. The array value is replaced by the value returned by the callback: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['first' => 'james', 'last' => 'kirk']; +$array = ['first' => 'james', 'last' => 'kirk']; - $mapped = Arr::map($array, function (string $value, string $key) { - return ucfirst($value); - }); +$mapped = Arr::map($array, function (string $value, string $key) { + return ucfirst($value); +}); - // ['first' => 'James', 'last' => 'Kirk'] +// ['first' => 'James', 'last' => 'Kirk'] +``` #### `Arr::mapSpread()` {.collection-method} The `Arr::mapSpread` method iterates over the array, passing each nested item value into the given closure. The closure is free to modify the item and return it, thus forming a new array of modified items: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - [0, 1], - [2, 3], - [4, 5], - [6, 7], - [8, 9], - ]; +$array = [ + [0, 1], + [2, 3], + [4, 5], + [6, 7], + [8, 9], +]; - $mapped = Arr::mapSpread($array, function (int $even, int $odd) { - return $even + $odd; - }); +$mapped = Arr::mapSpread($array, function (int $even, int $odd) { + return $even + $odd; +}); - /* - [1, 5, 9, 13, 17] - */ +/* + [1, 5, 9, 13, 17] +*/ +``` #### `Arr::mapWithKeys()` {.collection-method} The `Arr::mapWithKeys` method iterates through the array and passes each value to the given callback. The callback should return an associative array containing a single key / value pair: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - [ - 'name' => 'John', - 'department' => 'Sales', - 'email' => 'john@example.com', - ], - [ - 'name' => 'Jane', - 'department' => 'Marketing', - 'email' => 'jane@example.com', - ] - ]; - - $mapped = Arr::mapWithKeys($array, function (array $item, int $key) { - return [$item['email'] => $item['name']]; - }); +$array = [ + [ + 'name' => 'John', + 'department' => 'Sales', + 'email' => 'john@example.com', + ], + [ + 'name' => 'Jane', + 'department' => 'Marketing', + 'email' => 'jane@example.com', + ] +]; - /* - [ - 'john@example.com' => 'John', - 'jane@example.com' => 'Jane', - ] - */ +$mapped = Arr::mapWithKeys($array, function (array $item, int $key) { + return [$item['email'] => $item['name']]; +}); + +/* + [ + 'john@example.com' => 'John', + 'jane@example.com' => 'Jane', + ] +*/ +``` #### `Arr::only()` {.collection-method} The `Arr::only` method returns only the specified key / value pairs from the given array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['name' => 'Desk', 'price' => 100, 'orders' => 10]; +$array = ['name' => 'Desk', 'price' => 100, 'orders' => 10]; - $slice = Arr::only($array, ['name', 'price']); +$slice = Arr::only($array, ['name', 'price']); - // ['name' => 'Desk', 'price' => 100] +// ['name' => 'Desk', 'price' => 100] +``` #### `Arr::pluck()` {.collection-method} The `Arr::pluck` method retrieves all of the values for a given key from an array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - ['developer' => ['id' => 1, 'name' => 'Taylor']], - ['developer' => ['id' => 2, 'name' => 'Abigail']], - ]; +$array = [ + ['developer' => ['id' => 1, 'name' => 'Taylor']], + ['developer' => ['id' => 2, 'name' => 'Abigail']], +]; - $names = Arr::pluck($array, 'developer.name'); +$names = Arr::pluck($array, 'developer.name'); - // ['Taylor', 'Abigail'] +// ['Taylor', 'Abigail'] +``` You may also specify how you wish the resulting list to be keyed: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $names = Arr::pluck($array, 'developer.name', 'developer.id'); +$names = Arr::pluck($array, 'developer.name', 'developer.id'); - // [1 => 'Taylor', 2 => 'Abigail'] +// [1 => 'Taylor', 2 => 'Abigail'] +``` #### `Arr::prepend()` {.collection-method} The `Arr::prepend` method will push an item onto the beginning of an array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['one', 'two', 'three', 'four']; +$array = ['one', 'two', 'three', 'four']; - $array = Arr::prepend($array, 'zero'); +$array = Arr::prepend($array, 'zero'); - // ['zero', 'one', 'two', 'three', 'four'] +// ['zero', 'one', 'two', 'three', 'four'] +``` If needed, you may specify the key that should be used for the value: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['price' => 100]; +$array = ['price' => 100]; - $array = Arr::prepend($array, 'Desk', 'name'); +$array = Arr::prepend($array, 'Desk', 'name'); - // ['name' => 'Desk', 'price' => 100] +// ['name' => 'Desk', 'price' => 100] +``` #### `Arr::prependKeysWith()` {.collection-method} The `Arr::prependKeysWith` prepends all key names of an associative array with the given prefix: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - 'name' => 'Desk', - 'price' => 100, - ]; +$array = [ + 'name' => 'Desk', + 'price' => 100, +]; - $keyed = Arr::prependKeysWith($array, 'product.'); +$keyed = Arr::prependKeysWith($array, 'product.'); - /* - [ - 'product.name' => 'Desk', - 'product.price' => 100, - ] - */ +/* + [ + 'product.name' => 'Desk', + 'product.price' => 100, + ] +*/ +``` #### `Arr::pull()` {.collection-method} The `Arr::pull` method returns and removes a key / value pair from an array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['name' => 'Desk', 'price' => 100]; +$array = ['name' => 'Desk', 'price' => 100]; - $name = Arr::pull($array, 'name'); +$name = Arr::pull($array, 'name'); - // $name: Desk +// $name: Desk - // $array: ['price' => 100] +// $array: ['price' => 100] +``` A default value may be passed as the third argument to the method. This value will be returned if the key doesn't exist: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $value = Arr::pull($array, $key, $default); +$value = Arr::pull($array, $key, $default); +``` #### `Arr::query()` {.collection-method} The `Arr::query` method converts the array into a query string: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - 'name' => 'Taylor', - 'order' => [ - 'column' => 'created_at', - 'direction' => 'desc' - ] - ]; +$array = [ + 'name' => 'Taylor', + 'order' => [ + 'column' => 'created_at', + 'direction' => 'desc' + ] +]; - Arr::query($array); +Arr::query($array); - // name=Taylor&order[column]=created_at&order[direction]=desc +// name=Taylor&order[column]=created_at&order[direction]=desc +``` #### `Arr::random()` {.collection-method} The `Arr::random` method returns a random value from an array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [1, 2, 3, 4, 5]; +$array = [1, 2, 3, 4, 5]; - $random = Arr::random($array); +$random = Arr::random($array); - // 4 - (retrieved randomly) +// 4 - (retrieved randomly) +``` You may also specify the number of items to return as an optional second argument. Note that providing this argument will return an array even if only one item is desired: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $items = Arr::random($array, 2); +$items = Arr::random($array, 2); - // [2, 5] - (retrieved randomly) +// [2, 5] - (retrieved randomly) +``` #### `Arr::set()` {.collection-method} The `Arr::set` method sets a value within a deeply nested array using "dot" notation: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['products' => ['desk' => ['price' => 100]]]; +$array = ['products' => ['desk' => ['price' => 100]]]; - Arr::set($array, 'products.desk.price', 200); +Arr::set($array, 'products.desk.price', 200); - // ['products' => ['desk' => ['price' => 200]]] +// ['products' => ['desk' => ['price' => 200]]] +``` #### `Arr::shuffle()` {.collection-method} The `Arr::shuffle` method randomly shuffles the items in the array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = Arr::shuffle([1, 2, 3, 4, 5]); +$array = Arr::shuffle([1, 2, 3, 4, 5]); - // [3, 2, 5, 1, 4] - (generated randomly) +// [3, 2, 5, 1, 4] - (generated randomly) +``` #### `Arr::sort()` {.collection-method} The `Arr::sort` method sorts an array by its values: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['Desk', 'Table', 'Chair']; +$array = ['Desk', 'Table', 'Chair']; - $sorted = Arr::sort($array); +$sorted = Arr::sort($array); - // ['Chair', 'Desk', 'Table'] +// ['Chair', 'Desk', 'Table'] +``` You may also sort the array by the results of a given closure: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - ['name' => 'Desk'], - ['name' => 'Table'], - ['name' => 'Chair'], - ]; +$array = [ + ['name' => 'Desk'], + ['name' => 'Table'], + ['name' => 'Chair'], +]; - $sorted = array_values(Arr::sort($array, function (array $value) { - return $value['name']; - })); +$sorted = array_values(Arr::sort($array, function (array $value) { + return $value['name']; +})); - /* - [ - ['name' => 'Chair'], - ['name' => 'Desk'], - ['name' => 'Table'], - ] - */ +/* + [ + ['name' => 'Chair'], + ['name' => 'Desk'], + ['name' => 'Table'], + ] +*/ +``` #### `Arr::sortDesc()` {.collection-method} The `Arr::sortDesc` method sorts an array in descending order by its values: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = ['Desk', 'Table', 'Chair']; +$array = ['Desk', 'Table', 'Chair']; - $sorted = Arr::sortDesc($array); +$sorted = Arr::sortDesc($array); - // ['Table', 'Desk', 'Chair'] +// ['Table', 'Desk', 'Chair'] +``` You may also sort the array by the results of a given closure: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - ['name' => 'Desk'], - ['name' => 'Table'], - ['name' => 'Chair'], - ]; +$array = [ + ['name' => 'Desk'], + ['name' => 'Table'], + ['name' => 'Chair'], +]; - $sorted = array_values(Arr::sortDesc($array, function (array $value) { - return $value['name']; - })); +$sorted = array_values(Arr::sortDesc($array, function (array $value) { + return $value['name']; +})); - /* - [ - ['name' => 'Table'], - ['name' => 'Desk'], - ['name' => 'Chair'], - ] - */ +/* + [ + ['name' => 'Table'], + ['name' => 'Desk'], + ['name' => 'Chair'], + ] +*/ +``` #### `Arr::sortRecursive()` {.collection-method} The `Arr::sortRecursive` method recursively sorts an array using the `sort` function for numerically indexed sub-arrays and the `ksort` function for associative sub-arrays: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - ['Roman', 'Taylor', 'Li'], - ['PHP', 'Ruby', 'JavaScript'], - ['one' => 1, 'two' => 2, 'three' => 3], - ]; +$array = [ + ['Roman', 'Taylor', 'Li'], + ['PHP', 'Ruby', 'JavaScript'], + ['one' => 1, 'two' => 2, 'three' => 3], +]; - $sorted = Arr::sortRecursive($array); +$sorted = Arr::sortRecursive($array); - /* - [ - ['JavaScript', 'PHP', 'Ruby'], - ['one' => 1, 'three' => 3, 'two' => 2], - ['Li', 'Roman', 'Taylor'], - ] - */ +/* + [ + ['JavaScript', 'PHP', 'Ruby'], + ['one' => 1, 'three' => 3, 'two' => 2], + ['Li', 'Roman', 'Taylor'], + ] +*/ +``` If you would like the results sorted in descending order, you may use the `Arr::sortRecursiveDesc` method. - $sorted = Arr::sortRecursiveDesc($array); +```php +$sorted = Arr::sortRecursiveDesc($array); +``` #### `Arr::take()` {.collection-method} The `Arr::take` method returns a new array with the specified number of items: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [0, 1, 2, 3, 4, 5]; +$array = [0, 1, 2, 3, 4, 5]; - $chunk = Arr::take($array, 3); +$chunk = Arr::take($array, 3); - // [0, 1, 2] +// [0, 1, 2] +``` You may also pass a negative integer to take the specified number of items from the end of the array: - $array = [0, 1, 2, 3, 4, 5]; +```php +$array = [0, 1, 2, 3, 4, 5]; - $chunk = Arr::take($array, -2); +$chunk = Arr::take($array, -2); - // [4, 5] +// [4, 5] +``` #### `Arr::toCssClasses()` {.collection-method} The `Arr::toCssClasses` method conditionally compiles a CSS class string. The method accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $isActive = false; - $hasError = true; +$isActive = false; +$hasError = true; - $array = ['p-4', 'font-bold' => $isActive, 'bg-red' => $hasError]; +$array = ['p-4', 'font-bold' => $isActive, 'bg-red' => $hasError]; - $classes = Arr::toCssClasses($array); +$classes = Arr::toCssClasses($array); - /* - 'p-4 bg-red' - */ +/* + 'p-4 bg-red' +*/ +``` #### `Arr::toCssStyles()` {.collection-method} @@ -942,233 +1036,269 @@ This method powers Laravel's functionality allowing [merging classes with a Blad The `Arr::undot` method expands a single-dimensional array that uses "dot" notation into a multi-dimensional array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [ - 'user.name' => 'Kevin Malone', - 'user.occupation' => 'Accountant', - ]; +$array = [ + 'user.name' => 'Kevin Malone', + 'user.occupation' => 'Accountant', +]; - $array = Arr::undot($array); +$array = Arr::undot($array); - // ['user' => ['name' => 'Kevin Malone', 'occupation' => 'Accountant']] +// ['user' => ['name' => 'Kevin Malone', 'occupation' => 'Accountant']] +``` #### `Arr::where()` {.collection-method} The `Arr::where` method filters an array using the given closure: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [100, '200', 300, '400', 500]; +$array = [100, '200', 300, '400', 500]; - $filtered = Arr::where($array, function (string|int $value, int $key) { - return is_string($value); - }); +$filtered = Arr::where($array, function (string|int $value, int $key) { + return is_string($value); +}); - // [1 => '200', 3 => '400'] +// [1 => '200', 3 => '400'] +``` #### `Arr::whereNotNull()` {.collection-method} The `Arr::whereNotNull` method removes all `null` values from the given array: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = [0, null]; +$array = [0, null]; - $filtered = Arr::whereNotNull($array); +$filtered = Arr::whereNotNull($array); - // [0 => 0] +// [0 => 0] +``` #### `Arr::wrap()` {.collection-method} The `Arr::wrap` method wraps the given value in an array. If the given value is already an array it will be returned without modification: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $string = 'Laravel'; +$string = 'Laravel'; - $array = Arr::wrap($string); +$array = Arr::wrap($string); - // ['Laravel'] +// ['Laravel'] +``` If the given value is `null`, an empty array will be returned: - use Illuminate\Support\Arr; +```php +use Illuminate\Support\Arr; - $array = Arr::wrap(null); +$array = Arr::wrap(null); - // [] +// [] +``` #### `data_fill()` {.collection-method} The `data_fill` function sets a missing value within a nested array or object using "dot" notation: - $data = ['products' => ['desk' => ['price' => 100]]]; +```php +$data = ['products' => ['desk' => ['price' => 100]]]; - data_fill($data, 'products.desk.price', 200); +data_fill($data, 'products.desk.price', 200); - // ['products' => ['desk' => ['price' => 100]]] +// ['products' => ['desk' => ['price' => 100]]] - data_fill($data, 'products.desk.discount', 10); +data_fill($data, 'products.desk.discount', 10); - // ['products' => ['desk' => ['price' => 100, 'discount' => 10]]] +// ['products' => ['desk' => ['price' => 100, 'discount' => 10]]] +``` This function also accepts asterisks as wildcards and will fill the target accordingly: - $data = [ +```php +$data = [ + 'products' => [ + ['name' => 'Desk 1', 'price' => 100], + ['name' => 'Desk 2'], + ], +]; + +data_fill($data, 'products.*.price', 200); + +/* + [ 'products' => [ ['name' => 'Desk 1', 'price' => 100], - ['name' => 'Desk 2'], + ['name' => 'Desk 2', 'price' => 200], ], - ]; - - data_fill($data, 'products.*.price', 200); - - /* - [ - 'products' => [ - ['name' => 'Desk 1', 'price' => 100], - ['name' => 'Desk 2', 'price' => 200], - ], - ] - */ + ] +*/ +``` #### `data_get()` {.collection-method} The `data_get` function retrieves a value from a nested array or object using "dot" notation: - $data = ['products' => ['desk' => ['price' => 100]]]; +```php +$data = ['products' => ['desk' => ['price' => 100]]]; - $price = data_get($data, 'products.desk.price'); +$price = data_get($data, 'products.desk.price'); - // 100 +// 100 +``` The `data_get` function also accepts a default value, which will be returned if the specified key is not found: - $discount = data_get($data, 'products.desk.discount', 0); +```php +$discount = data_get($data, 'products.desk.discount', 0); - // 0 +// 0 +``` The function also accepts wildcards using asterisks, which may target any key of the array or object: - $data = [ - 'product-one' => ['name' => 'Desk 1', 'price' => 100], - 'product-two' => ['name' => 'Desk 2', 'price' => 150], - ]; +```php +$data = [ + 'product-one' => ['name' => 'Desk 1', 'price' => 100], + 'product-two' => ['name' => 'Desk 2', 'price' => 150], +]; - data_get($data, '*.name'); +data_get($data, '*.name'); - // ['Desk 1', 'Desk 2']; +// ['Desk 1', 'Desk 2']; +``` The `{first}` and `{last}` placeholders may be used to retrieve the first or last items in an array: - $flight = [ - 'segments' => [ - ['from' => 'LHR', 'departure' => '9:00', 'to' => 'IST', 'arrival' => '15:00'], - ['from' => 'IST', 'departure' => '16:00', 'to' => 'PKX', 'arrival' => '20:00'], - ], - ]; +```php +$flight = [ + 'segments' => [ + ['from' => 'LHR', 'departure' => '9:00', 'to' => 'IST', 'arrival' => '15:00'], + ['from' => 'IST', 'departure' => '16:00', 'to' => 'PKX', 'arrival' => '20:00'], + ], +]; - data_get($flight, 'segments.{first}.arrival'); +data_get($flight, 'segments.{first}.arrival'); - // 15:00 +// 15:00 +``` #### `data_set()` {.collection-method} The `data_set` function sets a value within a nested array or object using "dot" notation: - $data = ['products' => ['desk' => ['price' => 100]]]; +```php +$data = ['products' => ['desk' => ['price' => 100]]]; - data_set($data, 'products.desk.price', 200); +data_set($data, 'products.desk.price', 200); - // ['products' => ['desk' => ['price' => 200]]] +// ['products' => ['desk' => ['price' => 200]]] +``` This function also accepts wildcards using asterisks and will set values on the target accordingly: - $data = [ - 'products' => [ - ['name' => 'Desk 1', 'price' => 100], - ['name' => 'Desk 2', 'price' => 150], - ], - ]; +```php +$data = [ + 'products' => [ + ['name' => 'Desk 1', 'price' => 100], + ['name' => 'Desk 2', 'price' => 150], + ], +]; - data_set($data, 'products.*.price', 200); +data_set($data, 'products.*.price', 200); - /* - [ - 'products' => [ - ['name' => 'Desk 1', 'price' => 200], - ['name' => 'Desk 2', 'price' => 200], - ], - ] - */ +/* + [ + 'products' => [ + ['name' => 'Desk 1', 'price' => 200], + ['name' => 'Desk 2', 'price' => 200], + ], + ] +*/ +``` By default, any existing values are overwritten. If you wish to only set a value if it doesn't exist, you may pass `false` as the fourth argument to the function: - $data = ['products' => ['desk' => ['price' => 100]]]; +```php +$data = ['products' => ['desk' => ['price' => 100]]]; - data_set($data, 'products.desk.price', 200, overwrite: false); +data_set($data, 'products.desk.price', 200, overwrite: false); - // ['products' => ['desk' => ['price' => 100]]] +// ['products' => ['desk' => ['price' => 100]]] +``` #### `data_forget()` {.collection-method} The `data_forget` function removes a value within a nested array or object using "dot" notation: - $data = ['products' => ['desk' => ['price' => 100]]]; +```php +$data = ['products' => ['desk' => ['price' => 100]]]; - data_forget($data, 'products.desk.price'); +data_forget($data, 'products.desk.price'); - // ['products' => ['desk' => []]] +// ['products' => ['desk' => []]] +``` This function also accepts wildcards using asterisks and will remove values on the target accordingly: - $data = [ - 'products' => [ - ['name' => 'Desk 1', 'price' => 100], - ['name' => 'Desk 2', 'price' => 150], - ], - ]; +```php +$data = [ + 'products' => [ + ['name' => 'Desk 1', 'price' => 100], + ['name' => 'Desk 2', 'price' => 150], + ], +]; - data_forget($data, 'products.*.price'); +data_forget($data, 'products.*.price'); - /* - [ - 'products' => [ - ['name' => 'Desk 1'], - ['name' => 'Desk 2'], - ], - ] - */ +/* + [ + 'products' => [ + ['name' => 'Desk 1'], + ['name' => 'Desk 2'], + ], + ] +*/ +``` #### `head()` {.collection-method} The `head` function returns the first element in the given array: - $array = [100, 200, 300]; +```php +$array = [100, 200, 300]; - $first = head($array); +$first = head($array); - // 100 +// 100 +``` #### `last()` {.collection-method} The `last` function returns the last element in the given array: - $array = [100, 200, 300]; +```php +$array = [100, 200, 300]; - $last = last($array); +$last = last($array); - // 300 +// 300 +``` ## Numbers @@ -1178,163 +1308,181 @@ The `last` function returns the last element in the given array: The `Number::abbreviate` method returns the human-readable format of the provided numerical value, with an abbreviation for the units: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::abbreviate(1000); +$number = Number::abbreviate(1000); - // 1K +// 1K - $number = Number::abbreviate(489939); +$number = Number::abbreviate(489939); - // 490K +// 490K - $number = Number::abbreviate(1230000, precision: 2); +$number = Number::abbreviate(1230000, precision: 2); - // 1.23M +// 1.23M +``` #### `Number::clamp()` {.collection-method} The `Number::clamp` method ensures a given number stays within a specified range. If the number is lower than the minimum, the minimum value is returned. If the number is higher than the maximum, the maximum value is returned: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::clamp(105, min: 10, max: 100); +$number = Number::clamp(105, min: 10, max: 100); - // 100 +// 100 - $number = Number::clamp(5, min: 10, max: 100); +$number = Number::clamp(5, min: 10, max: 100); - // 10 +// 10 - $number = Number::clamp(10, min: 10, max: 100); +$number = Number::clamp(10, min: 10, max: 100); - // 10 +// 10 - $number = Number::clamp(20, min: 10, max: 100); +$number = Number::clamp(20, min: 10, max: 100); - // 20 +// 20 +``` #### `Number::currency()` {.collection-method} The `Number::currency` method returns the currency representation of the given value as a string: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $currency = Number::currency(1000); +$currency = Number::currency(1000); - // $1,000.00 +// $1,000.00 - $currency = Number::currency(1000, in: 'EUR'); +$currency = Number::currency(1000, in: 'EUR'); - // €1,000.00 +// €1,000.00 - $currency = Number::currency(1000, in: 'EUR', locale: 'de'); +$currency = Number::currency(1000, in: 'EUR', locale: 'de'); - // 1.000,00 € +// 1.000,00 € +``` #### `Number::defaultCurrency()` {.collection-method} The `Number::defaultCurrency` method returns the default currency being used by the `Number` class: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $currency = Number::defaultCurrency(); +$currency = Number::defaultCurrency(); - // USD +// USD +``` #### `Number::defaultLocale()` {.collection-method} The `Number::defaultLocale` method returns the default locale being used by the `Number` class: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $locale = Number::defaultLocale(); +$locale = Number::defaultLocale(); - // en +// en +``` #### `Number::fileSize()` {.collection-method} The `Number::fileSize` method returns the file size representation of the given byte value as a string: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $size = Number::fileSize(1024); +$size = Number::fileSize(1024); - // 1 KB +// 1 KB - $size = Number::fileSize(1024 * 1024); +$size = Number::fileSize(1024 * 1024); - // 1 MB +// 1 MB - $size = Number::fileSize(1024, precision: 2); +$size = Number::fileSize(1024, precision: 2); - // 1.00 KB +// 1.00 KB +``` #### `Number::forHumans()` {.collection-method} The `Number::forHumans` method returns the human-readable format of the provided numerical value: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::forHumans(1000); +$number = Number::forHumans(1000); - // 1 thousand +// 1 thousand - $number = Number::forHumans(489939); +$number = Number::forHumans(489939); - // 490 thousand +// 490 thousand - $number = Number::forHumans(1230000, precision: 2); +$number = Number::forHumans(1230000, precision: 2); - // 1.23 million +// 1.23 million +``` #### `Number::format()` {.collection-method} The `Number::format` method formats the given number into a locale specific string: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::format(100000); +$number = Number::format(100000); - // 100,000 +// 100,000 - $number = Number::format(100000, precision: 2); +$number = Number::format(100000, precision: 2); - // 100,000.00 +// 100,000.00 - $number = Number::format(100000.123, maxPrecision: 2); +$number = Number::format(100000.123, maxPrecision: 2); - // 100,000.12 +// 100,000.12 - $number = Number::format(100000, locale: 'de'); +$number = Number::format(100000, locale: 'de'); - // 100.000 +// 100.000 +``` #### `Number::ordinal()` {.collection-method} The `Number::ordinal` method returns a number's ordinal representation: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::ordinal(1); +$number = Number::ordinal(1); - // 1st +// 1st - $number = Number::ordinal(2); +$number = Number::ordinal(2); - // 2nd +// 2nd - $number = Number::ordinal(21); +$number = Number::ordinal(21); - // 21st +// 21st +``` #### `Number::pairs()` {.collection-method} @@ -1358,125 +1506,143 @@ $result = Number::pairs(25, 10, offset: 0); The `Number::percentage` method returns the percentage representation of the given value as a string: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $percentage = Number::percentage(10); +$percentage = Number::percentage(10); - // 10% +// 10% - $percentage = Number::percentage(10, precision: 2); +$percentage = Number::percentage(10, precision: 2); - // 10.00% +// 10.00% - $percentage = Number::percentage(10.123, maxPrecision: 2); +$percentage = Number::percentage(10.123, maxPrecision: 2); - // 10.12% +// 10.12% - $percentage = Number::percentage(10, precision: 2, locale: 'de'); +$percentage = Number::percentage(10, precision: 2, locale: 'de'); - // 10,00% +// 10,00% +``` #### `Number::spell()` {.collection-method} The `Number::spell` method transforms the given number into a string of words: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::spell(102); +$number = Number::spell(102); - // one hundred and two +// one hundred and two - $number = Number::spell(88, locale: 'fr'); +$number = Number::spell(88, locale: 'fr'); - // quatre-vingt-huit +// quatre-vingt-huit +``` The `after` argument allows you to specify a value after which all numbers should be spelled out: - $number = Number::spell(10, after: 10); +```php +$number = Number::spell(10, after: 10); - // 10 +// 10 - $number = Number::spell(11, after: 10); +$number = Number::spell(11, after: 10); - // eleven +// eleven +``` The `until` argument allows you to specify a value before which all numbers should be spelled out: - $number = Number::spell(5, until: 10); +```php +$number = Number::spell(5, until: 10); - // five +// five - $number = Number::spell(10, until: 10); +$number = Number::spell(10, until: 10); - // 10 +// 10 +``` #### `Number::trim()` {.collection-method} The `Number::trim` method removes any trailing zero digits after the decimal point of the given number: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::trim(12.0); +$number = Number::trim(12.0); - // 12 +// 12 - $number = Number::trim(12.30); +$number = Number::trim(12.30); - // 12.3 +// 12.3 +``` #### `Number::useLocale()` {.collection-method} The `Number::useLocale` method sets the default number locale globally, which affects how numbers and currency are formatted by subsequent invocations to the `Number` class's methods: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Number::useLocale('de'); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Number::useLocale('de'); +} +``` #### `Number::withLocale()` {.collection-method} The `Number::withLocale` method executes the given closure using the specified locale and then restores the original locale after the callback has executed: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::withLocale('de', function () { - return Number::format(1500); - }); +$number = Number::withLocale('de', function () { + return Number::format(1500); +}); +``` #### `Number::useCurrency()` {.collection-method} The `Number::useCurrency` method sets the default number currency globally, which affects how the currency is formatted by subsequent invocations to the `Number` class's methods: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Number::useCurrency('GBP'); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Number::useCurrency('GBP'); +} +``` #### `Number::withCurrency()` {.collection-method} The `Number::withCurrency` method executes the given closure using the specified currency and then restores the original currency after the callback has executed: - use Illuminate\Support\Number; +```php +use Illuminate\Support\Number; - $number = Number::withCurrency('GBP', function () { - // ... - }); +$number = Number::withCurrency('GBP', function () { + // ... +}); +``` ## Paths @@ -1486,45 +1652,55 @@ The `Number::withCurrency` method executes the given closure using the specified The `app_path` function returns the fully qualified path to your application's `app` directory. You may also use the `app_path` function to generate a fully qualified path to a file relative to the application directory: - $path = app_path(); +```php +$path = app_path(); - $path = app_path('Http/Controllers/Controller.php'); +$path = app_path('Http/Controllers/Controller.php'); +``` #### `base_path()` {.collection-method} The `base_path` function returns the fully qualified path to your application's root directory. You may also use the `base_path` function to generate a fully qualified path to a given file relative to the project root directory: - $path = base_path(); +```php +$path = base_path(); - $path = base_path('vendor/bin'); +$path = base_path('vendor/bin'); +``` #### `config_path()` {.collection-method} The `config_path` function returns the fully qualified path to your application's `config` directory. You may also use the `config_path` function to generate a fully qualified path to a given file within the application's configuration directory: - $path = config_path(); +```php +$path = config_path(); - $path = config_path('app.php'); +$path = config_path('app.php'); +``` #### `database_path()` {.collection-method} The `database_path` function returns the fully qualified path to your application's `database` directory. You may also use the `database_path` function to generate a fully qualified path to a given file within the database directory: - $path = database_path(); +```php +$path = database_path(); - $path = database_path('factories/UserFactory.php'); +$path = database_path('factories/UserFactory.php'); +``` #### `lang_path()` {.collection-method} The `lang_path` function returns the fully qualified path to your application's `lang` directory. You may also use the `lang_path` function to generate a fully qualified path to a given file within the directory: - $path = lang_path(); +```php +$path = lang_path(); - $path = lang_path('en/messages.php'); +$path = lang_path('en/messages.php'); +``` > [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -1534,34 +1710,42 @@ The `lang_path` function returns the fully qualified path to your application's The `mix` function returns the path to a [versioned Mix file](/docs/{{version}}/mix): - $path = mix('css/app.css'); +```php +$path = mix('css/app.css'); +``` #### `public_path()` {.collection-method} The `public_path` function returns the fully qualified path to your application's `public` directory. You may also use the `public_path` function to generate a fully qualified path to a given file within the public directory: - $path = public_path(); +```php +$path = public_path(); - $path = public_path('css/app.css'); +$path = public_path('css/app.css'); +``` #### `resource_path()` {.collection-method} The `resource_path` function returns the fully qualified path to your application's `resources` directory. You may also use the `resource_path` function to generate a fully qualified path to a given file within the resources directory: - $path = resource_path(); +```php +$path = resource_path(); - $path = resource_path('sass/app.scss'); +$path = resource_path('sass/app.scss'); +``` #### `storage_path()` {.collection-method} The `storage_path` function returns the fully qualified path to your application's `storage` directory. You may also use the `storage_path` function to generate a fully qualified path to a given file within the storage directory: - $path = storage_path(); +```php +$path = storage_path(); - $path = storage_path('app/file.txt'); +$path = storage_path('app/file.txt'); +``` ## URLs @@ -1571,85 +1755,111 @@ The `storage_path` function returns the fully qualified path to your application The `action` function generates a URL for the given controller action: - use App\Http\Controllers\HomeController; +```php +use App\Http\Controllers\HomeController; - $url = action([HomeController::class, 'index']); +$url = action([HomeController::class, 'index']); +``` If the method accepts route parameters, you may pass them as the second argument to the method: - $url = action([UserController::class, 'profile'], ['id' => 1]); +```php +$url = action([UserController::class, 'profile'], ['id' => 1]); +``` #### `asset()` {.collection-method} The `asset` function generates a URL for an asset using the current scheme of the request (HTTP or HTTPS): - $url = asset('img/photo.jpg'); +```php +$url = asset('img/photo.jpg'); +``` You can configure the asset URL host by setting the `ASSET_URL` variable in your `.env` file. This can be useful if you host your assets on an external service like Amazon S3 or another CDN: - // ASSET_URL=http://example.com/assets +```php +// ASSET_URL=http://example.com/assets - $url = asset('img/photo.jpg'); // http://example.com/assets/img/photo.jpg +$url = asset('img/photo.jpg'); // http://example.com/assets/img/photo.jpg +``` #### `route()` {.collection-method} The `route` function generates a URL for a given [named route](/docs/{{version}}/routing#named-routes): - $url = route('route.name'); +```php +$url = route('route.name'); +``` If the route accepts parameters, you may pass them as the second argument to the function: - $url = route('route.name', ['id' => 1]); +```php +$url = route('route.name', ['id' => 1]); +``` By default, the `route` function generates an absolute URL. If you wish to generate a relative URL, you may pass `false` as the third argument to the function: - $url = route('route.name', ['id' => 1], false); +```php +$url = route('route.name', ['id' => 1], false); +``` #### `secure_asset()` {.collection-method} The `secure_asset` function generates a URL for an asset using HTTPS: - $url = secure_asset('img/photo.jpg'); +```php +$url = secure_asset('img/photo.jpg'); +``` #### `secure_url()` {.collection-method} The `secure_url` function generates a fully qualified HTTPS URL to the given path. Additional URL segments may be passed in the function's second argument: - $url = secure_url('https://github.com/user/profile'); +```php +$url = secure_url('https://github.com/user/profile'); - $url = secure_url('https://github.com/user/profile',%20[1]); +$url = secure_url('https://github.com/user/profile',%20[1]); +``` #### `to_route()` {.collection-method} The `to_route` function generates a [redirect HTTP response](/docs/{{version}}/responses#redirects) for a given [named route](/docs/{{version}}/routing#named-routes): - return to_route('users.show', ['user' => 1]); +```php +return to_route('users.show', ['user' => 1]); +``` If necessary, you may pass the HTTP status code that should be assigned to the redirect and any additional response headers as the third and fourth arguments to the `to_route` method: - return to_route('users.show', ['user' => 1], 302, ['X-Framework' => 'Laravel']); +```php +return to_route('users.show', ['user' => 1], 302, ['X-Framework' => 'Laravel']); +``` #### `url()` {.collection-method} The `url` function generates a fully qualified URL to the given path: - $url = url('https://github.com/user/profile'); +```php +$url = url('https://github.com/user/profile'); - $url = url('https://github.com/user/profile',%20[1]); +$url = url('https://github.com/user/profile',%20[1]); +``` If no path is provided, an `Illuminate\Routing\UrlGenerator` instance is returned: - $current = url()->current(); +```php +$current = url()->current(); - $full = url()->full(); +$full = url()->full(); - $previous = url()->previous(); +$previous = url()->previous(); +``` ## Miscellaneous @@ -1659,18 +1869,24 @@ If no path is provided, an `Illuminate\Routing\UrlGenerator` instance is returne The `abort` function throws [an HTTP exception](/docs/{{version}}/errors#http-exceptions) which will be rendered by the [exception handler](/docs/{{version}}/errors#handling-exceptions): - abort(403); +```php +abort(403); +``` You may also provide the exception's message and custom HTTP response headers that should be sent to the browser: - abort(403, 'Unauthorized.', $headers); +```php +abort(403, 'Unauthorized.', $headers); +``` #### `abort_if()` {.collection-method} The `abort_if` function throws an HTTP exception if a given boolean expression evaluates to `true`: - abort_if(! Auth::user()->isAdmin(), 403); +```php +abort_if(! Auth::user()->isAdmin(), 403); +``` Like the `abort` method, you may also provide the exception's response text as the third argument and an array of custom response headers as the fourth argument to the function. @@ -1679,7 +1895,9 @@ Like the `abort` method, you may also provide the exception's response text as t The `abort_unless` function throws an HTTP exception if a given boolean expression evaluates to `false`: - abort_unless(Auth::user()->isAdmin(), 403); +```php +abort_unless(Auth::user()->isAdmin(), 403); +``` Like the `abort` method, you may also provide the exception's response text as the third argument and an array of custom response headers as the fourth argument to the function. @@ -1688,56 +1906,70 @@ Like the `abort` method, you may also provide the exception's response text as t The `app` function returns the [service container](/docs/{{version}}/container) instance: - $container = app(); +```php +$container = app(); +``` You may pass a class or interface name to resolve it from the container: - $api = app('HelpSpot\API'); +```php +$api = app('HelpSpot\API'); +``` #### `auth()` {.collection-method} The `auth` function returns an [authenticator](/docs/{{version}}/authentication) instance. You may use it as an alternative to the `Auth` facade: - $user = auth()->user(); +```php +$user = auth()->user(); +``` If needed, you may specify which guard instance you would like to access: - $user = auth('admin')->user(); +```php +$user = auth('admin')->user(); +``` #### `back()` {.collection-method} The `back` function generates a [redirect HTTP response](/docs/{{version}}/responses#redirects) to the user's previous location: - return back($status = 302, $headers = [], $fallback = '/'); +```php +return back($status = 302, $headers = [], $fallback = '/'); - return back(); +return back(); +``` #### `bcrypt()` {.collection-method} The `bcrypt` function [hashes](/docs/{{version}}/hashing) the given value using Bcrypt. You may use this function as an alternative to the `Hash` facade: - $password = bcrypt('my-secret-password'); +```php +$password = bcrypt('my-secret-password'); +``` #### `blank()` {.collection-method} The `blank` function determines whether the given value is "blank": - blank(''); - blank(' '); - blank(null); - blank(collect()); +```php +blank(''); +blank(' '); +blank(null); +blank(collect()); - // true +// true - blank(0); - blank(true); - blank(false); +blank(0); +blank(true); +blank(false); - // false +// false +``` For the inverse of `blank`, see the [`filled`](#method-filled) method. @@ -1746,103 +1978,131 @@ For the inverse of `blank`, see the [`filled`](#method-filled) method. The `broadcast` function [broadcasts](/docs/{{version}}/broadcasting) the given [event](/docs/{{version}}/events) to its listeners: - broadcast(new UserRegistered($user)); +```php +broadcast(new UserRegistered($user)); - broadcast(new UserRegistered($user))->toOthers(); +broadcast(new UserRegistered($user))->toOthers(); +``` #### `cache()` {.collection-method} The `cache` function may be used to get values from the [cache](/docs/{{version}}/cache). If the given key does not exist in the cache, an optional default value will be returned: - $value = cache('key'); +```php +$value = cache('key'); - $value = cache('key', 'default'); +$value = cache('key', 'default'); +``` You may add items to the cache by passing an array of key / value pairs to the function. You should also pass the number of seconds or duration the cached value should be considered valid: - cache(['key' => 'value'], 300); +```php +cache(['key' => 'value'], 300); - cache(['key' => 'value'], now()->addSeconds(10)); +cache(['key' => 'value'], now()->addSeconds(10)); +``` #### `class_uses_recursive()` {.collection-method} The `class_uses_recursive` function returns all traits used by a class, including traits used by all of its parent classes: - $traits = class_uses_recursive(App\Models\User::class); +```php +$traits = class_uses_recursive(App\Models\User::class); +``` #### `collect()` {.collection-method} The `collect` function creates a [collection](/docs/{{version}}/collections) instance from the given value: - $collection = collect(['taylor', 'abigail']); +```php +$collection = collect(['taylor', 'abigail']); +``` #### `config()` {.collection-method} The `config` function gets the value of a [configuration](/docs/{{version}}/configuration) variable. The configuration values may be accessed using "dot" syntax, which includes the name of the file and the option you wish to access. A default value may be specified and is returned if the configuration option does not exist: - $value = config('app.timezone'); +```php +$value = config('app.timezone'); - $value = config('app.timezone', $default); +$value = config('app.timezone', $default); +``` You may set configuration variables at runtime by passing an array of key / value pairs. However, note that this function only affects the configuration value for the current request and does not update your actual configuration values: - config(['app.debug' => true]); +```php +config(['app.debug' => true]); +``` #### `context()` {.collection-method} The `context` function gets the value from the [current context](/docs/{{version}}/context). A default value may be specified and is returned if the context key does not exist: - $value = context('trace_id'); +```php +$value = context('trace_id'); - $value = context('trace_id', $default); +$value = context('trace_id', $default); +``` You may set context values by passing an array of key / value pairs: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - context(['trace_id' => Str::uuid()->toString()]); +context(['trace_id' => Str::uuid()->toString()]); +``` #### `cookie()` {.collection-method} The `cookie` function creates a new [cookie](/docs/{{version}}/requests#cookies) instance: - $cookie = cookie('name', 'value', $minutes); +```php +$cookie = cookie('name', 'value', $minutes); +``` #### `csrf_field()` {.collection-method} The `csrf_field` function generates an HTML `hidden` input field containing the value of the CSRF token. For example, using [Blade syntax](/docs/{{version}}/blade): - {{ csrf_field() }} +```blade +{{ csrf_field() }} +``` #### `csrf_token()` {.collection-method} The `csrf_token` function retrieves the value of the current CSRF token: - $token = csrf_token(); +```php +$token = csrf_token(); +``` #### `decrypt()` {.collection-method} The `decrypt` function [decrypts](/docs/{{version}}/encryption) the given value. You may use this function as an alternative to the `Crypt` facade: - $password = decrypt($value); +```php +$password = decrypt($value); +``` #### `dd()` {.collection-method} The `dd` function dumps the given variables and ends the execution of the script: - dd($value); +```php +dd($value); - dd($value1, $value2, $value3, ...); +dd($value1, $value2, $value3, ...); +``` If you do not want to halt the execution of your script, use the [`dump`](#method-dump) function instead. @@ -1851,23 +2111,29 @@ If you do not want to halt the execution of your script, use the [`dump`](#metho The `dispatch` function pushes the given [job](/docs/{{version}}/queues#creating-jobs) onto the Laravel [job queue](/docs/{{version}}/queues): - dispatch(new App\Jobs\SendEmails); +```php +dispatch(new App\Jobs\SendEmails); +``` #### `dispatch_sync()` {.collection-method} The `dispatch_sync` function pushes the given job to the [sync](/docs/{{version}}/queues#synchronous-dispatching) queue so that it is processed immediately: - dispatch_sync(new App\Jobs\SendEmails); +```php +dispatch_sync(new App\Jobs\SendEmails); +``` #### `dump()` {.collection-method} The `dump` function dumps the given variables: - dump($value); +```php +dump($value); - dump($value1, $value2, $value3, ...); +dump($value1, $value2, $value3, ...); +``` If you want to stop executing the script after dumping the variables, use the [`dd`](#method-dd) function instead. @@ -1876,16 +2142,20 @@ If you want to stop executing the script after dumping the variables, use the [` The `encrypt` function [encrypts](/docs/{{version}}/encryption) the given value. You may use this function as an alternative to the `Crypt` facade: - $secret = encrypt('my-secret-value'); +```php +$secret = encrypt('my-secret-value'); +``` #### `env()` {.collection-method} The `env` function retrieves the value of an [environment variable](/docs/{{version}}/configuration#environment-configuration) or returns a default value: - $env = env('APP_ENV'); +```php +$env = env('APP_ENV'); - $env = env('APP_ENV', 'production'); +$env = env('APP_ENV', 'production'); +``` > [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. @@ -1895,7 +2165,9 @@ The `env` function retrieves the value of an [environment variable](/docs/{{vers The `event` function dispatches the given [event](/docs/{{version}}/events) to its listeners: - event(new UserRegistered($user)); +```php +event(new UserRegistered($user)); +``` #### `fake()` {.collection-method} @@ -1916,25 +2188,29 @@ The `fake` function resolves a [Faker](https://github.com/FakerPHP/Faker) single By default, the `fake` function will utilize the `app.faker_locale` configuration option in your `config/app.php` configuration. Typically, this configuration option is set via the `APP_FAKER_LOCALE` environment variable. You may also specify the locale by passing it to the `fake` function. Each locale will resolve an individual singleton: - fake('nl_NL')->name() +```php +fake('nl_NL')->name() +``` #### `filled()` {.collection-method} The `filled` function determines whether the given value is not "blank": - filled(0); - filled(true); - filled(false); +```php +filled(0); +filled(true); +filled(false); - // true +// true - filled(''); - filled(' '); - filled(null); - filled(collect()); +filled(''); +filled(' '); +filled(null); +filled(collect()); - // false +// false +``` For the inverse of `filled`, see the [`blank`](#method-blank) method. @@ -1943,88 +2219,110 @@ For the inverse of `filled`, see the [`blank`](#method-blank) method. The `info` function will write information to your application's [log](/docs/{{version}}/logging): - info('Some helpful information!'); +```php +info('Some helpful information!'); +``` An array of contextual data may also be passed to the function: - info('User login attempt failed.', ['id' => $user->id]); +```php +info('User login attempt failed.', ['id' => $user->id]); +``` #### `literal()` {.collection-method} The `literal` function creates a new [stdClass](https://www.php.net/manual/en/class.stdclass.php) instance with the given named arguments as properties: - $obj = literal( - name: 'Joe', - languages: ['PHP', 'Ruby'], - ); +```php +$obj = literal( + name: 'Joe', + languages: ['PHP', 'Ruby'], +); - $obj->name; // 'Joe' - $obj->languages; // ['PHP', 'Ruby'] +$obj->name; // 'Joe' +$obj->languages; // ['PHP', 'Ruby'] +``` #### `logger()` {.collection-method} The `logger` function can be used to write a `debug` level message to the [log](/docs/{{version}}/logging): - logger('Debug message'); +```php +logger('Debug message'); +``` An array of contextual data may also be passed to the function: - logger('User has logged in.', ['id' => $user->id]); +```php +logger('User has logged in.', ['id' => $user->id]); +``` A [logger](/docs/{{version}}/logging) instance will be returned if no value is passed to the function: - logger()->error('You are not allowed here.'); +```php +logger()->error('You are not allowed here.'); +``` #### `method_field()` {.collection-method} The `method_field` function generates an HTML `hidden` input field containing the spoofed value of the form's HTTP verb. For example, using [Blade syntax](/docs/{{version}}/blade): - - {{ method_field('DELETE') }} - +```blade +
    + {{ method_field('DELETE') }} +
    +``` #### `now()` {.collection-method} The `now` function creates a new `Illuminate\Support\Carbon` instance for the current time: - $now = now(); +```php +$now = now(); +``` #### `old()` {.collection-method} The `old` function [retrieves](/docs/{{version}}/requests#retrieving-input) an [old input](/docs/{{version}}/requests#old-input) value flashed into the session: - $value = old('value'); +```php +$value = old('value'); - $value = old('value', 'default'); +$value = old('value', 'default'); +``` Since the "default value" provided as the second argument to the `old` function is often an attribute of an Eloquent model, Laravel allows you to simply pass the entire Eloquent model as the second argument to the `old` function. When doing so, Laravel will assume the first argument provided to the `old` function is the name of the Eloquent attribute that should be considered the "default value": - {{ old('name', $user->name) }} +```blade +{{ old('name', $user->name) }} - // Is equivalent to... +// Is equivalent to... - {{ old('name', $user) }} +{{ old('name', $user) }} +``` #### `once()` {.collection-method} The `once` function executes the given callback and caches the result in memory for the duration of the request. Any subsequent calls to the `once` function with the same callback will return the previously cached result: - function random(): int - { - return once(function () { - return random_int(1, 1000); - }); - } +```php +function random(): int +{ + return once(function () { + return random_int(1, 1000); + }); +} - random(); // 123 - random(); // 123 (cached result) - random(); // 123 (cached result) +random(); // 123 +random(); // 123 (cached result) +random(); // 123 (cached result) +``` When the `once` function is executed from within an object instance, the cached result will be unique to that object instance: @@ -2054,318 +2352,390 @@ $secondService->all(); // (cached result) The `optional` function accepts any argument and allows you to access properties or call methods on that object. If the given object is `null`, properties and methods will return `null` instead of causing an error: - return optional($user->address)->street; +```php +return optional($user->address)->street; - {!! old('name', optional($user)->name) !!} +{!! old('name', optional($user)->name) !!} +``` The `optional` function also accepts a closure as its second argument. The closure will be invoked if the value provided as the first argument is not null: - return optional(User::find($id), function (User $user) { - return $user->name; - }); +```php +return optional(User::find($id), function (User $user) { + return $user->name; +}); +``` #### `policy()` {.collection-method} The `policy` method retrieves a [policy](/docs/{{version}}/authorization#creating-policies) instance for a given class: - $policy = policy(App\Models\User::class); +```php +$policy = policy(App\Models\User::class); +``` #### `redirect()` {.collection-method} The `redirect` function returns a [redirect HTTP response](/docs/{{version}}/responses#redirects), or returns the redirector instance if called with no arguments: - return redirect($to = null, $status = 302, $headers = [], $https = null); +```php +return redirect($to = null, $status = 302, $headers = [], $https = null); - return redirect('/home'); +return redirect('/home'); - return redirect()->route('route.name'); +return redirect()->route('route.name'); +``` #### `report()` {.collection-method} The `report` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions): - report($e); +```php +report($e); +``` The `report` function also accepts a string as an argument. When a string is given to the function, the function will create an exception with the given string as its message: - report('Something went wrong.'); +```php +report('Something went wrong.'); +``` #### `report_if()` {.collection-method} The `report_if` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if the given condition is `true`: - report_if($shouldReport, $e); +```php +report_if($shouldReport, $e); - report_if($shouldReport, 'Something went wrong.'); +report_if($shouldReport, 'Something went wrong.'); +``` #### `report_unless()` {.collection-method} The `report_unless` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if the given condition is `false`: - report_unless($reportingDisabled, $e); +```php +report_unless($reportingDisabled, $e); - report_unless($reportingDisabled, 'Something went wrong.'); +report_unless($reportingDisabled, 'Something went wrong.'); +``` #### `request()` {.collection-method} The `request` function returns the current [request](/docs/{{version}}/requests) instance or obtains an input field's value from the current request: - $request = request(); +```php +$request = request(); - $value = request('key', $default); +$value = request('key', $default); +``` #### `rescue()` {.collection-method} The `rescue` function executes the given closure and catches any exceptions that occur during its execution. All exceptions that are caught will be sent to your [exception handler](/docs/{{version}}/errors#handling-exceptions); however, the request will continue processing: - return rescue(function () { - return $this->method(); - }); +```php +return rescue(function () { + return $this->method(); +}); +``` You may also pass a second argument to the `rescue` function. This argument will be the "default" value that should be returned if an exception occurs while executing the closure: - return rescue(function () { - return $this->method(); - }, false); - - return rescue(function () { - return $this->method(); - }, function () { - return $this->failure(); - }); +```php +return rescue(function () { + return $this->method(); +}, false); + +return rescue(function () { + return $this->method(); +}, function () { + return $this->failure(); +}); +``` A `report` argument may be provided to the `rescue` function to determine if the exception should be reported via the `report` function: - return rescue(function () { - return $this->method(); - }, report: function (Throwable $throwable) { - return $throwable instanceof InvalidArgumentException; - }); +```php +return rescue(function () { + return $this->method(); +}, report: function (Throwable $throwable) { + return $throwable instanceof InvalidArgumentException; +}); +``` #### `resolve()` {.collection-method} The `resolve` function resolves a given class or interface name to an instance using the [service container](/docs/{{version}}/container): - $api = resolve('HelpSpot\API'); +```php +$api = resolve('HelpSpot\API'); +``` #### `response()` {.collection-method} The `response` function creates a [response](/docs/{{version}}/responses) instance or obtains an instance of the response factory: - return response('Hello World', 200, $headers); +```php +return response('Hello World', 200, $headers); - return response()->json(['foo' => 'bar'], 200, $headers); +return response()->json(['foo' => 'bar'], 200, $headers); +``` #### `retry()` {.collection-method} The `retry` function attempts to execute the given callback until the given maximum attempt threshold is met. If the callback does not throw an exception, its return value will be returned. If the callback throws an exception, it will automatically be retried. If the maximum attempt count is exceeded, the exception will be thrown: - return retry(5, function () { - // Attempt 5 times while resting 100ms between attempts... - }, 100); +```php +return retry(5, function () { + // Attempt 5 times while resting 100ms between attempts... +}, 100); +``` If you would like to manually calculate the number of milliseconds to sleep between attempts, you may pass a closure as the third argument to the `retry` function: - use Exception; +```php +use Exception; - return retry(5, function () { - // ... - }, function (int $attempt, Exception $exception) { - return $attempt * 100; - }); +return retry(5, function () { + // ... +}, function (int $attempt, Exception $exception) { + return $attempt * 100; +}); +``` For convenience, you may provide an array as the first argument to the `retry` function. This array will be used to determine how many milliseconds to sleep between subsequent attempts: - return retry([100, 200], function () { - // Sleep for 100ms on first retry, 200ms on second retry... - }); +```php +return retry([100, 200], function () { + // Sleep for 100ms on first retry, 200ms on second retry... +}); +``` To only retry under specific conditions, you may pass a closure as the fourth argument to the `retry` function: - use Exception; +```php +use Exception; - return retry(5, function () { - // ... - }, 100, function (Exception $exception) { - return $exception instanceof RetryException; - }); +return retry(5, function () { + // ... +}, 100, function (Exception $exception) { + return $exception instanceof RetryException; +}); +``` #### `session()` {.collection-method} The `session` function may be used to get or set [session](/docs/{{version}}/session) values: - $value = session('key'); +```php +$value = session('key'); +``` You may set values by passing an array of key / value pairs to the function: - session(['chairs' => 7, 'instruments' => 3]); +```php +session(['chairs' => 7, 'instruments' => 3]); +``` The session store will be returned if no value is passed to the function: - $value = session()->get('key'); +```php +$value = session()->get('key'); - session()->put('key', $value); +session()->put('key', $value); +``` #### `tap()` {.collection-method} The `tap` function accepts two arguments: an arbitrary `$value` and a closure. The `$value` will be passed to the closure and then be returned by the `tap` function. The return value of the closure is irrelevant: - $user = tap(User::first(), function (User $user) { - $user->name = 'taylor'; +```php +$user = tap(User::first(), function (User $user) { + $user->name = 'taylor'; - $user->save(); - }); + $user->save(); +}); +``` If no closure is passed to the `tap` function, you may call any method on the given `$value`. The return value of the method you call will always be `$value`, regardless of what the method actually returns in its definition. For example, the Eloquent `update` method typically returns an integer. However, we can force the method to return the model itself by chaining the `update` method call through the `tap` function: - $user = tap($user)->update([ - 'name' => $name, - 'email' => $email, - ]); +```php +$user = tap($user)->update([ + 'name' => $name, + 'email' => $email, +]); +``` To add a `tap` method to a class, you may add the `Illuminate\Support\Traits\Tappable` trait to the class. The `tap` method of this trait accepts a Closure as its only argument. The object instance itself will be passed to the Closure and then be returned by the `tap` method: - return $user->tap(function (User $user) { - // ... - }); +```php +return $user->tap(function (User $user) { + // ... +}); +``` #### `throw_if()` {.collection-method} The `throw_if` function throws the given exception if a given boolean expression evaluates to `true`: - throw_if(! Auth::user()->isAdmin(), AuthorizationException::class); +```php +throw_if(! Auth::user()->isAdmin(), AuthorizationException::class); - throw_if( - ! Auth::user()->isAdmin(), - AuthorizationException::class, - 'You are not allowed to access this page.' - ); +throw_if( + ! Auth::user()->isAdmin(), + AuthorizationException::class, + 'You are not allowed to access this page.' +); +``` #### `throw_unless()` {.collection-method} The `throw_unless` function throws the given exception if a given boolean expression evaluates to `false`: - throw_unless(Auth::user()->isAdmin(), AuthorizationException::class); +```php +throw_unless(Auth::user()->isAdmin(), AuthorizationException::class); - throw_unless( - Auth::user()->isAdmin(), - AuthorizationException::class, - 'You are not allowed to access this page.' - ); +throw_unless( + Auth::user()->isAdmin(), + AuthorizationException::class, + 'You are not allowed to access this page.' +); +``` #### `today()` {.collection-method} The `today` function creates a new `Illuminate\Support\Carbon` instance for the current date: - $today = today(); +```php +$today = today(); +``` #### `trait_uses_recursive()` {.collection-method} The `trait_uses_recursive` function returns all traits used by a trait: - $traits = trait_uses_recursive(\Illuminate\Notifications\Notifiable::class); +```php +$traits = trait_uses_recursive(\Illuminate\Notifications\Notifiable::class); +``` #### `transform()` {.collection-method} The `transform` function executes a closure on a given value if the value is not [blank](#method-blank) and then returns the return value of the closure: - $callback = function (int $value) { - return $value * 2; - }; +```php +$callback = function (int $value) { + return $value * 2; +}; - $result = transform(5, $callback); +$result = transform(5, $callback); - // 10 +// 10 +``` A default value or closure may be passed as the third argument to the function. This value will be returned if the given value is blank: - $result = transform(null, $callback, 'The value is blank'); +```php +$result = transform(null, $callback, 'The value is blank'); - // The value is blank +// The value is blank +``` #### `validator()` {.collection-method} The `validator` function creates a new [validator](/docs/{{version}}/validation) instance with the given arguments. You may use it as an alternative to the `Validator` facade: - $validator = validator($data, $rules, $messages); +```php +$validator = validator($data, $rules, $messages); +``` #### `value()` {.collection-method} The `value` function returns the value it is given. However, if you pass a closure to the function, the closure will be executed and its returned value will be returned: - $result = value(true); +```php +$result = value(true); - // true +// true - $result = value(function () { - return false; - }); +$result = value(function () { + return false; +}); - // false +// false +``` Additional arguments may be passed to the `value` function. If the first argument is a closure then the additional parameters will be passed to the closure as arguments, otherwise they will be ignored: - $result = value(function (string $name) { - return $name; - }, 'Taylor'); +```php +$result = value(function (string $name) { + return $name; +}, 'Taylor'); - // 'Taylor' +// 'Taylor' +``` #### `view()` {.collection-method} The `view` function retrieves a [view](/docs/{{version}}/views) instance: - return view('auth.login'); +```php +return view('auth.login'); +``` #### `with()` {.collection-method} The `with` function returns the value it is given. If a closure is passed as the second argument to the function, the closure will be executed and its returned value will be returned: - $callback = function (mixed $value) { - return is_numeric($value) ? $value * 2 : 0; - }; +```php +$callback = function (mixed $value) { + return is_numeric($value) ? $value * 2 : 0; +}; - $result = with(5, $callback); +$result = with(5, $callback); - // 10 +// 10 - $result = with(null, $callback); +$result = with(null, $callback); - // 0 +// 0 - $result = with(5, null); +$result = with(5, null); - // 5 +// 5 +``` #### `when()` {.collection-method} The `when` function returns the value it is given if a given condition evaluates to `true`. Otherwise, `null` is returned. If a closure is passed as the second argument to the function, the closure will be executed and its returned value will be returned: - $value = when(true, 'Hello World'); +```php +$value = when(true, 'Hello World'); - $value = when(true, fn () => 'Hello World'); +$value = when(true, fn () => 'Hello World'); +``` The `when` function is primarily useful for conditionally rendering HTML attributes: @@ -2383,27 +2753,33 @@ The `when` function is primarily useful for conditionally rendering HTML attribu Sometimes you may wish to quickly test the performance of certain parts of your application. On those occasions, you may utilize the `Benchmark` support class to measure the number of milliseconds it takes for the given callbacks to complete: - User::find(1)); // 0.1 ms +Benchmark::dd(fn () => User::find(1)); // 0.1 ms - Benchmark::dd([ - 'Scenario 1' => fn () => User::count(), // 0.5 ms - 'Scenario 2' => fn () => User::all()->count(), // 20.0 ms - ]); +Benchmark::dd([ + 'Scenario 1' => fn () => User::count(), // 0.5 ms + 'Scenario 2' => fn () => User::all()->count(), // 20.0 ms +]); +``` By default, the given callbacks will be executed once (one iteration), and their duration will be displayed in the browser / console. To invoke a callback more than once, you may specify the number of iterations that the callback should be invoked as the second argument to the method. When executing a callback more than once, the `Benchmark` class will return the average amount of milliseconds it took to execute the callback across all iterations: - Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms +```php +Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms +``` Sometimes, you may want to benchmark the execution of a callback while still obtaining the value returned by the callback. The `value` method will return a tuple containing the value returned by the callback and the amount of milliseconds it took to execute the callback: - [$count, $duration] = Benchmark::value(fn () => User::count()); +```php +[$count, $duration] = Benchmark::value(fn () => User::count()); +``` ### Dates @@ -2531,40 +2907,46 @@ abstract class TestCase extends BaseTestCase Laravel's lottery class may be used to execute callbacks based on a set of given odds. This can be particularly useful when you only want to execute code for a percentage of your incoming requests: - use Illuminate\Support\Lottery; +```php +use Illuminate\Support\Lottery; - Lottery::odds(1, 20) - ->winner(fn () => $user->won()) - ->loser(fn () => $user->lost()) - ->choose(); +Lottery::odds(1, 20) + ->winner(fn () => $user->won()) + ->loser(fn () => $user->lost()) + ->choose(); +``` You may combine Laravel's lottery class with other Laravel features. For example, you may wish to only report a small percentage of slow queries to your exception handler. And, since the lottery class is callable, we may pass an instance of the class into any method that accepts callables: - use Carbon\CarbonInterval; - use Illuminate\Support\Facades\DB; - use Illuminate\Support\Lottery; - - DB::whenQueryingForLongerThan( - CarbonInterval::seconds(2), - Lottery::odds(1, 100)->winner(fn () => report('Querying > 2 seconds.')), - ); +```php +use Carbon\CarbonInterval; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Lottery; + +DB::whenQueryingForLongerThan( + CarbonInterval::seconds(2), + Lottery::odds(1, 100)->winner(fn () => report('Querying > 2 seconds.')), +); +``` #### Testing Lotteries Laravel provides some simple methods to allow you to easily test your application's lottery invocations: - // Lottery will always win... - Lottery::alwaysWin(); +```php +// Lottery will always win... +Lottery::alwaysWin(); - // Lottery will always lose... - Lottery::alwaysLose(); +// Lottery will always lose... +Lottery::alwaysLose(); - // Lottery will win then lose, and finally return to normal behavior... - Lottery::fix([true, false]); +// Lottery will win then lose, and finally return to normal behavior... +Lottery::fix([true, false]); - // Lottery will return to normal behavior... - Lottery::determineResultsNormally(); +// Lottery will return to normal behavior... +Lottery::determineResultsNormally(); +``` ### Pipeline @@ -2613,63 +2995,71 @@ $user = Pipeline::send($user) Laravel's `Sleep` class is a light-weight wrapper around PHP's native `sleep` and `usleep` functions, offering greater testability while also exposing a developer friendly API for working with time: - use Illuminate\Support\Sleep; +```php +use Illuminate\Support\Sleep; - $waiting = true; +$waiting = true; - while ($waiting) { - Sleep::for(1)->second(); +while ($waiting) { + Sleep::for(1)->second(); - $waiting = /* ... */; - } + $waiting = /* ... */; +} +``` The `Sleep` class offers a variety of methods that allow you to work with different units of time: - // Return a value after sleeping... - $result = Sleep::for(1)->second()->then(fn () => 1 + 1); +```php +// Return a value after sleeping... +$result = Sleep::for(1)->second()->then(fn () => 1 + 1); - // Sleep while a given value is true... - Sleep::for(1)->second()->while(fn () => shouldKeepSleeping()); +// Sleep while a given value is true... +Sleep::for(1)->second()->while(fn () => shouldKeepSleeping()); - // Pause execution for 90 seconds... - Sleep::for(1.5)->minutes(); +// Pause execution for 90 seconds... +Sleep::for(1.5)->minutes(); - // Pause execution for 2 seconds... - Sleep::for(2)->seconds(); +// Pause execution for 2 seconds... +Sleep::for(2)->seconds(); - // Pause execution for 500 milliseconds... - Sleep::for(500)->milliseconds(); +// Pause execution for 500 milliseconds... +Sleep::for(500)->milliseconds(); - // Pause execution for 5,000 microseconds... - Sleep::for(5000)->microseconds(); +// Pause execution for 5,000 microseconds... +Sleep::for(5000)->microseconds(); - // Pause execution until a given time... - Sleep::until(now()->addMinute()); +// Pause execution until a given time... +Sleep::until(now()->addMinute()); - // Alias of PHP's native "sleep" function... - Sleep::sleep(2); +// Alias of PHP's native "sleep" function... +Sleep::sleep(2); - // Alias of PHP's native "usleep" function... - Sleep::usleep(5000); +// Alias of PHP's native "usleep" function... +Sleep::usleep(5000); +``` To easily combine units of time, you may use the `and` method: - Sleep::for(1)->second()->and(10)->milliseconds(); +```php +Sleep::for(1)->second()->and(10)->milliseconds(); +``` #### Testing Sleep When testing code that utilizes the `Sleep` class or PHP's native sleep functions, your test will pause execution. As you might expect, this makes your test suite significantly slower. For example, imagine you are testing the following code: - $waiting = /* ... */; +```php +$waiting = /* ... */; - $seconds = 1; +$seconds = 1; - while ($waiting) { - Sleep::for($seconds++)->seconds(); +while ($waiting) { + Sleep::for($seconds++)->seconds(); - $waiting = /* ... */; - } + $waiting = /* ... */; +} +``` Typically, testing this code would take _at least_ one second. Luckily, the `Sleep` class allows us to "fake" sleeping so that our test suite stays fast: @@ -2725,22 +3115,24 @@ public function test_it_checks_if_ready_three_times() Of course, the `Sleep` class offers a variety of other assertions you may use when testing: - use Carbon\CarbonInterval as Duration; - use Illuminate\Support\Sleep; +```php +use Carbon\CarbonInterval as Duration; +use Illuminate\Support\Sleep; - // Assert that sleep was called 3 times... - Sleep::assertSleptTimes(3); +// Assert that sleep was called 3 times... +Sleep::assertSleptTimes(3); - // Assert against the duration of sleep... - Sleep::assertSlept(function (Duration $duration): bool { - return /* ... */; - }, times: 1); +// Assert against the duration of sleep... +Sleep::assertSlept(function (Duration $duration): bool { + return /* ... */; +}, times: 1); - // Assert that the Sleep class was never invoked... - Sleep::assertNeverSlept(); +// Assert that the Sleep class was never invoked... +Sleep::assertNeverSlept(); - // Assert that, even if Sleep was called, no execution paused occurred... - Sleep::assertInsomniac(); +// Assert that, even if Sleep was called, no execution paused occurred... +Sleep::assertInsomniac(); +``` Sometimes it may be useful to perform an action whenever a fake sleep occurs in your application code. To achieve this, you may provide a callback to the `whenFakingSleep` method. In the following example, we use Laravel's [time manipulation helpers](/docs/{{version}}/mocking#interacting-with-time) to instantly progress time by the duration of each sleep: diff --git a/homestead.md b/homestead.md index 05d9448955e..aeea06fd25d 100644 --- a/homestead.md +++ b/homestead.md @@ -266,7 +266,9 @@ Homestead publishes hostnames using `mDNS` for automatic host resolution. If you Using automatic hostnames works best for [per project installations](#per-project-installation) of Homestead. If you host multiple sites on a single Homestead instance, you may add the "domains" for your web sites to the `hosts` file on your machine. The `hosts` file will redirect requests for your Homestead sites into your Homestead virtual machine. On macOS and Linux, this file is located at `/etc/hosts`. On Windows, it is located at `C:\Windows\System32\drivers\etc\hosts`. The lines you add to this file will look like the following: - 192.168.56.56 homestead.test +```text +192.168.56.56 homestead.test +``` Make sure the IP address listed is the one set in your `Homestead.yaml` file. Once you have added the domain to your `hosts` file and launched the Vagrant box you will be able to access the site via your web browser: @@ -473,8 +475,10 @@ sites: If Vagrant is not automatically managing your "hosts" file, you may need to add the new site to that file as well. On macOS and Linux, this file is located at `/etc/hosts`. On Windows, it is located at `C:\Windows\System32\drivers\etc\hosts`: - 192.168.56.56 homestead.test - 192.168.56.56 another.test +```text +192.168.56.56 homestead.test +192.168.56.56 another.test +``` Once the site has been added, execute the `vagrant reload --provision` terminal command from your Homestead directory. @@ -616,7 +620,9 @@ A `homestead` database is configured for both MySQL and PostgreSQL out of the bo Homestead can automatically backup your database when your Homestead virtual machine is destroyed. To utilize this feature, you must be using Vagrant 2.1.0 or greater. Or, if you are using an older version of Vagrant, you must install the `vagrant-triggers` plug-in. To enable automatic database backups, add the following line to your `Homestead.yaml` file: - backup: true +```yaml +backup: true +``` Once configured, Homestead will export your databases to `.backup/mysql_backup` and `.backup/postgres_backup` directories when the `vagrant destroy` command is executed. These directories can be found in the folder where you installed Homestead or in the root of your project if you are using the [per project installation](#per-project-installation) method. @@ -747,7 +753,9 @@ xdebug.start_with_request = yes To debug a PHP CLI application, use the `xphp` shell alias inside your Homestead virtual machine: - xphp /path/to/script +```shell +xphp /path/to/script +``` ### Profiling Applications With Blackfire diff --git a/horizon.md b/horizon.md index fc2a090c627..6bec738bc63 100644 --- a/horizon.md +++ b/horizon.md @@ -58,33 +58,37 @@ After publishing Horizon's assets, its primary configuration file will be locate After installation, the primary Horizon configuration option that you should familiarize yourself with is the `environments` configuration option. This configuration option is an array of environments that your application runs on and defines the worker process options for each environment. By default, this entry contains a `production` and `local` environment. However, you are free to add more environments as needed: - 'environments' => [ - 'production' => [ - 'supervisor-1' => [ - 'maxProcesses' => 10, - 'balanceMaxShift' => 1, - 'balanceCooldown' => 3, - ], +```php +'environments' => [ + 'production' => [ + 'supervisor-1' => [ + 'maxProcesses' => 10, + 'balanceMaxShift' => 1, + 'balanceCooldown' => 3, ], + ], - 'local' => [ - 'supervisor-1' => [ - 'maxProcesses' => 3, - ], + 'local' => [ + 'supervisor-1' => [ + 'maxProcesses' => 3, ], ], +], +``` You may also define a wildcard environment (`*`) which will be used when no other matching environment is found: - 'environments' => [ - // ... +```php +'environments' => [ + // ... - '*' => [ - 'supervisor-1' => [ - 'maxProcesses' => 3, - ], + '*' => [ + 'supervisor-1' => [ + 'maxProcesses' => 3, ], ], +], +``` When you start Horizon, it will use the worker process configuration options for the environment that your application is running on. Typically, the environment is determined by the value of the `APP_ENV` [environment variable](/docs/{{version}}/configuration#determining-the-current-environment). For example, the default `local` Horizon environment is configured to start three worker processes and automatically balance the number of worker processes assigned to each queue. The default `production` environment is configured to start a maximum of 10 worker processes and automatically balance the number of worker processes assigned to each queue. @@ -103,14 +107,16 @@ You may add additional supervisors to a given environment if you would like to d While your application is in [maintenance mode](/docs/{{version}}/configuration#maintenance-mode), queued jobs will not be processed by Horizon unless the supervisor's `force` option is defined as `true` within the Horizon configuration file: - 'environments' => [ - 'production' => [ - 'supervisor-1' => [ - // ... - 'force' => true, - ], +```php +'environments' => [ + 'production' => [ + 'supervisor-1' => [ + // ... + 'force' => true, ], ], +], +``` #### Default Values @@ -128,21 +134,23 @@ The `auto` strategy, which is the configuration file's default, adjusts the numb When using the `auto` strategy, you may define the `minProcesses` and `maxProcesses` configuration options to control the minimum number of processes per queue and the maximum number of worker processes in total Horizon should scale up and down to: - 'environments' => [ - 'production' => [ - 'supervisor-1' => [ - 'connection' => 'redis', - 'queue' => ['default'], - 'balance' => 'auto', - 'autoScalingStrategy' => 'time', - 'minProcesses' => 1, - 'maxProcesses' => 10, - 'balanceMaxShift' => 1, - 'balanceCooldown' => 3, - 'tries' => 3, - ], +```php +'environments' => [ + 'production' => [ + 'supervisor-1' => [ + 'connection' => 'redis', + 'queue' => ['default'], + 'balance' => 'auto', + 'autoScalingStrategy' => 'time', + 'minProcesses' => 1, + 'maxProcesses' => 10, + 'balanceMaxShift' => 1, + 'balanceCooldown' => 3, + 'tries' => 3, ], ], +], +``` The `autoScalingStrategy` configuration value determines if Horizon will assign more worker processes to queues based on the total amount of time it will take to clear the queue (`time` strategy) or by the total number of jobs on the queue (`size` strategy). @@ -155,19 +163,21 @@ When the `balance` option is set to `false`, the default Laravel behavior will b The Horizon dashboard may be accessed via the `/horizon` route. By default, you will only be able to access this dashboard in the `local` environment. However, within your `app/Providers/HorizonServiceProvider.php` file, there is an [authorization gate](/docs/{{version}}/authorization#gates) definition. This authorization gate controls access to Horizon in **non-local** environments. You are free to modify this gate as needed to restrict access to your Horizon installation: - /** - * Register the Horizon gate. - * - * This gate determines who can access Horizon in non-local environments. - */ - protected function gate(): void - { - Gate::define('viewHorizon', function (User $user) { - return in_array($user->email, [ - 'taylor@laravel.com', - ]); - }); - } +```php +/** + * Register the Horizon gate. + * + * This gate determines who can access Horizon in non-local environments. + */ +protected function gate(): void +{ + Gate::define('viewHorizon', function (User $user) { + return in_array($user->email, [ + 'taylor@laravel.com', + ]); + }); +} +``` #### Alternative Authentication Strategies @@ -179,20 +189,24 @@ Remember that Laravel automatically injects the authenticated user into the gate Sometimes, you may not be interested in viewing certain jobs dispatched by your application or third-party packages. Instead of these jobs taking up space in your "Completed Jobs" list, you can silence them. To get started, add the job's class name to the `silenced` configuration option in your application's `horizon` configuration file: - 'silenced' => [ - App\Jobs\ProcessPodcast::class, - ], +```php +'silenced' => [ + App\Jobs\ProcessPodcast::class, +], +``` Alternatively, the job you wish to silence can implement the `Laravel\Horizon\Contracts\Silenced` interface. If a job implements this interface, it will automatically be silenced, even if it is not present in the `silenced` configuration array: - use Laravel\Horizon\Contracts\Silenced; +```php +use Laravel\Horizon\Contracts\Silenced; - class ProcessPodcast implements ShouldQueue, Silenced - { - use Queueable; +class ProcessPodcast implements ShouldQueue, Silenced +{ + use Queueable; - // ... - } + // ... +} +``` ## Upgrading Horizon @@ -308,78 +322,86 @@ sudo supervisorctl start horizon Horizon allows you to assign “tags” to jobs, including mailables, broadcast events, notifications, and queued event listeners. In fact, Horizon will intelligently and automatically tag most jobs depending on the Eloquent models that are attached to the job. For example, take a look at the following job: - #### Manually Tagging Jobs If you would like to manually define the tags for one of your queueable objects, you may define a `tags` method on the class: - class RenderVideo implements ShouldQueue +```php +class RenderVideo implements ShouldQueue +{ + /** + * Get the tags that should be assigned to the job. + * + * @return array + */ + public function tags(): array { - /** - * Get the tags that should be assigned to the job. - * - * @return array - */ - public function tags(): array - { - return ['render', 'video:'.$this->video->id]; - } + return ['render', 'video:'.$this->video->id]; } +} +``` #### Manually Tagging Event Listeners When retrieving the tags for a queued event listener, Horizon will automatically pass the event instance to the `tags` method, allowing you to add event data to the tags: - class SendRenderNotifications implements ShouldQueue +```php +class SendRenderNotifications implements ShouldQueue +{ + /** + * Get the tags that should be assigned to the listener. + * + * @return array + */ + public function tags(VideoRendered $event): array { - /** - * Get the tags that should be assigned to the listener. - * - * @return array - */ - public function tags(VideoRendered $event): array - { - return ['video:'.$event->video->id]; - } + return ['video:'.$event->video->id]; } +} +``` ## Notifications @@ -389,37 +411,43 @@ When retrieving the tags for a queued event listener, Horizon will automatically If you would like to be notified when one of your queues has a long wait time, you may use the `Horizon::routeMailNotificationsTo`, `Horizon::routeSlackNotificationsTo`, and `Horizon::routeSmsNotificationsTo` methods. You may call these methods from the `boot` method of your application's `App\Providers\HorizonServiceProvider`: - /** - * Bootstrap any application services. - */ - public function boot(): void - { - parent::boot(); - - Horizon::routeSmsNotificationsTo('15556667777'); - Horizon::routeMailNotificationsTo('example@example.com'); - Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel'); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + parent::boot(); + + Horizon::routeSmsNotificationsTo('15556667777'); + Horizon::routeMailNotificationsTo('example@example.com'); + Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel'); +} +``` #### Configuring Notification Wait Time Thresholds You may configure how many seconds are considered a "long wait" within your application's `config/horizon.php` configuration file. The `waits` configuration option within this file allows you to control the long wait threshold for each connection / queue combination. Any undefined connection / queue combinations will default to a long wait threshold of 60 seconds: - 'waits' => [ - 'redis:critical' => 30, - 'redis:default' => 60, - 'redis:batch' => 120, - ], +```php +'waits' => [ + 'redis:critical' => 30, + 'redis:default' => 60, + 'redis:batch' => 120, +], +``` ## Metrics Horizon includes a metrics dashboard which provides information regarding your job and queue wait times and throughput. In order to populate this dashboard, you should configure Horizon's `snapshot` Artisan command to run every five minutes in your application's `routes/console.php` file: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('horizon:snapshot')->everyFiveMinutes(); +Schedule::command('horizon:snapshot')->everyFiveMinutes(); +``` ## Deleting Failed Jobs diff --git a/http-client.md b/http-client.md index 9970fa34c88..312c6362713 100644 --- a/http-client.md +++ b/http-client.md @@ -28,47 +28,55 @@ Laravel provides an expressive, minimal API around the [Guzzle HTTP client](http To make requests, you may use the `head`, `get`, `post`, `put`, `patch`, and `delete` methods provided by the `Http` facade. First, let's examine how to make a basic `GET` request to another URL: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - $response = Http::get('http://example.com/'); +$response = Http::get('http://example.com/'); +``` The `get` method returns an instance of `Illuminate\Http\Client\Response`, which provides a variety of methods that may be used to inspect the response: - $response->body() : string; - $response->json($key = null, $default = null) : mixed; - $response->object() : object; - $response->collect($key = null) : Illuminate\Support\Collection; - $response->resource() : resource; - $response->status() : int; - $response->successful() : bool; - $response->redirect(): bool; - $response->failed() : bool; - $response->clientError() : bool; - $response->header($header) : string; - $response->headers() : array; +```php +$response->body() : string; +$response->json($key = null, $default = null) : mixed; +$response->object() : object; +$response->collect($key = null) : Illuminate\Support\Collection; +$response->resource() : resource; +$response->status() : int; +$response->successful() : bool; +$response->redirect(): bool; +$response->failed() : bool; +$response->clientError() : bool; +$response->header($header) : string; +$response->headers() : array; +``` The `Illuminate\Http\Client\Response` object also implements the PHP `ArrayAccess` interface, allowing you to access JSON response data directly on the response: - return Http::get('http://example.com/users/1')['name']; +```php +return Http::get('http://example.com/users/1')['name']; +``` In addition to the response methods listed above, the following methods may be used to determine if the response has a given status code: - $response->ok() : bool; // 200 OK - $response->created() : bool; // 201 Created - $response->accepted() : bool; // 202 Accepted - $response->noContent() : bool; // 204 No Content - $response->movedPermanently() : bool; // 301 Moved Permanently - $response->found() : bool; // 302 Found - $response->badRequest() : bool; // 400 Bad Request - $response->unauthorized() : bool; // 401 Unauthorized - $response->paymentRequired() : bool; // 402 Payment Required - $response->forbidden() : bool; // 403 Forbidden - $response->notFound() : bool; // 404 Not Found - $response->requestTimeout() : bool; // 408 Request Timeout - $response->conflict() : bool; // 409 Conflict - $response->unprocessableEntity() : bool; // 422 Unprocessable Entity - $response->tooManyRequests() : bool; // 429 Too Many Requests - $response->serverError() : bool; // 500 Internal Server Error +```php +$response->ok() : bool; // 200 OK +$response->created() : bool; // 201 Created +$response->accepted() : bool; // 202 Accepted +$response->noContent() : bool; // 204 No Content +$response->movedPermanently() : bool; // 301 Moved Permanently +$response->found() : bool; // 302 Found +$response->badRequest() : bool; // 400 Bad Request +$response->unauthorized() : bool; // 401 Unauthorized +$response->paymentRequired() : bool; // 402 Payment Required +$response->forbidden() : bool; // 403 Forbidden +$response->notFound() : bool; // 404 Not Found +$response->requestTimeout() : bool; // 408 Request Timeout +$response->conflict() : bool; // 409 Conflict +$response->unprocessableEntity() : bool; // 422 Unprocessable Entity +$response->tooManyRequests() : bool; // 429 Too Many Requests +$response->serverError() : bool; // 500 Internal Server Error +``` #### URI Templates @@ -89,92 +97,114 @@ Http::withUrlParameters([ If you would like to dump the outgoing request instance before it is sent and terminate the script's execution, you may add the `dd` method to the beginning of your request definition: - return Http::dd()->get('http://example.com/'); +```php +return Http::dd()->get('http://example.com/'); +``` ### Request Data Of course, it is common when making `POST`, `PUT`, and `PATCH` requests to send additional data with your request, so these methods accept an array of data as their second argument. By default, data will be sent using the `application/json` content type: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - $response = Http::post('http://example.com/users', [ - 'name' => 'Steve', - 'role' => 'Network Administrator', - ]); +$response = Http::post('http://example.com/users', [ + 'name' => 'Steve', + 'role' => 'Network Administrator', +]); +``` #### GET Request Query Parameters When making `GET` requests, you may either append a query string to the URL directly or pass an array of key / value pairs as the second argument to the `get` method: - $response = Http::get('http://example.com/users', [ - 'name' => 'Taylor', - 'page' => 1, - ]); +```php +$response = Http::get('http://example.com/users', [ + 'name' => 'Taylor', + 'page' => 1, +]); +``` Alternatively, the `withQueryParameters` method may be used: - Http::retry(3, 100)->withQueryParameters([ - 'name' => 'Taylor', - 'page' => 1, - ])->get('http://example.com/users') +```php +Http::retry(3, 100)->withQueryParameters([ + 'name' => 'Taylor', + 'page' => 1, +])->get('http://example.com/users') +``` #### Sending Form URL Encoded Requests If you would like to send data using the `application/x-www-form-urlencoded` content type, you should call the `asForm` method before making your request: - $response = Http::asForm()->post('http://example.com/users', [ - 'name' => 'Sara', - 'role' => 'Privacy Consultant', - ]); +```php +$response = Http::asForm()->post('http://example.com/users', [ + 'name' => 'Sara', + 'role' => 'Privacy Consultant', +]); +``` #### Sending a Raw Request Body You may use the `withBody` method if you would like to provide a raw request body when making a request. The content type may be provided via the method's second argument: - $response = Http::withBody( - base64_encode($photo), 'image/jpeg' - )->post('http://example.com/photo'); +```php +$response = Http::withBody( + base64_encode($photo), 'image/jpeg' +)->post('http://example.com/photo'); +``` #### Multi-Part Requests If you would like to send files as multi-part requests, you should call the `attach` method before making your request. This method accepts the name of the file and its contents. If needed, you may provide a third argument which will be considered the file's filename, while a fourth argument may be used to provide headers associated with the file: - $response = Http::attach( - 'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg'] - )->post('http://example.com/attachments'); +```php +$response = Http::attach( + 'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg'] +)->post('http://example.com/attachments'); +``` Instead of passing the raw contents of a file, you may pass a stream resource: - $photo = fopen('photo.jpg', 'r'); +```php +$photo = fopen('photo.jpg', 'r'); - $response = Http::attach( - 'attachment', $photo, 'photo.jpg' - )->post('http://example.com/attachments'); +$response = Http::attach( + 'attachment', $photo, 'photo.jpg' +)->post('http://example.com/attachments'); +``` ### Headers Headers may be added to requests using the `withHeaders` method. This `withHeaders` method accepts an array of key / value pairs: - $response = Http::withHeaders([ - 'X-First' => 'foo', - 'X-Second' => 'bar' - ])->post('http://example.com/users', [ - 'name' => 'Taylor', - ]); +```php +$response = Http::withHeaders([ + 'X-First' => 'foo', + 'X-Second' => 'bar' +])->post('http://example.com/users', [ + 'name' => 'Taylor', +]); +``` You may use the `accept` method to specify the content type that your application is expecting in response to your request: - $response = Http::accept('application/json')->get('http://example.com/users'); +```php +$response = Http::accept('application/json')->get('http://example.com/users'); +``` For convenience, you may use the `acceptJson` method to quickly specify that your application expects the `application/json` content type in response to your request: - $response = Http::acceptJson()->get('http://example.com/users'); +```php +$response = Http::acceptJson()->get('http://example.com/users'); +``` The `withHeaders` method merges new headers into the request's existing headers. If needed, you may replace all of the headers entirely using the `replaceHeaders` method: @@ -193,79 +223,99 @@ $response = Http::withHeaders([ You may specify basic and digest authentication credentials using the `withBasicAuth` and `withDigestAuth` methods, respectively: - // Basic authentication... - $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); +```php +// Basic authentication... +$response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); - // Digest authentication... - $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */); +// Digest authentication... +$response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */); +``` #### Bearer Tokens If you would like to quickly add a bearer token to the request's `Authorization` header, you may use the `withToken` method: - $response = Http::withToken('token')->post(/* ... */); +```php +$response = Http::withToken('token')->post(/* ... */); +``` ### Timeout The `timeout` method may be used to specify the maximum number of seconds to wait for a response. By default, the HTTP client will timeout after 30 seconds: - $response = Http::timeout(3)->get(/* ... */); +```php +$response = Http::timeout(3)->get(/* ... */); +``` If the given timeout is exceeded, an instance of `Illuminate\Http\Client\ConnectionException` will be thrown. You may specify the maximum number of seconds to wait while trying to connect to a server using the `connectTimeout` method: - $response = Http::connectTimeout(3)->get(/* ... */); +```php +$response = Http::connectTimeout(3)->get(/* ... */); +``` ### Retries If you would like the HTTP client to automatically retry the request if a client or server error occurs, you may use the `retry` method. The `retry` method accepts the maximum number of times the request should be attempted and the number of milliseconds that Laravel should wait in between attempts: - $response = Http::retry(3, 100)->post(/* ... */); +```php +$response = Http::retry(3, 100)->post(/* ... */); +``` If you would like to manually calculate the number of milliseconds to sleep between attempts, you may pass a closure as the second argument to the `retry` method: - use Exception; +```php +use Exception; - $response = Http::retry(3, function (int $attempt, Exception $exception) { - return $attempt * 100; - })->post(/* ... */); +$response = Http::retry(3, function (int $attempt, Exception $exception) { + return $attempt * 100; +})->post(/* ... */); +``` For convenience, you may also provide an array as the first argument to the `retry` method. This array will be used to determine how many milliseconds to sleep between subsequent attempts: - $response = Http::retry([100, 200])->post(/* ... */); +```php +$response = Http::retry([100, 200])->post(/* ... */); +``` If needed, you may pass a third argument to the `retry` method. The third argument should be a callable that determines if the retries should actually be attempted. For example, you may wish to only retry the request if the initial request encounters an `ConnectionException`: - use Exception; - use Illuminate\Http\Client\PendingRequest; +```php +use Exception; +use Illuminate\Http\Client\PendingRequest; - $response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) { - return $exception instanceof ConnectionException; - })->post(/* ... */); +$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) { + return $exception instanceof ConnectionException; +})->post(/* ... */); +``` If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying the request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: - use Exception; - use Illuminate\Http\Client\PendingRequest; - use Illuminate\Http\Client\RequestException; +```php +use Exception; +use Illuminate\Http\Client\PendingRequest; +use Illuminate\Http\Client\RequestException; - $response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) { - if (! $exception instanceof RequestException || $exception->response->status() !== 401) { - return false; - } +$response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) { + if (! $exception instanceof RequestException || $exception->response->status() !== 401) { + return false; + } - $request->withToken($this->getNewToken()); + $request->withToken($this->getNewToken()); - return true; - })->post(/* ... */); + return true; +})->post(/* ... */); +``` If all of the requests fail, an instance of `Illuminate\Http\Client\RequestException` will be thrown. If you would like to disable this behavior, you may provide a `throw` argument with a value of `false`. When disabled, the last response received by the client will be returned after all retries have been attempted: - $response = Http::retry(3, 100, throw: false)->post(/* ... */); +```php +$response = Http::retry(3, 100, throw: false)->post(/* ... */); +``` > [!WARNING] > If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. @@ -275,106 +325,120 @@ If all of the requests fail, an instance of `Illuminate\Http\Client\RequestExcep Unlike Guzzle's default behavior, Laravel's HTTP client wrapper does not throw exceptions on client or server errors (`400` and `500` level responses from servers). You may determine if one of these errors was returned using the `successful`, `clientError`, or `serverError` methods: - // Determine if the status code is >= 200 and < 300... - $response->successful(); +```php +// Determine if the status code is >= 200 and < 300... +$response->successful(); - // Determine if the status code is >= 400... - $response->failed(); +// Determine if the status code is >= 400... +$response->failed(); - // Determine if the response has a 400 level status code... - $response->clientError(); +// Determine if the response has a 400 level status code... +$response->clientError(); - // Determine if the response has a 500 level status code... - $response->serverError(); +// Determine if the response has a 500 level status code... +$response->serverError(); - // Immediately execute the given callback if there was a client or server error... - $response->onError(callable $callback); +// Immediately execute the given callback if there was a client or server error... +$response->onError(callable $callback); +``` #### Throwing Exceptions If you have a response instance and would like to throw an instance of `Illuminate\Http\Client\RequestException` if the response status code indicates a client or server error, you may use the `throw` or `throwIf` methods: - use Illuminate\Http\Client\Response; +```php +use Illuminate\Http\Client\Response; - $response = Http::post(/* ... */); +$response = Http::post(/* ... */); - // Throw an exception if a client or server error occurred... - $response->throw(); +// Throw an exception if a client or server error occurred... +$response->throw(); - // Throw an exception if an error occurred and the given condition is true... - $response->throwIf($condition); +// Throw an exception if an error occurred and the given condition is true... +$response->throwIf($condition); - // Throw an exception if an error occurred and the given closure resolves to true... - $response->throwIf(fn (Response $response) => true); +// Throw an exception if an error occurred and the given closure resolves to true... +$response->throwIf(fn (Response $response) => true); - // Throw an exception if an error occurred and the given condition is false... - $response->throwUnless($condition); +// Throw an exception if an error occurred and the given condition is false... +$response->throwUnless($condition); - // Throw an exception if an error occurred and the given closure resolves to false... - $response->throwUnless(fn (Response $response) => false); +// Throw an exception if an error occurred and the given closure resolves to false... +$response->throwUnless(fn (Response $response) => false); - // Throw an exception if the response has a specific status code... - $response->throwIfStatus(403); +// Throw an exception if the response has a specific status code... +$response->throwIfStatus(403); - // Throw an exception unless the response has a specific status code... - $response->throwUnlessStatus(200); +// Throw an exception unless the response has a specific status code... +$response->throwUnlessStatus(200); - return $response['user']['id']; +return $response['user']['id']; +``` The `Illuminate\Http\Client\RequestException` instance has a public `$response` property which will allow you to inspect the returned response. The `throw` method returns the response instance if no error occurred, allowing you to chain other operations onto the `throw` method: - return Http::post(/* ... */)->throw()->json(); +```php +return Http::post(/* ... */)->throw()->json(); +``` If you would like to perform some additional logic before the exception is thrown, you may pass a closure to the `throw` method. The exception will be thrown automatically after the closure is invoked, so you do not need to re-throw the exception from within the closure: - use Illuminate\Http\Client\Response; - use Illuminate\Http\Client\RequestException; +```php +use Illuminate\Http\Client\Response; +use Illuminate\Http\Client\RequestException; - return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) { - // ... - })->json(); +return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) { + // ... +})->json(); +``` By default, `RequestException` messages are truncated to 120 characters when logged or reported. To customize or disable this behavior, you may utilize the `truncateRequestExceptionsAt` and `dontTruncateRequestExceptions` methods when configuring your application's exception handling behavior in your `bootstrap/app.php` file: - ->withExceptions(function (Exceptions $exceptions) { - // Truncate request exception messages to 240 characters... - $exceptions->truncateRequestExceptionsAt(240); +```php +->withExceptions(function (Exceptions $exceptions) { + // Truncate request exception messages to 240 characters... + $exceptions->truncateRequestExceptionsAt(240); - // Disable request exception message truncation... - $exceptions->dontTruncateRequestExceptions(); - }) + // Disable request exception message truncation... + $exceptions->dontTruncateRequestExceptions(); +}) +``` ### Guzzle Middleware Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guzzle Middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html) to manipulate the outgoing request or inspect the incoming response. To manipulate the outgoing request, register a Guzzle middleware via the `withRequestMiddleware` method: - use Illuminate\Support\Facades\Http; - use Psr\Http\Message\RequestInterface; +```php +use Illuminate\Support\Facades\Http; +use Psr\Http\Message\RequestInterface; - $response = Http::withRequestMiddleware( - function (RequestInterface $request) { - return $request->withHeader('X-Example', 'Value'); - } - )->get('http://example.com/'); +$response = Http::withRequestMiddleware( + function (RequestInterface $request) { + return $request->withHeader('X-Example', 'Value'); + } +)->get('http://example.com/'); +``` Likewise, you can inspect the incoming HTTP response by registering a middleware via the `withResponseMiddleware` method: - use Illuminate\Support\Facades\Http; - use Psr\Http\Message\ResponseInterface; +```php +use Illuminate\Support\Facades\Http; +use Psr\Http\Message\ResponseInterface; - $response = Http::withResponseMiddleware( - function (ResponseInterface $response) { - $header = $response->getHeader('X-Example'); +$response = Http::withResponseMiddleware( + function (ResponseInterface $response) { + $header = $response->getHeader('X-Example'); - // ... + // ... - return $response; - } - )->get('http://example.com/'); + return $response; + } +)->get('http://example.com/'); +``` #### Global Middleware @@ -398,9 +462,11 @@ Http::globalResponseMiddleware(fn ($response) => $response->withHeader( You may specify additional [Guzzle request options](http://docs.guzzlephp.org/en/stable/request-options.html) for an outgoing request using the `withOptions` method. The `withOptions` method accepts an array of key / value pairs: - $response = Http::withOptions([ - 'debug' => true, - ])->get('http://example.com/users'); +```php +$response = Http::withOptions([ + 'debug' => true, +])->get('http://example.com/users'); +``` #### Global Options @@ -428,31 +494,35 @@ Sometimes, you may wish to make multiple HTTP requests concurrently. In other wo Thankfully, you may accomplish this using the `pool` method. The `pool` method accepts a closure which receives an `Illuminate\Http\Client\Pool` instance, allowing you to easily add requests to the request pool for dispatching: - use Illuminate\Http\Client\Pool; - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Http\Client\Pool; +use Illuminate\Support\Facades\Http; - $responses = Http::pool(fn (Pool $pool) => [ - $pool->get('http://localhost/first'), - $pool->get('http://localhost/second'), - $pool->get('http://localhost/third'), - ]); +$responses = Http::pool(fn (Pool $pool) => [ + $pool->get('http://localhost/first'), + $pool->get('http://localhost/second'), + $pool->get('http://localhost/third'), +]); - return $responses[0]->ok() && - $responses[1]->ok() && - $responses[2]->ok(); +return $responses[0]->ok() && + $responses[1]->ok() && + $responses[2]->ok(); +``` As you can see, each response instance can be accessed based on the order it was added to the pool. If you wish, you can name the requests using the `as` method, which allows you to access the corresponding responses by name: - use Illuminate\Http\Client\Pool; - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Http\Client\Pool; +use Illuminate\Support\Facades\Http; - $responses = Http::pool(fn (Pool $pool) => [ - $pool->as('first')->get('http://localhost/first'), - $pool->as('second')->get('http://localhost/second'), - $pool->as('third')->get('http://localhost/third'), - ]); +$responses = Http::pool(fn (Pool $pool) => [ + $pool->as('first')->get('http://localhost/first'), + $pool->as('second')->get('http://localhost/second'), + $pool->as('third')->get('http://localhost/third'), +]); - return $responses['first']->ok(); +return $responses['first']->ok(); +``` #### Customizing Concurrent Requests @@ -511,110 +581,130 @@ Many Laravel services provide functionality to help you easily and expressively For example, to instruct the HTTP client to return empty, `200` status code responses for every request, you may call the `fake` method with no arguments: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - Http::fake(); +Http::fake(); - $response = Http::post(/* ... */); +$response = Http::post(/* ... */); +``` #### Faking Specific URLs Alternatively, you may pass an array to the `fake` method. The array's keys should represent URL patterns that you wish to fake and their associated responses. The `*` character may be used as a wildcard character. Any requests made to URLs that have not been faked will actually be executed. You may use the `Http` facade's `response` method to construct stub / fake responses for these endpoints: - Http::fake([ - // Stub a JSON response for GitHub endpoints... - 'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers), +```php +Http::fake([ + // Stub a JSON response for GitHub endpoints... + 'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers), - // Stub a string response for Google endpoints... - 'google.com/*' => Http::response('Hello World', 200, $headers), - ]); + // Stub a string response for Google endpoints... + 'google.com/*' => Http::response('Hello World', 200, $headers), +]); +``` If you would like to specify a fallback URL pattern that will stub all unmatched URLs, you may use a single `*` character: - Http::fake([ - // Stub a JSON response for GitHub endpoints... - 'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']), +```php +Http::fake([ + // Stub a JSON response for GitHub endpoints... + 'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']), - // Stub a string response for all other endpoints... - '*' => Http::response('Hello World', 200, ['Headers']), - ]); + // Stub a string response for all other endpoints... + '*' => Http::response('Hello World', 200, ['Headers']), +]); +``` For convenience, simple string, JSON, and empty responses may be generated by providing a string, array, or integer as the response: - Http::fake([ - 'google.com/*' => 'Hello World', - 'github.com/*' => ['foo' => 'bar'], - 'chatgpt.com/*' => 200, - ]); +```php +Http::fake([ + 'google.com/*' => 'Hello World', + 'github.com/*' => ['foo' => 'bar'], + 'chatgpt.com/*' => 200, +]); +``` #### Faking Connection Exceptions Sometimes you may need to test your application's behavior if the HTTP client encounters an `Illuminate\Http\Client\ConnectionException` when attempting to make a request. You can instruct the HTTP client to throw a connection exception using the `failedConnection` method: - Http::fake([ - 'github.com/*' => Http::failedConnection(), - ]); +```php +Http::fake([ + 'github.com/*' => Http::failedConnection(), +]); +``` #### Faking Response Sequences Sometimes you may need to specify that a single URL should return a series of fake responses in a specific order. You may accomplish this using the `Http::sequence` method to build the responses: - Http::fake([ - // Stub a series of responses for GitHub endpoints... - 'github.com/*' => Http::sequence() - ->push('Hello World', 200) - ->push(['foo' => 'bar'], 200) - ->pushStatus(404), - ]); +```php +Http::fake([ + // Stub a series of responses for GitHub endpoints... + 'github.com/*' => Http::sequence() + ->push('Hello World', 200) + ->push(['foo' => 'bar'], 200) + ->pushStatus(404), +]); +``` When all the responses in a response sequence have been consumed, any further requests will cause the response sequence to throw an exception. If you would like to specify a default response that should be returned when a sequence is empty, you may use the `whenEmpty` method: - Http::fake([ - // Stub a series of responses for GitHub endpoints... - 'github.com/*' => Http::sequence() - ->push('Hello World', 200) - ->push(['foo' => 'bar'], 200) - ->whenEmpty(Http::response()), - ]); +```php +Http::fake([ + // Stub a series of responses for GitHub endpoints... + 'github.com/*' => Http::sequence() + ->push('Hello World', 200) + ->push(['foo' => 'bar'], 200) + ->whenEmpty(Http::response()), +]); +``` If you would like to fake a sequence of responses but do not need to specify a specific URL pattern that should be faked, you may use the `Http::fakeSequence` method: - Http::fakeSequence() - ->push('Hello World', 200) - ->whenEmpty(Http::response()); +```php +Http::fakeSequence() + ->push('Hello World', 200) + ->whenEmpty(Http::response()); +``` #### Fake Callback If you require more complicated logic to determine what responses to return for certain endpoints, you may pass a closure to the `fake` method. This closure will receive an instance of `Illuminate\Http\Client\Request` and should return a response instance. Within your closure, you may perform whatever logic is necessary to determine what type of response to return: - use Illuminate\Http\Client\Request; +```php +use Illuminate\Http\Client\Request; - Http::fake(function (Request $request) { - return Http::response('Hello World', 200); - }); +Http::fake(function (Request $request) { + return Http::response('Hello World', 200); +}); +``` ### Preventing Stray Requests If you would like to ensure that all requests sent via the HTTP client have been faked throughout your individual test or complete test suite, you can call the `preventStrayRequests` method. After calling this method, any requests that do not have a corresponding fake response will throw an exception rather than making the actual HTTP request: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - Http::preventStrayRequests(); +Http::preventStrayRequests(); - Http::fake([ - 'github.com/*' => Http::response('ok'), - ]); +Http::fake([ + 'github.com/*' => Http::response('ok'), +]); - // An "ok" response is returned... - Http::get('https://github.com/laravel/framework'); +// An "ok" response is returned... +Http::get('https://github.com/laravel/framework'); - // An exception is thrown... - Http::get('https://laravel.com/'); +// An exception is thrown... +Http::get('https://laravel.com/'); +``` ### Inspecting Requests @@ -623,52 +713,60 @@ When faking responses, you may occasionally wish to inspect the requests the cli The `assertSent` method accepts a closure which will receive an `Illuminate\Http\Client\Request` instance and should return a boolean value indicating if the request matches your expectations. In order for the test to pass, at least one request must have been issued matching the given expectations: - use Illuminate\Http\Client\Request; - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Http\Client\Request; +use Illuminate\Support\Facades\Http; - Http::fake(); +Http::fake(); - Http::withHeaders([ - 'X-First' => 'foo', - ])->post('http://example.com/users', [ - 'name' => 'Taylor', - 'role' => 'Developer', - ]); +Http::withHeaders([ + 'X-First' => 'foo', +])->post('http://example.com/users', [ + 'name' => 'Taylor', + 'role' => 'Developer', +]); - Http::assertSent(function (Request $request) { - return $request->hasHeader('X-First', 'foo') && - $request->url() == 'http://example.com/users' && - $request['name'] == 'Taylor' && - $request['role'] == 'Developer'; - }); +Http::assertSent(function (Request $request) { + return $request->hasHeader('X-First', 'foo') && + $request->url() == 'http://example.com/users' && + $request['name'] == 'Taylor' && + $request['role'] == 'Developer'; +}); +``` If needed, you may assert that a specific request was not sent using the `assertNotSent` method: - use Illuminate\Http\Client\Request; - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Http\Client\Request; +use Illuminate\Support\Facades\Http; - Http::fake(); +Http::fake(); - Http::post('http://example.com/users', [ - 'name' => 'Taylor', - 'role' => 'Developer', - ]); +Http::post('http://example.com/users', [ + 'name' => 'Taylor', + 'role' => 'Developer', +]); - Http::assertNotSent(function (Request $request) { - return $request->url() === 'http://example.com/posts'; - }); +Http::assertNotSent(function (Request $request) { + return $request->url() === 'http://example.com/posts'; +}); +``` You may use the `assertSentCount` method to assert how many requests were "sent" during the test: - Http::fake(); +```php +Http::fake(); - Http::assertSentCount(5); +Http::assertSentCount(5); +``` Or, you may use the `assertNothingSent` method to assert that no requests were sent during the test: - Http::fake(); +```php +Http::fake(); - Http::assertNothingSent(); +Http::assertNothingSent(); +``` #### Recording Requests / Responses @@ -716,15 +814,17 @@ Laravel fires three events during the process of sending HTTP requests. The `Req The `RequestSending` and `ConnectionFailed` events both contain a public `$request` property that you may use to inspect the `Illuminate\Http\Client\Request` instance. Likewise, the `ResponseReceived` event contains a `$request` property as well as a `$response` property which may be used to inspect the `Illuminate\Http\Client\Response` instance. You may create [event listeners](/docs/{{version}}/events) for these events within your application: - use Illuminate\Http\Client\Events\RequestSending; +```php +use Illuminate\Http\Client\Events\RequestSending; - class LogRequest +class LogRequest +{ + /** + * Handle the given event. + */ + public function handle(RequestSending $event): void { - /** - * Handle the given event. - */ - public function handle(RequestSending $event): void - { - // $event->request ... - } + // $event->request ... } +} +``` diff --git a/http-tests.md b/http-tests.md index e64e4e66bf1..29a978a6802 100644 --- a/http-tests.md +++ b/http-tests.md @@ -257,7 +257,9 @@ class ExampleTest extends TestCase You may also specify which guard should be used to authenticate the given user by passing the guard name as the second argument to the `actingAs` method. The guard that is provided to the `actingAs` method will also become the default guard for the duration of the test: - $this->actingAs($user, 'web') +```php +$this->actingAs($user, 'web') +``` ### Debugging Responses @@ -410,11 +412,15 @@ Exceptions::assertNothingReported(); You may totally disable exception handling for a given request by invoking the `withoutExceptionHandling` method before making your request: - $response = $this->withoutExceptionHandling()->get('/'); +```php +$response = $this->withoutExceptionHandling()->get('/'); +``` In addition, if you would like to ensure that your application is not utilizing features that have been deprecated by the PHP language or the libraries your application is using, you may invoke the `withoutDeprecationHandling` method before making your request. When deprecation handling is disabled, deprecation warnings will be converted to exceptions, thus causing your test to fail: - $response = $this->withoutDeprecationHandling()->get('/'); +```php +$response = $this->withoutDeprecationHandling()->get('/'); +``` The `assertThrows` method may be used to assert that code within a given closure throws an exception of the specified type: @@ -578,7 +584,9 @@ class ExampleTest extends TestCase The `assertJsonPath` method also accepts a closure, which may be used to dynamically determine if the assertion should pass: - $response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3); +```php +$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3); +``` ### Fluent JSON Testing @@ -638,108 +646,128 @@ However, you should be aware that not including the `etc` method in your asserti To assert that an attribute is present or absent, you may use the `has` and `missing` methods: - $response->assertJson(fn (AssertableJson $json) => - $json->has('data') - ->missing('message') - ); +```php +$response->assertJson(fn (AssertableJson $json) => + $json->has('data') + ->missing('message') +); +``` In addition, the `hasAll` and `missingAll` methods allow asserting the presence or absence of multiple attributes simultaneously: - $response->assertJson(fn (AssertableJson $json) => - $json->hasAll(['status', 'data']) - ->missingAll(['message', 'code']) - ); +```php +$response->assertJson(fn (AssertableJson $json) => + $json->hasAll(['status', 'data']) + ->missingAll(['message', 'code']) +); +``` You may use the `hasAny` method to determine if at least one of a given list of attributes is present: - $response->assertJson(fn (AssertableJson $json) => - $json->has('status') - ->hasAny('data', 'message', 'code') - ); +```php +$response->assertJson(fn (AssertableJson $json) => + $json->has('status') + ->hasAny('data', 'message', 'code') +); +``` #### Asserting Against JSON Collections Often, your route will return a JSON response that contains multiple items, such as multiple users: - Route::get('/users', function () { - return User::all(); - }); +```php +Route::get('/users', function () { + return User::all(); +}); +``` In these situations, we may use the fluent JSON object's `has` method to make assertions against the users included in the response. For example, let's assert that the JSON response contains three users. Next, we'll make some assertions about the first user in the collection using the `first` method. The `first` method accepts a closure which receives another assertable JSON string that we can use to make assertions about the first object in the JSON collection: - $response - ->assertJson(fn (AssertableJson $json) => - $json->has(3) - ->first(fn (AssertableJson $json) => - $json->where('id', 1) - ->where('name', 'Victoria Faith') - ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) - ->missing('password') - ->etc() - ) - ); +```php +$response + ->assertJson(fn (AssertableJson $json) => + $json->has(3) + ->first(fn (AssertableJson $json) => + $json->where('id', 1) + ->where('name', 'Victoria Faith') + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) + ->missing('password') + ->etc() + ) + ); +``` #### Scoping JSON Collection Assertions Sometimes, your application's routes will return JSON collections that are assigned named keys: - Route::get('/users', function () { - return [ - 'meta' => [...], - 'users' => User::all(), - ]; - }) +```php +Route::get('/users', function () { + return [ + 'meta' => [...], + 'users' => User::all(), + ]; +}) +``` When testing these routes, you may use the `has` method to assert against the number of items in the collection. In addition, you may use the `has` method to scope a chain of assertions: - $response - ->assertJson(fn (AssertableJson $json) => - $json->has('meta') - ->has('users', 3) - ->has('users.0', fn (AssertableJson $json) => - $json->where('id', 1) - ->where('name', 'Victoria Faith') - ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) - ->missing('password') - ->etc() - ) - ); +```php +$response + ->assertJson(fn (AssertableJson $json) => + $json->has('meta') + ->has('users', 3) + ->has('users.0', fn (AssertableJson $json) => + $json->where('id', 1) + ->where('name', 'Victoria Faith') + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) + ->missing('password') + ->etc() + ) + ); +``` However, instead of making two separate calls to the `has` method to assert against the `users` collection, you may make a single call which provides a closure as its third parameter. When doing so, the closure will automatically be invoked and scoped to the first item in the collection: - $response - ->assertJson(fn (AssertableJson $json) => - $json->has('meta') - ->has('users', 3, fn (AssertableJson $json) => - $json->where('id', 1) - ->where('name', 'Victoria Faith') - ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) - ->missing('password') - ->etc() - ) - ); +```php +$response + ->assertJson(fn (AssertableJson $json) => + $json->has('meta') + ->has('users', 3, fn (AssertableJson $json) => + $json->where('id', 1) + ->where('name', 'Victoria Faith') + ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) + ->missing('password') + ->etc() + ) + ); +``` #### Asserting JSON Types You may only want to assert that the properties in the JSON response are of a certain type. The `Illuminate\Testing\Fluent\AssertableJson` class provides the `whereType` and `whereAllType` methods for doing just that: - $response->assertJson(fn (AssertableJson $json) => - $json->whereType('id', 'integer') - ->whereAllType([ - 'users.0.name' => 'string', - 'meta' => 'array' - ]) - ); +```php +$response->assertJson(fn (AssertableJson $json) => + $json->whereType('id', 'integer') + ->whereAllType([ + 'users.0.name' => 'string', + 'meta' => 'array' + ]) +); +``` You may specify multiple types using the `|` character, or passing an array of types as the second parameter to the `whereType` method. The assertion will be successful if the response value is any of the listed types: - $response->assertJson(fn (AssertableJson $json) => - $json->whereType('name', 'string|null') - ->whereType('id', ['string', 'integer']) - ); +```php +$response->assertJson(fn (AssertableJson $json) => + $json->whereType('name', 'string|null') + ->whereType('id', ['string', 'integer']) +); +``` The `whereType` and `whereAllType` methods recognize the following types: `string`, `integer`, `double`, `boolean`, `array`, and `null`. @@ -795,28 +823,36 @@ class ExampleTest extends TestCase If you would like to assert that a given file does not exist, you may use the `assertMissing` method provided by the `Storage` facade: - Storage::fake('avatars'); +```php +Storage::fake('avatars'); - // ... +// ... - Storage::disk('avatars')->assertMissing('missing.jpg'); +Storage::disk('avatars')->assertMissing('missing.jpg'); +``` #### Fake File Customization When creating files using the `fake` method provided by the `UploadedFile` class, you may specify the width, height, and size of the image (in kilobytes) in order to better test your application's validation rules: - UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100); +```php +UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100); +``` In addition to creating images, you may create files of any other type using the `create` method: - UploadedFile::fake()->create('document.pdf', $sizeInKilobytes); +```php +UploadedFile::fake()->create('document.pdf', $sizeInKilobytes); +``` If needed, you may pass a `$mimeType` argument to the method to explicitly define the MIME type that should be returned by the file: - UploadedFile::fake()->create( - 'document.pdf', $sizeInKilobytes, 'application/pdf' - ); +```php +UploadedFile::fake()->create( + 'document.pdf', $sizeInKilobytes, 'application/pdf' +); +``` ## Testing Views @@ -855,36 +891,44 @@ The `TestView` class provides the following assertion methods: `assertSee`, `ass If needed, you may get the raw, rendered view contents by casting the `TestView` instance to a string: - $contents = (string) $this->view('welcome'); +```php +$contents = (string) $this->view('welcome'); +``` #### Sharing Errors Some views may depend on errors shared in the [global error bag provided by Laravel](/docs/{{version}}/validation#quick-displaying-the-validation-errors). To hydrate the error bag with error messages, you may use the `withViewErrors` method: - $view = $this->withViewErrors([ - 'name' => ['Please provide a valid name.'] - ])->view('form'); +```php +$view = $this->withViewErrors([ + 'name' => ['Please provide a valid name.'] +])->view('form'); - $view->assertSee('Please provide a valid name.'); +$view->assertSee('Please provide a valid name.'); +``` ### Rendering Blade and Components If necessary, you may use the `blade` method to evaluate and render a raw [Blade](/docs/{{version}}/blade) string. Like the `view` method, the `blade` method returns an instance of `Illuminate\Testing\TestView`: - $view = $this->blade( - '', - ['name' => 'Taylor'] - ); +```php +$view = $this->blade( + '', + ['name' => 'Taylor'] +); - $view->assertSee('Taylor'); +$view->assertSee('Taylor'); +``` You may use the `component` method to evaluate and render a [Blade component](/docs/{{version}}/blade#components). The `component` method returns an instance of `Illuminate\Testing\TestComponent`: - $view = $this->component(Profile::class, ['name' => 'Taylor']); +```php +$view = $this->component(Profile::class, ['name' => 'Taylor']); - $view->assertSee('Taylor'); +$view->assertSee('Taylor'); +``` ## Available Assertions @@ -990,95 +1034,123 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a Assert that the response has a bad request (400) HTTP status code: - $response->assertBadRequest(); +```php +$response->assertBadRequest(); +``` #### assertAccepted Assert that the response has an accepted (202) HTTP status code: - $response->assertAccepted(); +```php +$response->assertAccepted(); +``` #### assertConflict Assert that the response has a conflict (409) HTTP status code: - $response->assertConflict(); +```php +$response->assertConflict(); +``` #### assertCookie Assert that the response contains the given cookie: - $response->assertCookie($cookieName, $value = null); +```php +$response->assertCookie($cookieName, $value = null); +``` #### assertCookieExpired Assert that the response contains the given cookie and it is expired: - $response->assertCookieExpired($cookieName); +```php +$response->assertCookieExpired($cookieName); +``` #### assertCookieNotExpired Assert that the response contains the given cookie and it is not expired: - $response->assertCookieNotExpired($cookieName); +```php +$response->assertCookieNotExpired($cookieName); +``` #### assertCookieMissing Assert that the response does not contain the given cookie: - $response->assertCookieMissing($cookieName); +```php +$response->assertCookieMissing($cookieName); +``` #### assertCreated Assert that the response has a 201 HTTP status code: - $response->assertCreated(); +```php +$response->assertCreated(); +``` #### assertDontSee Assert that the given string is not contained within the response returned by the application. This assertion will automatically escape the given string unless you pass a second argument of `false`: - $response->assertDontSee($value, $escaped = true); +```php +$response->assertDontSee($value, $escaped = true); +``` #### assertDontSeeText Assert that the given string is not contained within the response text. This assertion will automatically escape the given string unless you pass a second argument of `false`. This method will pass the response content to the `strip_tags` PHP function before making the assertion: - $response->assertDontSeeText($value, $escaped = true); +```php +$response->assertDontSeeText($value, $escaped = true); +``` #### assertDownload Assert that the response is a "download". Typically, this means the invoked route that returned the response returned a `Response::download` response, `BinaryFileResponse`, or `Storage::download` response: - $response->assertDownload(); +```php +$response->assertDownload(); +``` If you wish, you may assert that the downloadable file was assigned a given file name: - $response->assertDownload('image.jpg'); +```php +$response->assertDownload('image.jpg'); +``` #### assertExactJson Assert that the response contains an exact match of the given JSON data: - $response->assertExactJson(array $data); +```php +$response->assertExactJson(array $data); +``` #### assertExactJsonStructure Assert that the response contains an exact match of the given JSON structure: - $response->assertExactJsonStructure(array $data); +```php +$response->assertExactJsonStructure(array $data); +``` This method is a more strict variant of [assertJsonStructure](#assert-json-structure). In contrast with `assertJsonStructure`, this method will fail if the response contains any keys that aren't explicitly included in the expected JSON structure. @@ -1087,49 +1159,63 @@ This method is a more strict variant of [assertJsonStructure](#assert-json-struc Assert that the response has a forbidden (403) HTTP status code: - $response->assertForbidden(); +```php +$response->assertForbidden(); +``` #### assertFound Assert that the response has a found (302) HTTP status code: - $response->assertFound(); +```php +$response->assertFound(); +``` #### assertGone Assert that the response has a gone (410) HTTP status code: - $response->assertGone(); +```php +$response->assertGone(); +``` #### assertHeader Assert that the given header and value is present on the response: - $response->assertHeader($headerName, $value = null); +```php +$response->assertHeader($headerName, $value = null); +``` #### assertHeaderMissing Assert that the given header is not present on the response: - $response->assertHeaderMissing($headerName); +```php +$response->assertHeaderMissing($headerName); +``` #### assertInternalServerError Assert that the response has an "Internal Server Error" (500) HTTP status code: - $response->assertInternalServerError(); +```php +$response->assertInternalServerError(); +``` #### assertJson Assert that the response contains the given JSON data: - $response->assertJson(array $data, $strict = false); +```php +$response->assertJson(array $data, $strict = false); +``` The `assertJson` method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. @@ -1138,59 +1224,73 @@ The `assertJson` method converts the response to an array to verify that the giv Assert that the response JSON has an array with the expected number of items at the given key: - $response->assertJsonCount($count, $key = null); +```php +$response->assertJsonCount($count, $key = null); +``` #### assertJsonFragment Assert that the response contains the given JSON data anywhere in the response: - Route::get('/users', function () { - return [ - 'users' => [ - [ - 'name' => 'Taylor Otwell', - ], +```php +Route::get('/users', function () { + return [ + 'users' => [ + [ + 'name' => 'Taylor Otwell', ], - ]; - }); + ], + ]; +}); - $response->assertJsonFragment(['name' => 'Taylor Otwell']); +$response->assertJsonFragment(['name' => 'Taylor Otwell']); +``` #### assertJsonIsArray Assert that the response JSON is an array: - $response->assertJsonIsArray(); +```php +$response->assertJsonIsArray(); +``` #### assertJsonIsObject Assert that the response JSON is an object: - $response->assertJsonIsObject(); +```php +$response->assertJsonIsObject(); +``` #### assertJsonMissing Assert that the response does not contain the given JSON data: - $response->assertJsonMissing(array $data); +```php +$response->assertJsonMissing(array $data); +``` #### assertJsonMissingExact Assert that the response does not contain the exact JSON data: - $response->assertJsonMissingExact(array $data); +```php +$response->assertJsonMissingExact(array $data); +``` #### assertJsonMissingValidationErrors Assert that the response has no JSON validation errors for the given keys: - $response->assertJsonMissingValidationErrors($keys); +```php +$response->assertJsonMissingValidationErrors($keys); +``` > [!NOTE] > The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. @@ -1200,7 +1300,9 @@ Assert that the response has no JSON validation errors for the given keys: Assert that the response contains the given data at the specified path: - $response->assertJsonPath($path, $expectedValue); +```php +$response->assertJsonPath($path, $expectedValue); +``` For example, if the following JSON response is returned by your application: @@ -1214,14 +1316,18 @@ For example, if the following JSON response is returned by your application: You may assert that the `name` property of the `user` object matches a given value like so: - $response->assertJsonPath('user.name', 'Steve Schoger'); +```php +$response->assertJsonPath('user.name', 'Steve Schoger'); +``` #### assertJsonMissingPath Assert that the response does not contain the given path: - $response->assertJsonMissingPath($path); +```php +$response->assertJsonMissingPath($path); +``` For example, if the following JSON response is returned by your application: @@ -1235,14 +1341,18 @@ For example, if the following JSON response is returned by your application: You may assert that it does not contain the `email` property of the `user` object: - $response->assertJsonMissingPath('user.email'); +```php +$response->assertJsonMissingPath('user.email'); +``` #### assertJsonStructure Assert that the response has a given JSON structure: - $response->assertJsonStructure(array $structure); +```php +$response->assertJsonStructure(array $structure); +``` For example, if the JSON response returned by your application contains the following data: @@ -1256,11 +1366,13 @@ For example, if the JSON response returned by your application contains the foll You may assert that the JSON structure matches your expectations like so: - $response->assertJsonStructure([ - 'user' => [ - 'name', - ] - ]); +```php +$response->assertJsonStructure([ + 'user' => [ + 'name', + ] +]); +``` Sometimes, JSON responses returned by your application may contain arrays of objects: @@ -1283,22 +1395,26 @@ Sometimes, JSON responses returned by your application may contain arrays of obj In this situation, you may use the `*` character to assert against the structure of all of the objects in the array: - $response->assertJsonStructure([ - 'user' => [ - '*' => [ - 'name', - 'age', - 'location' - ] +```php +$response->assertJsonStructure([ + 'user' => [ + '*' => [ + 'name', + 'age', + 'location' ] - ]); + ] +]); +``` #### assertJsonValidationErrors Assert that the response has the given JSON validation errors for the given keys. This method should be used when asserting against responses where the validation errors are returned as a JSON structure instead of being flashed to the session: - $response->assertJsonValidationErrors(array $data, $responseKey = 'errors'); +```php +$response->assertJsonValidationErrors(array $data, $responseKey = 'errors'); +``` > [!NOTE] > The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. @@ -1308,215 +1424,277 @@ Assert that the response has the given JSON validation errors for the given keys Assert the response has any JSON validation errors for the given key: - $response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors'); +```php +$response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors'); +``` #### assertMethodNotAllowed Assert that the response has a method not allowed (405) HTTP status code: - $response->assertMethodNotAllowed(); +```php +$response->assertMethodNotAllowed(); +``` #### assertMovedPermanently Assert that the response has a moved permanently (301) HTTP status code: - $response->assertMovedPermanently(); +```php +$response->assertMovedPermanently(); +``` #### assertLocation Assert that the response has the given URI value in the `Location` header: - $response->assertLocation($uri); +```php +$response->assertLocation($uri); +``` #### assertContent Assert that the given string matches the response content: - $response->assertContent($value); +```php +$response->assertContent($value); +``` #### assertNoContent Assert that the response has the given HTTP status code and no content: - $response->assertNoContent($status = 204); +```php +$response->assertNoContent($status = 204); +``` #### assertStreamedContent Assert that the given string matches the streamed response content: - $response->assertStreamedContent($value); +```php +$response->assertStreamedContent($value); +``` #### assertNotFound Assert that the response has a not found (404) HTTP status code: - $response->assertNotFound(); +```php +$response->assertNotFound(); +``` #### assertOk Assert that the response has a 200 HTTP status code: - $response->assertOk(); +```php +$response->assertOk(); +``` #### assertPaymentRequired Assert that the response has a payment required (402) HTTP status code: - $response->assertPaymentRequired(); +```php +$response->assertPaymentRequired(); +``` #### assertPlainCookie Assert that the response contains the given unencrypted cookie: - $response->assertPlainCookie($cookieName, $value = null); +```php +$response->assertPlainCookie($cookieName, $value = null); +``` #### assertRedirect Assert that the response is a redirect to the given URI: - $response->assertRedirect($uri = null); +```php +$response->assertRedirect($uri = null); +``` #### assertRedirectContains Assert whether the response is redirecting to a URI that contains the given string: - $response->assertRedirectContains($string); +```php +$response->assertRedirectContains($string); +``` #### assertRedirectToRoute Assert that the response is a redirect to the given [named route](/docs/{{version}}/routing#named-routes): - $response->assertRedirectToRoute($name, $parameters = []); +```php +$response->assertRedirectToRoute($name, $parameters = []); +``` #### assertRedirectToSignedRoute Assert that the response is a redirect to the given [signed route](/docs/{{version}}/urls#signed-urls): - $response->assertRedirectToSignedRoute($name = null, $parameters = []); +```php +$response->assertRedirectToSignedRoute($name = null, $parameters = []); +``` #### assertRequestTimeout Assert that the response has a request timeout (408) HTTP status code: - $response->assertRequestTimeout(); +```php +$response->assertRequestTimeout(); +``` #### assertSee Assert that the given string is contained within the response. This assertion will automatically escape the given string unless you pass a second argument of `false`: - $response->assertSee($value, $escaped = true); +```php +$response->assertSee($value, $escaped = true); +``` #### assertSeeInOrder Assert that the given strings are contained in order within the response. This assertion will automatically escape the given strings unless you pass a second argument of `false`: - $response->assertSeeInOrder(array $values, $escaped = true); +```php +$response->assertSeeInOrder(array $values, $escaped = true); +``` #### assertSeeText Assert that the given string is contained within the response text. This assertion will automatically escape the given string unless you pass a second argument of `false`. The response content will be passed to the `strip_tags` PHP function before the assertion is made: - $response->assertSeeText($value, $escaped = true); +```php +$response->assertSeeText($value, $escaped = true); +``` #### assertSeeTextInOrder Assert that the given strings are contained in order within the response text. This assertion will automatically escape the given strings unless you pass a second argument of `false`. The response content will be passed to the `strip_tags` PHP function before the assertion is made: - $response->assertSeeTextInOrder(array $values, $escaped = true); +```php +$response->assertSeeTextInOrder(array $values, $escaped = true); +``` #### assertServerError Assert that the response has a server error (>= 500 , < 600) HTTP status code: - $response->assertServerError(); +```php +$response->assertServerError(); +``` #### assertServiceUnavailable Assert that the response has a "Service Unavailable" (503) HTTP status code: - $response->assertServiceUnavailable(); +```php +$response->assertServiceUnavailable(); +``` #### assertSessionHas Assert that the session contains the given piece of data: - $response->assertSessionHas($key, $value = null); +```php +$response->assertSessionHas($key, $value = null); +``` If needed, a closure can be provided as the second argument to the `assertSessionHas` method. The assertion will pass if the closure returns `true`: - $response->assertSessionHas($key, function (User $value) { - return $value->name === 'Taylor Otwell'; - }); +```php +$response->assertSessionHas($key, function (User $value) { + return $value->name === 'Taylor Otwell'; +}); +``` #### assertSessionHasInput Assert that the session has a given value in the [flashed input array](/docs/{{version}}/responses#redirecting-with-flashed-session-data): - $response->assertSessionHasInput($key, $value = null); +```php +$response->assertSessionHasInput($key, $value = null); +``` If needed, a closure can be provided as the second argument to the `assertSessionHasInput` method. The assertion will pass if the closure returns `true`: - use Illuminate\Support\Facades\Crypt; +```php +use Illuminate\Support\Facades\Crypt; - $response->assertSessionHasInput($key, function (string $value) { - return Crypt::decryptString($value) === 'secret'; - }); +$response->assertSessionHasInput($key, function (string $value) { + return Crypt::decryptString($value) === 'secret'; +}); +``` #### assertSessionHasAll Assert that the session contains a given array of key / value pairs: - $response->assertSessionHasAll(array $data); +```php +$response->assertSessionHasAll(array $data); +``` For example, if your application's session contains `name` and `status` keys, you may assert that both exist and have the specified values like so: - $response->assertSessionHasAll([ - 'name' => 'Taylor Otwell', - 'status' => 'active', - ]); +```php +$response->assertSessionHasAll([ + 'name' => 'Taylor Otwell', + 'status' => 'active', +]); +``` #### assertSessionHasErrors Assert that the session contains an error for the given `$keys`. If `$keys` is an associative array, assert that the session contains a specific error message (value) for each field (key). This method should be used when testing routes that flash validation errors to the session instead of returning them as a JSON structure: - $response->assertSessionHasErrors( - array $keys = [], $format = null, $errorBag = 'default' - ); +```php +$response->assertSessionHasErrors( + array $keys = [], $format = null, $errorBag = 'default' +); +``` For example, to assert that the `name` and `email` fields have validation error messages that were flashed to the session, you may invoke the `assertSessionHasErrors` method like so: - $response->assertSessionHasErrors(['name', 'email']); +```php +$response->assertSessionHasErrors(['name', 'email']); +``` Or, you may assert that a given field has a particular validation error message: - $response->assertSessionHasErrors([ - 'name' => 'The given name was invalid.' - ]); +```php +$response->assertSessionHasErrors([ + 'name' => 'The given name was invalid.' +]); +``` > [!NOTE] > The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. @@ -1526,21 +1704,27 @@ Or, you may assert that a given field has a particular validation error message: Assert that the session contains an error for the given `$keys` within a specific [error bag](/docs/{{version}}/validation#named-error-bags). If `$keys` is an associative array, assert that the session contains a specific error message (value) for each field (key), within the error bag: - $response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null); +```php +$response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null); +``` #### assertSessionHasNoErrors Assert that the session has no validation errors: - $response->assertSessionHasNoErrors(); +```php +$response->assertSessionHasNoErrors(); +``` #### assertSessionDoesntHaveErrors Assert that the session has no validation errors for the given keys: - $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); +```php +$response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); +``` > [!NOTE] > The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. @@ -1550,87 +1734,111 @@ Assert that the session has no validation errors for the given keys: Assert that the session does not contain the given key: - $response->assertSessionMissing($key); +```php +$response->assertSessionMissing($key); +``` #### assertStatus Assert that the response has a given HTTP status code: - $response->assertStatus($code); +```php +$response->assertStatus($code); +``` #### assertSuccessful Assert that the response has a successful (>= 200 and < 300) HTTP status code: - $response->assertSuccessful(); +```php +$response->assertSuccessful(); +``` #### assertTooManyRequests Assert that the response has a too many requests (429) HTTP status code: - $response->assertTooManyRequests(); +```php +$response->assertTooManyRequests(); +``` #### assertUnauthorized Assert that the response has an unauthorized (401) HTTP status code: - $response->assertUnauthorized(); +```php +$response->assertUnauthorized(); +``` #### assertUnprocessable Assert that the response has an unprocessable entity (422) HTTP status code: - $response->assertUnprocessable(); +```php +$response->assertUnprocessable(); +``` #### assertUnsupportedMediaType Assert that the response has an unsupported media type (415) HTTP status code: - $response->assertUnsupportedMediaType(); +```php +$response->assertUnsupportedMediaType(); +``` #### assertValid Assert that the response has no validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session: - // Assert that no validation errors are present... - $response->assertValid(); +```php +// Assert that no validation errors are present... +$response->assertValid(); - // Assert that the given keys do not have validation errors... - $response->assertValid(['name', 'email']); +// Assert that the given keys do not have validation errors... +$response->assertValid(['name', 'email']); +``` #### assertInvalid Assert that the response has validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session: - $response->assertInvalid(['name', 'email']); +```php +$response->assertInvalid(['name', 'email']); +``` You may also assert that a given key has a particular validation error message. When doing so, you may provide the entire message or only a small portion of the message: - $response->assertInvalid([ - 'name' => 'The name field is required.', - 'email' => 'valid email address', - ]); +```php +$response->assertInvalid([ + 'name' => 'The name field is required.', + 'email' => 'valid email address', +]); +``` #### assertViewHas Assert that the response view contains a given piece of data: - $response->assertViewHas($key, $value = null); +```php +$response->assertViewHas($key, $value = null); +``` Passing a closure as the second argument to the `assertViewHas` method will allow you to inspect and make assertions against a particular piece of view data: - $response->assertViewHas('user', function (User $user) { - return $user->name === 'Taylor'; - }); +```php +$response->assertViewHas('user', function (User $user) { + return $user->name === 'Taylor'; +}); +``` In addition, view data may be accessed as array variables on the response, allowing you to conveniently inspect it: @@ -1647,35 +1855,45 @@ $this->assertEquals('Taylor', $response['name']); Assert that the response view has a given list of data: - $response->assertViewHasAll(array $data); +```php +$response->assertViewHasAll(array $data); +``` This method may be used to assert that the view simply contains data matching the given keys: - $response->assertViewHasAll([ - 'name', - 'email', - ]); +```php +$response->assertViewHasAll([ + 'name', + 'email', +]); +``` Or, you may assert that the view data is present and has specific values: - $response->assertViewHasAll([ - 'name' => 'Taylor Otwell', - 'email' => 'taylor@example.com,', - ]); +```php +$response->assertViewHasAll([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@example.com,', +]); +``` #### assertViewIs Assert that the given view was returned by the route: - $response->assertViewIs($value); +```php +$response->assertViewIs($value); +``` #### assertViewMissing Assert that the given data key was not made available to the view returned in the application's response: - $response->assertViewMissing($key); +```php +$response->assertViewMissing($key); +``` ### Authentication Assertions @@ -1687,21 +1905,27 @@ Laravel also provides a variety of authentication related assertions that you ma Assert that a user is authenticated: - $this->assertAuthenticated($guard = null); +```php +$this->assertAuthenticated($guard = null); +``` #### assertGuest Assert that a user is not authenticated: - $this->assertGuest($guard = null); +```php +$this->assertGuest($guard = null); +``` #### assertAuthenticatedAs Assert that a specific user is authenticated: - $this->assertAuthenticatedAs($user, $guard = null); +```php +$this->assertAuthenticatedAs($user, $guard = null); +``` ## Validation Assertions @@ -1713,22 +1937,28 @@ Laravel provides two primary validation related assertions that you may use to e Assert that the response has no validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session: - // Assert that no validation errors are present... - $response->assertValid(); +```php +// Assert that no validation errors are present... +$response->assertValid(); - // Assert that the given keys do not have validation errors... - $response->assertValid(['name', 'email']); +// Assert that the given keys do not have validation errors... +$response->assertValid(['name', 'email']); +``` #### assertInvalid Assert that the response has validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session: - $response->assertInvalid(['name', 'email']); +```php +$response->assertInvalid(['name', 'email']); +``` You may also assert that a given key has a particular validation error message. When doing so, you may provide the entire message or only a small portion of the message: - $response->assertInvalid([ - 'name' => 'The name field is required.', - 'email' => 'valid email address', - ]); +```php +$response->assertInvalid([ + 'name' => 'The name field is required.', + 'email' => 'valid email address', +]); +``` diff --git a/localization.md b/localization.md index c5e0313db6b..7022bffe5b9 100644 --- a/localization.md +++ b/localization.md @@ -22,17 +22,21 @@ Laravel's localization features provide a convenient way to retrieve strings in Laravel provides two ways to manage translation strings. First, language strings may be stored in files within the application's `lang` directory. Within this directory, there may be subdirectories for each language supported by the application. This is the approach Laravel uses to manage translation strings for built-in Laravel features such as validation error messages: - /lang - /en - messages.php - /es - messages.php +```text +/lang + /en + messages.php + /es + messages.php +``` Or, translation strings may be defined within JSON files that are placed within the `lang` directory. When taking this approach, each language supported by your application would have a corresponding JSON file within this directory. This approach is recommended for applications that have a large number of translatable strings: - /lang - en.json - es.json +```text +/lang + en.json + es.json +``` We'll discuss each approach to managing translation strings within this documentation. @@ -54,47 +58,53 @@ You may also configure a "fallback language", which will be used when the defaul You may modify the default language for a single HTTP request at runtime using the `setLocale` method provided by the `App` facade: - use Illuminate\Support\Facades\App; +```php +use Illuminate\Support\Facades\App; - Route::get('/greeting/{locale}', function (string $locale) { - if (! in_array($locale, ['en', 'es', 'fr'])) { - abort(400); - } +Route::get('/greeting/{locale}', function (string $locale) { + if (! in_array($locale, ['en', 'es', 'fr'])) { + abort(400); + } - App::setLocale($locale); + App::setLocale($locale); - // ... - }); + // ... +}); +``` #### Determining the Current Locale You may use the `currentLocale` and `isLocale` methods on the `App` facade to determine the current locale or check if the locale is a given value: - use Illuminate\Support\Facades\App; +```php +use Illuminate\Support\Facades\App; - $locale = App::currentLocale(); +$locale = App::currentLocale(); - if (App::isLocale('en')) { - // ... - } +if (App::isLocale('en')) { + // ... +} +``` ### Pluralization Language You may instruct Laravel's "pluralizer", which is used by Eloquent and other portions of the framework to convert singular strings to plural strings, to use a language other than English. This may be accomplished by invoking the `useLanguage` method within the `boot` method of one of your application's service providers. The pluralizer's currently supported languages are: `french`, `norwegian-bokmal`, `portuguese`, `spanish`, and `turkish`: - use Illuminate\Support\Pluralizer; +```php +use Illuminate\Support\Pluralizer; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Pluralizer::useLanguage('spanish'); +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Pluralizer::useLanguage('spanish'); - // ... - } + // ... +} +``` > [!WARNING] > If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). @@ -107,21 +117,25 @@ You may instruct Laravel's "pluralizer", which is used by Eloquent and other por Typically, translation strings are stored in files within the `lang` directory. Within this directory, there should be a subdirectory for each language supported by your application. This is the approach Laravel uses to manage translation strings for built-in Laravel features such as validation error messages: - /lang - /en - messages.php - /es - messages.php +```text +/lang + /en + messages.php + /es + messages.php +``` All language files return an array of keyed strings. For example: - 'Welcome to our application!', - ]; +return [ + 'welcome' => 'Welcome to our application!', +]; +``` > [!WARNING] > For languages that differ by territory, you should name the language directories according to the ISO 15897. For example, "en_GB" should be used for British English rather than "en-gb". @@ -148,35 +162,47 @@ You should not define translation string keys that conflict with other translati You may retrieve translation strings from your language files using the `__` helper function. If you are using "short keys" to define your translation strings, you should pass the file that contains the key and the key itself to the `__` function using "dot" syntax. For example, let's retrieve the `welcome` translation string from the `lang/en/messages.php` language file: - echo __('messages.welcome'); +```php +echo __('messages.welcome'); +``` If the specified translation string does not exist, the `__` function will return the translation string key. So, using the example above, the `__` function would return `messages.welcome` if the translation string does not exist. If you are using your [default translation strings as your translation keys](#using-translation-strings-as-keys), you should pass the default translation of your string to the `__` function; - echo __('I love programming.'); +```php +echo __('I love programming.'); +``` Again, if the translation string does not exist, the `__` function will return the translation string key that it was given. If you are using the [Blade templating engine](/docs/{{version}}/blade), you may use the `{{ }}` echo syntax to display the translation string: - {{ __('messages.welcome') }} +```blade +{{ __('messages.welcome') }} +``` ### Replacing Parameters in Translation Strings If you wish, you may define placeholders in your translation strings. All placeholders are prefixed with a `:`. For example, you may define a welcome message with a placeholder name: - 'welcome' => 'Welcome, :name', +```php +'welcome' => 'Welcome, :name', +``` To replace the placeholders when retrieving a translation string, you may pass an array of replacements as the second argument to the `__` function: - echo __('messages.welcome', ['name' => 'dayle']); +```php +echo __('messages.welcome', ['name' => 'dayle']); +``` If your placeholder contains all capital letters, or only has its first letter capitalized, the translated value will be capitalized accordingly: - 'welcome' => 'Welcome, :NAME', // Welcome, DAYLE - 'goodbye' => 'Goodbye, :Name', // Goodbye, Dayle +```php +'welcome' => 'Welcome, :NAME', // Welcome, DAYLE +'goodbye' => 'Goodbye, :Name', // Goodbye, Dayle +``` #### Object Replacement Formatting @@ -185,25 +211,29 @@ If you attempt to provide an object as a translation placeholder, the object's ` In these cases, Laravel allows you to register a custom formatting handler for that particular type of object. To accomplish this, you should invoke the translator's `stringable` method. The `stringable` method accepts a closure, which should type-hint the type of object that it is responsible for formatting. Typically, the `stringable` method should be invoked within the `boot` method of your application's `AppServiceProvider` class: - use Illuminate\Support\Facades\Lang; - use Money\Money; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Lang::stringable(function (Money $money) { - return $money->formatTo('en_GB'); - }); - } +```php +use Illuminate\Support\Facades\Lang; +use Money\Money; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Lang::stringable(function (Money $money) { + return $money->formatTo('en_GB'); + }); +} +``` ### Pluralization Pluralization is a complex problem, as different languages have a variety of complex rules for pluralization; however, Laravel can help you translate strings differently based on pluralization rules that you define. Using a `|` character, you may distinguish singular and plural forms of a string: - 'apples' => 'There is one apple|There are many apples', +```php +'apples' => 'There is one apple|There are many apples', +``` Of course, pluralization is also supported when using [translation strings as keys](#using-translation-strings-as-keys): @@ -215,21 +245,29 @@ Of course, pluralization is also supported when using [translation strings as ke You may even create more complex pluralization rules which specify translation strings for multiple ranges of values: - 'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many', +```php +'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many', +``` After defining a translation string that has pluralization options, you may use the `trans_choice` function to retrieve the line for a given "count". In this example, since the count is greater than one, the plural form of the translation string is returned: - echo trans_choice('messages.apples', 10); +```php +echo trans_choice('messages.apples', 10); +``` You may also define placeholder attributes in pluralization strings. These placeholders may be replaced by passing an array as the third argument to the `trans_choice` function: - 'minutes_ago' => '{1} :value minute ago|[2,*] :value minutes ago', +```php +'minutes_ago' => '{1} :value minute ago|[2,*] :value minutes ago', - echo trans_choice('time.minutes_ago', 5, ['value' => 5]); +echo trans_choice('time.minutes_ago', 5, ['value' => 5]); +``` If you would like to display the integer value that was passed to the `trans_choice` function, you may use the built-in `:count` placeholder: - 'apples' => '{0} There are none|{1} There is one|[2,*] There are :count', +```php +'apples' => '{0} There are none|{1} There is one|[2,*] There are :count', +``` ## Overriding Package Language Files diff --git a/logging.md b/logging.md index b7d5ced1330..e9bd266c85d 100644 --- a/logging.md +++ b/logging.md @@ -63,11 +63,13 @@ Each log channel is powered by a "driver". The driver determines how and where t By default, Monolog is instantiated with a "channel name" that matches the current environment, such as `production` or `local`. To change this value, you may add a `name` option to your channel's configuration: - 'stack' => [ - 'driver' => 'stack', - 'name' => 'channel-name', - 'channels' => ['single', 'slack'], - ], +```php +'stack' => [ + 'driver' => 'stack', + 'name' => 'channel-name', + 'channels' => ['single', 'slack'], +], +``` ### Channel Prerequisites @@ -114,23 +116,27 @@ By default, Slack will only receive logs at the `critical` level and above; howe PHP, Laravel, and other libraries often notify their users that some of their features have been deprecated and will be removed in a future version. If you would like to log these deprecation warnings, you may specify your preferred `deprecations` log channel using the `LOG_DEPRECATIONS_CHANNEL` environment variable, or within your application's `config/logging.php` configuration file: - 'deprecations' => [ - 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), - 'trace' => env('LOG_DEPRECATIONS_TRACE', false), - ], +```php +'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), +], - 'channels' => [ - // ... - ] +'channels' => [ + // ... +] +``` Or, you may define a log channel named `deprecations`. If a log channel with this name exists, it will always be used to log deprecations: - 'channels' => [ - 'deprecations' => [ - 'driver' => 'single', - 'path' => storage_path('logs/php-deprecation-warnings.log'), - ], +```php +'channels' => [ + 'deprecations' => [ + 'driver' => 'single', + 'path' => storage_path('logs/php-deprecation-warnings.log'), ], +], +``` ## Building Log Stacks @@ -172,128 +178,142 @@ Take note of the `level` configuration option present on the `syslog` and `slack So, imagine we log a message using the `debug` method: - Log::debug('An informational message.'); +```php +Log::debug('An informational message.'); +``` Given our configuration, the `syslog` channel will write the message to the system log; however, since the error message is not `critical` or above, it will not be sent to Slack. However, if we log an `emergency` message, it will be sent to both the system log and Slack since the `emergency` level is above our minimum level threshold for both channels: - Log::emergency('The system is down!'); +```php +Log::emergency('The system is down!'); +``` ## Writing Log Messages You may write information to the logs using the `Log` [facade](/docs/{{version}}/facades). As previously mentioned, the logger provides the eight logging levels defined in the [RFC 5424 specification](https://tools.ietf.org/html/rfc5424): **emergency**, **alert**, **critical**, **error**, **warning**, **notice**, **info** and **debug**: - use Illuminate\Support\Facades\Log; - - Log::emergency($message); - Log::alert($message); - Log::critical($message); - Log::error($message); - Log::warning($message); - Log::notice($message); - Log::info($message); - Log::debug($message); +```php +use Illuminate\Support\Facades\Log; + +Log::emergency($message); +Log::alert($message); +Log::critical($message); +Log::error($message); +Log::warning($message); +Log::notice($message); +Log::info($message); +Log::debug($message); +``` You may call any of these methods to log a message for the corresponding level. By default, the message will be written to the default log channel as configured by your `logging` configuration file: - $id]); - - return view('user.profile', [ - 'user' => User::findOrFail($id) - ]); - } + Log::info('Showing the user profile for user: {id}', ['id' => $id]); + + return view('user.profile', [ + 'user' => User::findOrFail($id) + ]); } +} +``` ### Contextual Information An array of contextual data may be passed to the log methods. This contextual data will be formatted and displayed with the log message: - use Illuminate\Support\Facades\Log; +```php +use Illuminate\Support\Facades\Log; - Log::info('User {id} failed to login.', ['id' => $user->id]); +Log::info('User {id} failed to login.', ['id' => $user->id]); +``` Occasionally, you may wish to specify some contextual information that should be included with all subsequent log entries in a particular channel. For example, you may wish to log a request ID that is associated with each incoming request to your application. To accomplish this, you may call the `Log` facade's `withContext` method: - $requestId - ]); + Log::withContext([ + 'request-id' => $requestId + ]); - $response = $next($request); + $response = $next($request); - $response->headers->set('Request-Id', $requestId); + $response->headers->set('Request-Id', $requestId); - return $response; - } + return $response; } +} +``` If you would like to share contextual information across _all_ logging channels, you may invoke the `Log::shareContext()` method. This method will provide the contextual information to all created channels and any channels that are created subsequently: - $requestId + ]); - class AssignRequestId - { - /** - * Handle an incoming request. - * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next - */ - public function handle(Request $request, Closure $next): Response - { - $requestId = (string) Str::uuid(); - - Log::shareContext([ - 'request-id' => $requestId - ]); - - // ... - } + // ... } +} +``` > [!NOTE] > If you need to share log context while processing queued jobs, you may utilize [job middleware](/docs/{{version}}/queues#job-middleware). @@ -303,36 +323,44 @@ If you would like to share contextual information across _all_ logging channels, Sometimes you may wish to log a message to a channel other than your application's default channel. You may use the `channel` method on the `Log` facade to retrieve and log to any channel defined in your configuration file: - use Illuminate\Support\Facades\Log; +```php +use Illuminate\Support\Facades\Log; - Log::channel('slack')->info('Something happened!'); +Log::channel('slack')->info('Something happened!'); +``` If you would like to create an on-demand logging stack consisting of multiple channels, you may use the `stack` method: - Log::stack(['single', 'slack'])->info('Something happened!'); +```php +Log::stack(['single', 'slack'])->info('Something happened!'); +``` #### On-Demand Channels It is also possible to create an on-demand channel by providing the configuration at runtime without that configuration being present in your application's `logging` configuration file. To accomplish this, you may pass a configuration array to the `Log` facade's `build` method: - use Illuminate\Support\Facades\Log; +```php +use Illuminate\Support\Facades\Log; - Log::build([ - 'driver' => 'single', - 'path' => storage_path('logs/custom.log'), - ])->info('Something happened!'); +Log::build([ + 'driver' => 'single', + 'path' => storage_path('logs/custom.log'), +])->info('Something happened!'); +``` You may also wish to include an on-demand channel in an on-demand logging stack. This can be achieved by including your on-demand channel instance in the array passed to the `stack` method: - use Illuminate\Support\Facades\Log; +```php +use Illuminate\Support\Facades\Log; - $channel = Log::build([ - 'driver' => 'single', - 'path' => storage_path('logs/custom.log'), - ]); +$channel = Log::build([ + 'driver' => 'single', + 'path' => storage_path('logs/custom.log'), +]); - Log::stack(['slack', $channel])->info('Something happened!'); +Log::stack(['slack', $channel])->info('Something happened!'); +``` ## Monolog Channel Customization @@ -344,37 +372,41 @@ Sometimes you may need complete control over how Monolog is configured for an ex To get started, define a `tap` array on the channel's configuration. The `tap` array should contain a list of classes that should have an opportunity to customize (or "tap" into) the Monolog instance after it is created. There is no conventional location where these classes should be placed, so you are free to create a directory within your application to contain these classes: - 'single' => [ - 'driver' => 'single', - 'tap' => [App\Logging\CustomizeFormatter::class], - 'path' => storage_path('logs/laravel.log'), - 'level' => env('LOG_LEVEL', 'debug'), - 'replace_placeholders' => true, - ], +```php +'single' => [ + 'driver' => 'single', + 'tap' => [App\Logging\CustomizeFormatter::class], + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, +], +``` Once you have configured the `tap` option on your channel, you're ready to define the class that will customize your Monolog instance. This class only needs a single method: `__invoke`, which receives an `Illuminate\Log\Logger` instance. The `Illuminate\Log\Logger` instance proxies all method calls to the underlying Monolog instance: - getHandlers() as $handler) { - $handler->setFormatter(new LineFormatter( - '[%datetime%] %channel%.%level_name%: %message% %context% %extra%' - )); - } + foreach ($logger->getHandlers() as $handler) { + $handler->setFormatter(new LineFormatter( + '[%datetime%] %channel%.%level_name%: %message% %context% %extra%' + )); } } +} +``` > [!NOTE] > All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected. @@ -386,36 +418,42 @@ Monolog has a variety of [available handlers](https://github.com/Seldaek/monolog When using the `monolog` driver, the `handler` configuration option is used to specify which handler will be instantiated. Optionally, any constructor parameters the handler needs may be specified using the `with` configuration option: - 'logentries' => [ - 'driver' => 'monolog', - 'handler' => Monolog\Handler\SyslogUdpHandler::class, - 'with' => [ - 'host' => 'my.logentries.internal.datahubhost.company.com', - 'port' => '10000', - ], +```php +'logentries' => [ + 'driver' => 'monolog', + 'handler' => Monolog\Handler\SyslogUdpHandler::class, + 'with' => [ + 'host' => 'my.logentries.internal.datahubhost.company.com', + 'port' => '10000', ], +], +``` #### Monolog Formatters When using the `monolog` driver, the Monolog `LineFormatter` will be used as the default formatter. However, you may customize the type of formatter passed to the handler using the `formatter` and `formatter_with` configuration options: - 'browser' => [ - 'driver' => 'monolog', - 'handler' => Monolog\Handler\BrowserConsoleHandler::class, - 'formatter' => Monolog\Formatter\HtmlFormatter::class, - 'formatter_with' => [ - 'dateFormat' => 'Y-m-d', - ], +```php +'browser' => [ + 'driver' => 'monolog', + 'handler' => Monolog\Handler\BrowserConsoleHandler::class, + 'formatter' => Monolog\Formatter\HtmlFormatter::class, + 'formatter_with' => [ + 'dateFormat' => 'Y-m-d', ], +], +``` If you are using a Monolog handler that is capable of providing its own formatter, you may set the value of the `formatter` configuration option to `default`: - 'newrelic' => [ - 'driver' => 'monolog', - 'handler' => Monolog\Handler\NewRelicHandler::class, - 'formatter' => 'default', - ], +```php +'newrelic' => [ + 'driver' => 'monolog', + 'handler' => Monolog\Handler\NewRelicHandler::class, + 'formatter' => 'default', +], +``` #### Monolog Processors @@ -424,54 +462,60 @@ Monolog can also process messages before logging them. You can create your own p If you would like to customize the processors for a `monolog` driver, add a `processors` configuration value to your channel's configuration: - 'memory' => [ - 'driver' => 'monolog', - 'handler' => Monolog\Handler\StreamHandler::class, - 'with' => [ - 'stream' => 'php://stderr', - ], - 'processors' => [ - // Simple syntax... - Monolog\Processor\MemoryUsageProcessor::class, - - // With options... - [ - 'processor' => Monolog\Processor\PsrLogMessageProcessor::class, - 'with' => ['removeUsedContextFields' => true], - ], - ], - ], +```php +'memory' => [ + 'driver' => 'monolog', + 'handler' => Monolog\Handler\StreamHandler::class, + 'with' => [ + 'stream' => 'php://stderr', + ], + 'processors' => [ + // Simple syntax... + Monolog\Processor\MemoryUsageProcessor::class, + + // With options... + [ + 'processor' => Monolog\Processor\PsrLogMessageProcessor::class, + 'with' => ['removeUsedContextFields' => true], + ], + ], +], +``` ### Creating Custom Channels via Factories If you would like to define an entirely custom channel in which you have full control over Monolog's instantiation and configuration, you may specify a `custom` driver type in your `config/logging.php` configuration file. Your configuration should include a `via` option that contains the name of the factory class which will be invoked to create the Monolog instance: - 'channels' => [ - 'example-custom-channel' => [ - 'driver' => 'custom', - 'via' => App\Logging\CreateCustomLogger::class, - ], +```php +'channels' => [ + 'example-custom-channel' => [ + 'driver' => 'custom', + 'via' => App\Logging\CreateCustomLogger::class, ], +], +``` Once you have configured the `custom` driver channel, you're ready to define the class that will create your Monolog instance. This class only needs a single `__invoke` method which should return the Monolog logger instance. The method will receive the channels configuration array as its only argument: - ## Tailing Log Messages Using Pail diff --git a/mail.md b/mail.md index e764550a9e2..b70b444e831 100644 --- a/mail.md +++ b/mail.md @@ -61,34 +61,42 @@ composer require symfony/mailgun-mailer symfony/http-client Next, you will need to make two changes in your application's `config/mail.php` configuration file. First, set your default mailer to `mailgun`: - 'default' => env('MAIL_MAILER', 'mailgun'), +```php +'default' => env('MAIL_MAILER', 'mailgun'), +``` Second, add the following configuration array to your array of `mailers`: - 'mailgun' => [ - 'transport' => 'mailgun', - // 'client' => [ - // 'timeout' => 5, - // ], - ], +```php +'mailgun' => [ + 'transport' => 'mailgun', + // 'client' => [ + // 'timeout' => 5, + // ], +], +``` After configuring your application's default mailer, add the following options to your `config/services.php` configuration file: - 'mailgun' => [ - 'domain' => env('MAILGUN_DOMAIN'), - 'secret' => env('MAILGUN_SECRET'), - 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), - 'scheme' => 'https', - ], +```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: - 'mailgun' => [ - 'domain' => env('MAILGUN_DOMAIN'), - 'secret' => env('MAILGUN_SECRET'), - 'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'), - 'scheme' => 'https', - ], +```php +'mailgun' => [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'), + 'scheme' => 'https', +], +``` #### Postmark Driver @@ -101,19 +109,23 @@ 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 +'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: - 'postmark' => [ - 'transport' => 'postmark', - 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), - // 'client' => [ - // 'timeout' => 5, - // ], - ], +```php +'postmark' => [ + 'transport' => 'postmark', + 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], +], +``` This way you are also able to set up multiple Postmark mailers with different message streams. @@ -128,9 +140,11 @@ 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 +'resend' => [ + 'key' => env('RESEND_KEY'), +], +``` #### SES Driver @@ -143,20 +157,24 @@ 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 +'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: - 'ses' => [ - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), - 'token' => env('AWS_SESSION_TOKEN'), - ], +```php +'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'token' => env('AWS_SESSION_TOKEN'), +], +``` To interact with SES's [subscription management features](https://docs.aws.amazon.com/ses/latest/dg/sending-email-subscription-management.html), you may return the `X-Ses-List-Management-Options` header in the array returned by the [`headers`](#headers) method of a mail message: @@ -176,17 +194,19 @@ public function headers(): Headers If you would like to define [additional options](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sesv2-2019-09-27.html#sendemail) that Laravel should pass to the AWS SDK's `SendEmail` method when sending an email, you may define an `options` array within your `ses` configuration: - 'ses' => [ - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), - 'options' => [ - 'ConfigurationSetName' => 'MyConfigurationSet', - 'EmailTags' => [ - ['Name' => 'foo', 'Value' => 'bar'], - ], +```php +'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'options' => [ + 'ConfigurationSetName' => 'MyConfigurationSet', + 'EmailTags' => [ + ['Name' => 'foo', 'Value' => 'bar'], ], ], +], +``` #### MailerSend Driver @@ -224,43 +244,51 @@ Sometimes, an external service you have configured to send your application's ma To accomplish this, you should define a mailer within your application's `mail` configuration file that uses the `failover` transport. The configuration array for your application's `failover` mailer should contain an array of `mailers` that reference the order in which configured mailers should be chosen for delivery: - 'mailers' => [ - 'failover' => [ - 'transport' => 'failover', - 'mailers' => [ - 'postmark', - 'mailgun', - 'sendmail', - ], +```php +'mailers' => [ + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'postmark', + 'mailgun', + 'sendmail', ], - - // ... ], + // ... +], +``` + Once your failover mailer has been defined, you should set this mailer as the default mailer used by your application by specifying its name as the value of the `default` configuration key within your application's `mail` configuration file: - 'default' => env('MAIL_MAILER', 'failover'), +```php +'default' => env('MAIL_MAILER', 'failover'), +``` ### Round Robin Configuration The `roundrobin` transport allows you to distribute your mailing workload across multiple mailers. To get started, define a mailer within your application's `mail` configuration file that uses the `roundrobin` transport. The configuration array for your application's `roundrobin` mailer should contain an array of `mailers` that reference which configured mailers should be used for delivery: - 'mailers' => [ - 'roundrobin' => [ - 'transport' => 'roundrobin', - 'mailers' => [ - 'ses', - 'postmark', - ], +```php +'mailers' => [ + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', ], - - // ... ], + // ... +], +``` + Once your round robin mailer has been defined, you should set this mailer as the default mailer used by your application by specifying its name as the value of the `default` configuration key within your application's `mail` configuration file: - 'default' => env('MAIL_MAILER', 'roundrobin'), +```php +'default' => env('MAIL_MAILER', 'roundrobin'), +``` The round robin transport selects a random mailer from the list of configured mailers and then switches to the next available mailer for each subsequent email. In contrast to `failover` transport, which helps to achieve *[high availability](https://en.wikipedia.org/wiki/High_availability)*, the `roundrobin` transport provides *[load balancing](https://en.wikipedia.org/wiki/Load_balancing_(computing))*. @@ -288,58 +316,68 @@ The `envelope` method returns an `Illuminate\Mail\Mailables\Envelope` object tha First, let's explore configuring the sender of the email. Or, in other words, who the email is going to be "from". There are two ways to configure the sender. First, you may specify the "from" address on your message's envelope: - use Illuminate\Mail\Mailables\Address; - use Illuminate\Mail\Mailables\Envelope; - - /** - * Get the message envelope. - */ - public function envelope(): Envelope - { - return new Envelope( - from: new Address('jeffrey@example.com', 'Jeffrey Way'), - subject: 'Order Shipped', - ); - } - -If you would like, you may also specify a `replyTo` address: +```php +use Illuminate\Mail\Mailables\Address; +use Illuminate\Mail\Mailables\Envelope; +/** + * Get the message envelope. + */ +public function envelope(): Envelope +{ return new Envelope( from: new Address('jeffrey@example.com', 'Jeffrey Way'), - replyTo: [ - new Address('taylor@example.com', 'Taylor Otwell'), - ], subject: 'Order Shipped', ); +} +``` + +If you would like, you may also specify a `replyTo` address: + +```php +return new Envelope( + from: new Address('jeffrey@example.com', 'Jeffrey Way'), + replyTo: [ + new Address('taylor@example.com', 'Taylor Otwell'), + ], + subject: 'Order Shipped', +); +``` #### Using a Global `from` Address However, if your application uses the same "from" address for all of its emails, it can become cumbersome to add it to each mailable class you generate. Instead, you may specify a global "from" address in your `config/mail.php` configuration file. This address will be used if no other "from" address is specified within the mailable class: - 'from' => [ - 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), - 'name' => env('MAIL_FROM_NAME', 'Example'), - ], +```php +'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), +], +``` In addition, you may define a global "reply_to" address within your `config/mail.php` configuration file: - 'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'], +```php +'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'], +``` ### Configuring the View Within a mailable class's `content` method, you may define the `view`, or which template should be used when rendering the email's contents. Since each email typically uses a [Blade template](/docs/{{version}}/blade) to render its contents, you have the full power and convenience of the Blade templating engine when building your email's HTML: - /** - * Get the message content definition. - */ - public function content(): Content - { - return new Content( - view: 'mail.orders.shipped', - ); - } +```php +/** + * Get the message content definition. + */ +public function content(): Content +{ + return new Content( + view: 'mail.orders.shipped', + ); +} +``` > [!NOTE] > You may wish to create a `resources/views/emails` directory to house all of your email templates; however, you are free to place them wherever you wish within your `resources/views` directory. @@ -349,23 +387,27 @@ Within a mailable class's `content` method, you may define the `view`, or which If you would like to define a plain-text version of your email, you may specify the plain-text template when creating the message's `Content` definition. Like the `view` parameter, the `text` parameter should be a template name which will be used to render the contents of the email. You are free to define both an HTML and plain-text version of your message: - /** - * Get the message content definition. - */ - public function content(): Content - { - return new Content( - view: 'mail.orders.shipped', - text: 'mail.orders.shipped-text' - ); - } - -For clarity, the `html` parameter may be used as an alias of the `view` parameter: - +```php +/** + * Get the message content definition. + */ +public function content(): Content +{ return new Content( - html: 'mail.orders.shipped', + view: 'mail.orders.shipped', text: 'mail.orders.shipped-text' ); +} +``` + +For clarity, the `html` parameter may be used as an alias of the `view` parameter: + +```php +return new Content( + html: 'mail.orders.shipped', + text: 'mail.orders.shipped-text' +); +``` ### View Data @@ -375,192 +417,212 @@ For clarity, the `html` parameter may be used as an alias of the `view` paramete Typically, you will want to pass some data to your view that you can utilize when rendering the email's HTML. There are two ways you may make data available to your view. First, any public property defined on your mailable class will automatically be made available to the view. So, for example, you may pass data into your mailable class's constructor and set that data to public properties defined on the class: - - Price: {{ $order->price }} - +```blade +
    + Price: {{ $order->price }} +
    +``` #### Via the `with` Parameter: If you would like to customize the format of your email's data before it is sent to the template, you may manually pass your data to the view via the `Content` definition's `with` parameter. Typically, you will still pass data via the mailable class's constructor; however, you should set this data to `protected` or `private` properties so the data is not automatically made available to the template: - $this->order->name, - 'orderPrice' => $this->order->price, - ], - ); - } + return new Content( + view: 'mail.orders.shipped', + with: [ + 'orderName' => $this->order->name, + 'orderPrice' => $this->order->price, + ], + ); } +} +``` Once the data has been passed to the `with` method, it will automatically be available in your view, so you may access it like you would access any other data in your Blade templates: -
    - Price: {{ $orderPrice }} -
    +```blade +
    + Price: {{ $orderPrice }} +
    +``` ### Attachments To add attachments to an email, you will add attachments to the array returned by the message's `attachments` method. First, you may add an attachment by providing a file path to the `fromPath` method provided by the `Attachment` class: - use Illuminate\Mail\Mailables\Attachment; +```php +use Illuminate\Mail\Mailables\Attachment; - /** - * Get the attachments for the message. - * - * @return array - */ - public function attachments(): array - { - return [ - Attachment::fromPath('/path/to/file'), - ]; - } +/** + * Get the attachments for the message. + * + * @return array + */ +public function attachments(): array +{ + return [ + Attachment::fromPath('/path/to/file'), + ]; +} +``` When attaching files to a message, you may also specify the display name and / or MIME type for the attachment using the `as` and `withMime` methods: - /** - * Get the attachments for the message. - * - * @return array - */ - public function attachments(): array - { - return [ - Attachment::fromPath('/path/to/file') - ->as('name.pdf') - ->withMime('application/pdf'), - ]; - } +```php +/** + * Get the attachments for the message. + * + * @return array + */ +public function attachments(): array +{ + return [ + Attachment::fromPath('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; +} +``` #### Attaching Files From Disk If you have stored a file on one of your [filesystem disks](/docs/{{version}}/filesystem), you may attach it to the email using the `fromStorage` attachment method: - /** - * Get the attachments for the message. - * - * @return array - */ - public function attachments(): array - { - return [ - Attachment::fromStorage('/path/to/file'), - ]; - } +```php +/** + * Get the attachments for the message. + * + * @return array + */ +public function attachments(): array +{ + return [ + Attachment::fromStorage('/path/to/file'), + ]; +} +``` Of course, you may also specify the attachment's name and MIME type: - /** - * Get the attachments for the message. - * - * @return array - */ - public function attachments(): array - { - return [ - Attachment::fromStorage('/path/to/file') - ->as('name.pdf') - ->withMime('application/pdf'), - ]; - } +```php +/** + * Get the attachments for the message. + * + * @return array + */ +public function attachments(): array +{ + return [ + Attachment::fromStorage('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; +} +``` The `fromStorageDisk` method may be used if you need to specify a storage disk other than your default disk: - /** - * Get the attachments for the message. - * - * @return array - */ - public function attachments(): array - { - return [ - Attachment::fromStorageDisk('s3', '/path/to/file') - ->as('name.pdf') - ->withMime('application/pdf'), - ]; - } +```php +/** + * Get the attachments for the message. + * + * @return array + */ +public function attachments(): array +{ + return [ + Attachment::fromStorageDisk('s3', '/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; +} +``` #### Raw Data Attachments The `fromData` attachment method may be used to attach a raw string of bytes as an attachment. For example, you might use this method if you have generated a PDF in memory and want to attach it to the email without writing it to disk. The `fromData` method accepts a closure which resolves the raw data bytes as well as the name that the attachment should be assigned: - /** - * Get the attachments for the message. - * - * @return array - */ - public function attachments(): array - { - return [ - Attachment::fromData(fn () => $this->pdf, 'Report.pdf') - ->withMime('application/pdf'), - ]; - } +```php +/** + * Get the attachments for the message. + * + * @return array + */ +public function attachments(): array +{ + return [ + Attachment::fromData(fn () => $this->pdf, 'Report.pdf') + ->withMime('application/pdf'), + ]; +} +``` ### Inline Attachments @@ -598,54 +660,64 @@ While attaching files to messages via simple string paths is often sufficient, i To get started, implement the `Illuminate\Contracts\Mail\Attachable` interface on the object that will be attachable to messages. This interface dictates that your class defines a `toMailAttachment` method that returns an `Illuminate\Mail\Attachment` instance: - - */ - public function attachments(): array - { - return [$this->photo]; - } +```php +/** + * Get the attachments for the message. + * + * @return array + */ +public function attachments(): array +{ + return [$this->photo]; +} +``` Of course, attachment data may be stored on a remote file storage service such as Amazon S3. So, Laravel also allows you to generate attachment instances from data that is stored on one of your application's [filesystem disks](/docs/{{version}}/filesystem): - // Create an attachment from a file on your default disk... - return Attachment::fromStorage($this->path); +```php +// Create an attachment from a file on your default disk... +return Attachment::fromStorage($this->path); - // Create an attachment from a file on a specific disk... - return Attachment::fromStorageDisk('backblaze', $this->path); +// Create an attachment from a file on a specific disk... +return Attachment::fromStorageDisk('backblaze', $this->path); +``` In addition, you may create attachment instances via data that you have in memory. To accomplish this, provide a closure to the `fromData` method. The closure should return the raw data that represents the attachment: - return Attachment::fromData(fn () => $this->content, 'Photo Name'); +```php +return Attachment::fromData(fn () => $this->content, 'Photo Name'); +``` Laravel also provides additional methods that you may use to customize your attachments. For example, you may use the `as` and `withMime` methods to customize the file's name and MIME type: - return Attachment::fromPath('/path/to/file') - ->as('Photo Name') - ->withMime('image/jpeg'); +```php +return Attachment::fromPath('/path/to/file') + ->as('Photo Name') + ->withMime('image/jpeg'); +``` ### Headers @@ -654,44 +726,48 @@ Sometimes you may need to attach additional headers to the outgoing message. For To accomplish this, define a `headers` method on your mailable. The `headers` method should return an `Illuminate\Mail\Mailables\Headers` instance. This class accepts `messageId`, `references`, and `text` parameters. Of course, you may provide only the parameters you need for your particular message: - use Illuminate\Mail\Mailables\Headers; +```php +use Illuminate\Mail\Mailables\Headers; - /** - * Get the message headers. - */ - public function headers(): Headers - { - return new Headers( - messageId: 'custom-message-id@example.com', - references: ['previous-message@example.com'], - text: [ - 'X-Custom-Header' => 'Custom Value', - ], - ); - } +/** + * Get the message headers. + */ +public function headers(): Headers +{ + return new Headers( + messageId: 'custom-message-id@example.com', + references: ['previous-message@example.com'], + text: [ + 'X-Custom-Header' => 'Custom Value', + ], + ); +} +``` ### Tags and Metadata Some third-party email providers such as Mailgun and Postmark support message "tags" and "metadata", which may be used to group and track emails sent by your application. You may add tags and metadata to an email message via your `Envelope` definition: - use Illuminate\Mail\Mailables\Envelope; +```php +use Illuminate\Mail\Mailables\Envelope; - /** - * Get the message envelope. - * - * @return \Illuminate\Mail\Mailables\Envelope - */ - public function envelope(): Envelope - { - return new Envelope( - subject: 'Order Shipped', - tags: ['shipment'], - metadata: [ - 'order_id' => $this->order->id, - ], - ); - } +/** + * Get the message envelope. + * + * @return \Illuminate\Mail\Mailables\Envelope + */ +public function envelope(): Envelope +{ + return new Envelope( + subject: 'Order Shipped', + tags: ['shipment'], + metadata: [ + 'order_id' => $this->order->id, + ], + ); +} +``` If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/docs/mailgun/user-manual/tracking-messages/#tagging) and [metadata](https://documentation.mailgun.com/docs/mailgun/user-manual/tracking-messages/#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). @@ -702,23 +778,25 @@ If your application is using Amazon SES to send emails, you should use the `meta Laravel's mail capabilities are powered by Symfony Mailer. Laravel allows you to register custom callbacks that will be invoked with the Symfony Message instance before sending the message. This gives you an opportunity to deeply customize the message before it is sent. To accomplish this, define a `using` parameter on your `Envelope` definition: - use Illuminate\Mail\Mailables\Envelope; - use Symfony\Component\Mime\Email; +```php +use Illuminate\Mail\Mailables\Envelope; +use Symfony\Component\Mime\Email; - /** - * Get the message envelope. - */ - public function envelope(): Envelope - { - return new Envelope( - subject: 'Order Shipped', - using: [ - function (Email $message) { - // ... - }, - ] - ); - } +/** + * Get the message envelope. + */ +public function envelope(): Envelope +{ + return new Envelope( + subject: 'Order Shipped', + using: [ + function (Email $message) { + // ... + }, + ] + ); +} +``` ## Markdown Mailables @@ -736,20 +814,22 @@ 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 +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 @@ -835,58 +915,66 @@ To customize the theme for an individual mailable, you may set the `$theme` prop To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/facades). The `to` method accepts an email address, a user instance, or a collection of users. If you pass an object or collection of objects, the mailer will automatically use their `email` and `name` properties when determining the email's recipients, so make sure these attributes are available on your objects. Once you have specified your recipients, you may pass an instance of your mailable class to the `send` method: - order_id); + $order = Order::findOrFail($request->order_id); - // Ship the order... + // Ship the order... - Mail::to($request->user())->send(new OrderShipped($order)); + Mail::to($request->user())->send(new OrderShipped($order)); - return redirect('/orders'); - } + return redirect('/orders'); } +} +``` You are not limited to just specifying the "to" recipients when sending a message. You are free to set "to", "cc", and "bcc" recipients by chaining their respective methods together: - Mail::to($request->user()) - ->cc($moreUsers) - ->bcc($evenMoreUsers) - ->send(new OrderShipped($order)); +```php +Mail::to($request->user()) + ->cc($moreUsers) + ->bcc($evenMoreUsers) + ->send(new OrderShipped($order)); +``` #### Looping Over Recipients Occasionally, you may need to send a mailable to a list of recipients by iterating over an array of recipients / email addresses. However, since the `to` method appends email addresses to the mailable's list of recipients, each iteration through the loop will send another email to every previous recipient. Therefore, you should always re-create the mailable instance for each recipient: - foreach (['taylor@example.com', 'dries@example.com'] as $recipient) { - Mail::to($recipient)->send(new OrderShipped($order)); - } +```php +foreach (['taylor@example.com', 'dries@example.com'] as $recipient) { + Mail::to($recipient)->send(new OrderShipped($order)); +} +``` #### Sending Mail via a Specific Mailer By default, Laravel will send email using the mailer configured as the `default` mailer in your application's `mail` configuration file. However, you may use the `mailer` method to send a message using a specific mailer configuration: - Mail::mailer('postmark') - ->to($request->user()) - ->send(new OrderShipped($order)); +```php +Mail::mailer('postmark') + ->to($request->user()) + ->send(new OrderShipped($order)); +``` ### Queueing Mail @@ -896,10 +984,12 @@ By default, Laravel will send email using the mailer configured as the `default` Since sending email messages can negatively impact the response time of your application, many developers choose to queue email messages for background sending. Laravel makes this easy using its built-in [unified queue API](/docs/{{version}}/queues). To queue a mail message, use the `queue` method on the `Mail` facade after specifying the message's recipients: - Mail::to($request->user()) - ->cc($moreUsers) - ->bcc($evenMoreUsers) - ->queue(new OrderShipped($order)); +```php +Mail::to($request->user()) + ->cc($moreUsers) + ->bcc($evenMoreUsers) + ->queue(new OrderShipped($order)); +``` This method will automatically take care of pushing a job onto the queue so the message is sent in the background. You will need to [configure your queues](/docs/{{version}}/queues) before using this feature. @@ -908,36 +998,42 @@ This method will automatically take care of pushing a job onto the queue so the If you wish to delay the delivery of a queued email message, you may use the `later` method. As its first argument, the `later` method accepts a `DateTime` instance indicating when the message should be sent: - Mail::to($request->user()) - ->cc($moreUsers) - ->bcc($evenMoreUsers) - ->later(now()->addMinutes(10), new OrderShipped($order)); +```php +Mail::to($request->user()) + ->cc($moreUsers) + ->bcc($evenMoreUsers) + ->later(now()->addMinutes(10), new OrderShipped($order)); +``` #### Pushing to Specific Queues Since all mailable classes generated using the `make:mail` command make use of the `Illuminate\Bus\Queueable` trait, you may call the `onQueue` and `onConnection` methods on any mailable class instance, allowing you to specify the connection and queue name for the message: - $message = (new OrderShipped($order)) - ->onConnection('sqs') - ->onQueue('emails'); - - Mail::to($request->user()) - ->cc($moreUsers) - ->bcc($evenMoreUsers) - ->queue($message); +```php +$message = (new OrderShipped($order)) + ->onConnection('sqs') + ->onQueue('emails'); + +Mail::to($request->user()) + ->cc($moreUsers) + ->bcc($evenMoreUsers) + ->queue($message); +``` #### Queueing by Default If you have mailable classes that you want to always be queued, you may implement the `ShouldQueue` contract on the class. Now, even if you call the `send` method when mailing, the mailable will still be queued since it implements the contract: - use Illuminate\Contracts\Queue\ShouldQueue; +```php +use Illuminate\Contracts\Queue\ShouldQueue; - class OrderShipped extends Mailable implements ShouldQueue - { - // ... - } +class OrderShipped extends Mailable implements ShouldQueue +{ + // ... +} +``` #### Queued Mailables and Database Transactions @@ -946,33 +1042,37 @@ When queued mailables are dispatched within database transactions, they may be p If your queue connection's `after_commit` configuration option is set to `false`, you may still indicate that a particular queued mailable should be dispatched after all open database transactions have been committed by calling the `afterCommit` method when sending the mail message: - Mail::to($request->user())->send( - (new OrderShipped($order))->afterCommit() - ); +```php +Mail::to($request->user())->send( + (new OrderShipped($order))->afterCommit() +); +``` Alternatively, you may call the `afterCommit` method from your mailable's constructor: - afterCommit(); - } + $this->afterCommit(); } +} +``` > [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -982,23 +1082,27 @@ Alternatively, you may call the `afterCommit` method from your mailable's constr Sometimes you may wish to capture the HTML content of a mailable without sending it. To accomplish this, you may call the `render` method of the mailable. This method will return the evaluated HTML content of the mailable as a string: - use App\Mail\InvoicePaid; - use App\Models\Invoice; +```php +use App\Mail\InvoicePaid; +use App\Models\Invoice; - $invoice = Invoice::find(1); +$invoice = Invoice::find(1); - return (new InvoicePaid($invoice))->render(); +return (new InvoicePaid($invoice))->render(); +``` ### Previewing Mailables in the Browser When designing a mailable's template, it is convenient to quickly preview the rendered mailable in your browser like a typical Blade template. For this reason, Laravel allows you to return any mailable directly from a route closure or controller. When a mailable 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('/mailable', function () { - $invoice = App\Models\Invoice::find(1); +```php +Route::get('/mailable', function () { + $invoice = App\Models\Invoice::find(1); - return new App\Mail\InvoicePaid($invoice); - }); + return new App\Mail\InvoicePaid($invoice); +}); +``` ## Localizing Mailables @@ -1007,31 +1111,37 @@ Laravel allows you to send mailables in a locale other than the request's curren To accomplish this, the `Mail` facade offers a `locale` method to set the desired language. The application will change into this locale when the mailable's template is being evaluated and then revert back to the previous locale when evaluation is complete: - Mail::to($request->user())->locale('es')->send( - new OrderShipped($order) - ); +```php +Mail::to($request->user())->locale('es')->send( + new OrderShipped($order) +); +``` ### User Preferred Locales Sometimes, applications store each user's preferred locale. By implementing the `HasLocalePreference` contract on one or more of your models, you may instruct Laravel to use this stored locale when sending mail: - use Illuminate\Contracts\Translation\HasLocalePreference; +```php +use Illuminate\Contracts\Translation\HasLocalePreference; - class User extends Model implements HasLocalePreference +class User extends Model implements HasLocalePreference +{ + /** + * Get the user's preferred locale. + */ + public function preferredLocale(): string { - /** - * Get the user's preferred locale. - */ - public function preferredLocale(): string - { - return $this->locale; - } + return $this->locale; } +} +``` Once you have implemented the interface, Laravel will automatically use the preferred locale when sending mailables and notifications to the model. Therefore, there is no need to call the `locale` method when using this interface: - Mail::to($request->user())->send(new OrderShipped($order)); +```php +Mail::to($request->user())->send(new OrderShipped($order)); +``` ## Testing @@ -1194,59 +1304,69 @@ class ExampleTest extends TestCase If you are queueing mailables for delivery in the background, you should use the `assertQueued` method instead of `assertSent`: - Mail::assertQueued(OrderShipped::class); - Mail::assertNotQueued(OrderShipped::class); - Mail::assertNothingQueued(); - Mail::assertQueuedCount(3); +```php +Mail::assertQueued(OrderShipped::class); +Mail::assertNotQueued(OrderShipped::class); +Mail::assertNothingQueued(); +Mail::assertQueuedCount(3); +``` You may pass a closure to the `assertSent`, `assertNotSent`, `assertQueued`, or `assertNotQueued` 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; - }); +```php +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 mailable: - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) { - return $mail->hasTo($user->email) && - $mail->hasCc('...') && - $mail->hasBcc('...') && - $mail->hasReplyTo('...') && - $mail->hasFrom('...') && - $mail->hasSubject('...'); - }); +```php +Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) { + return $mail->hasTo($user->email) && + $mail->hasCc('...') && + $mail->hasBcc('...') && + $mail->hasReplyTo('...') && + $mail->hasFrom('...') && + $mail->hasSubject('...'); +}); +``` The mailable instance also includes several helpful methods for examining the attachments on a mailable: - use Illuminate\Mail\Mailables\Attachment; +```php +use Illuminate\Mail\Mailables\Attachment; - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { - return $mail->hasAttachment( - Attachment::fromPath('/path/to/file') - ->as('name.pdf') - ->withMime('application/pdf') - ); - }); +Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { + return $mail->hasAttachment( + Attachment::fromPath('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf') + ); +}); - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { - return $mail->hasAttachment( - Attachment::fromStorageDisk('s3', '/path/to/file') - ); - }); +Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) { + return $mail->hasAttachment( + Attachment::fromStorageDisk('s3', '/path/to/file') + ); +}); - Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($pdfData) { - return $mail->hasAttachment( - Attachment::fromData(fn () => $pdfData, 'name.pdf') - ); - }); +Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($pdfData) { + return $mail->hasAttachment( + Attachment::fromData(fn () => $pdfData, 'name.pdf') + ); +}); +``` You may have noticed that there are two methods for asserting that mail was not sent: `assertNotSent` and `assertNotQueued`. Sometimes you may wish to assert that no mail was sent **or** queued. To accomplish this, you may use the `assertNothingOutgoing` and `assertNotOutgoing` methods: - Mail::assertNothingOutgoing(); +```php +Mail::assertNothingOutgoing(); - Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) { - return $mail->order->id === $order->id; - }); +Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) { + return $mail->order->id === $order->id; +}); +``` ## Mail and Local Development @@ -1270,106 +1390,116 @@ If you are using [Laravel Sail](/docs/{{version}}/sail), you may preview your me Finally, you may specify a global "to" address by invoking the `alwaysTo` method offered by the `Mail` facade. Typically, this method should be called from the `boot` method of one of your application's service providers: - use Illuminate\Support\Facades\Mail; +```php +use Illuminate\Support\Facades\Mail; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - if ($this->app->environment('local')) { - Mail::alwaysTo('taylor@example.com'); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + if ($this->app->environment('local')) { + Mail::alwaysTo('taylor@example.com'); } +} +``` ## Events Laravel dispatches two events while sending mail messages. The `MessageSending` event is dispatched prior to a message being sent, while the `MessageSent` event is dispatched after a message has been sent. Remember, these events are dispatched when the mail is being *sent*, not when it is queued. You may create [event listeners](/docs/{{version}}/events) for these events within your application: - use Illuminate\Mail\Events\MessageSending; - // use Illuminate\Mail\Events\MessageSent; +```php +use Illuminate\Mail\Events\MessageSending; +// use Illuminate\Mail\Events\MessageSent; - class LogMessage +class LogMessage +{ + /** + * Handle the given event. + */ + public function handle(MessageSending $event): void { - /** - * Handle the given event. - */ - public function handle(MessageSending $event): void - { - // ... - } + // ... } +} +``` ## Custom Transports Laravel includes a variety of mail transports; however, you may wish to write your own transports to deliver email via other services that Laravel does not support out of the box. To get started, define a class that extends the `Symfony\Component\Mailer\Transport\AbstractTransport` class. Then, implement the `doSend` and `__toString()` methods on your transport: - use MailchimpTransactional\ApiClient; - use Symfony\Component\Mailer\SentMessage; - use Symfony\Component\Mailer\Transport\AbstractTransport; - use Symfony\Component\Mime\Address; - use Symfony\Component\Mime\MessageConverter; +```php +use MailchimpTransactional\ApiClient; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\AbstractTransport; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\MessageConverter; - class MailchimpTransport extends AbstractTransport - { - /** - * Create a new Mailchimp transport instance. - */ - public function __construct( - protected ApiClient $client, - ) { - parent::__construct(); - } - - /** - * {@inheritDoc} - */ - protected function doSend(SentMessage $message): void - { - $email = MessageConverter::toEmail($message->getOriginalMessage()); - - $this->client->messages->send(['message' => [ - 'from_email' => $email->getFrom(), - 'to' => collect($email->getTo())->map(function (Address $email) { - return ['email' => $email->getAddress(), 'type' => 'to']; - })->all(), - 'subject' => $email->getSubject(), - 'text' => $email->getTextBody(), - ]]); - } - - /** - * Get the string representation of the transport. - */ - public function __toString(): string - { - return 'mailchimp'; - } +class MailchimpTransport extends AbstractTransport +{ + /** + * Create a new Mailchimp transport instance. + */ + public function __construct( + protected ApiClient $client, + ) { + parent::__construct(); } -Once you've defined your custom transport, you may register it via the `extend` method provided by the `Mail` facade. Typically, this should be done within the `boot` method of your application's `AppServiceProvider` service provider. A `$config` argument will be passed to the closure provided to the `extend` method. This argument will contain the configuration array defined for the mailer in the application's `config/mail.php` configuration file: - - use App\Mail\MailchimpTransport; - use Illuminate\Support\Facades\Mail; + /** + * {@inheritDoc} + */ + protected function doSend(SentMessage $message): void + { + $email = MessageConverter::toEmail($message->getOriginalMessage()); + + $this->client->messages->send(['message' => [ + 'from_email' => $email->getFrom(), + 'to' => collect($email->getTo())->map(function (Address $email) { + return ['email' => $email->getAddress(), 'type' => 'to']; + })->all(), + 'subject' => $email->getSubject(), + 'text' => $email->getTextBody(), + ]]); + } /** - * Bootstrap any application services. + * Get the string representation of the transport. */ - public function boot(): void + public function __toString(): string { - Mail::extend('mailchimp', function (array $config = []) { - return new MailchimpTransport(/* ... */); - }); + return 'mailchimp'; } +} +``` + +Once you've defined your custom transport, you may register it via the `extend` method provided by the `Mail` facade. Typically, this should be done within the `boot` method of your application's `AppServiceProvider` service provider. A `$config` argument will be passed to the closure provided to the `extend` method. This argument will contain the configuration array defined for the mailer in the application's `config/mail.php` configuration file: + +```php +use App\Mail\MailchimpTransport; +use Illuminate\Support\Facades\Mail; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Mail::extend('mailchimp', function (array $config = []) { + return new MailchimpTransport(/* ... */); + }); +} +``` Once your custom transport has been defined and registered, you may create a mailer definition within your application's `config/mail.php` configuration file that utilizes the new transport: - 'mailchimp' => [ - 'transport' => 'mailchimp', - // ... - ], +```php +'mailchimp' => [ + 'transport' => 'mailchimp', + // ... +], +``` ### Additional Symfony Transports @@ -1382,35 +1512,41 @@ composer require symfony/brevo-mailer symfony/http-client Once the Brevo mailer package has been installed, you may add an entry for your Brevo API credentials to your application's `services` configuration file: - 'brevo' => [ - 'key' => 'your-api-key', - ], +```php +'brevo' => [ + 'key' => 'your-api-key', +], +``` Next, you may use the `Mail` facade's `extend` method to register the transport with Laravel. Typically, this should be done within the `boot` method of a service provider: - use Illuminate\Support\Facades\Mail; - use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory; - use Symfony\Component\Mailer\Transport\Dsn; +```php +use Illuminate\Support\Facades\Mail; +use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory; +use Symfony\Component\Mailer\Transport\Dsn; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Mail::extend('brevo', function () { - return (new BrevoTransportFactory)->create( - new Dsn( - 'brevo+api', - 'default', - config('services.brevo.key') - ) - ); - }); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Mail::extend('brevo', function () { + return (new BrevoTransportFactory)->create( + new Dsn( + 'brevo+api', + 'default', + config('services.brevo.key') + ) + ); + }); +} +``` Once your transport has been registered, you may create a mailer definition within your application's config/mail.php configuration file that utilizes the new transport: - 'brevo' => [ - 'transport' => 'brevo', - // ... - ], +```php +'brevo' => [ + 'transport' => 'brevo', + // ... +], +``` diff --git a/middleware.md b/middleware.md index 95db7bb8b7d..c696ea8597b 100644 --- a/middleware.md +++ b/middleware.md @@ -29,30 +29,32 @@ 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: - input('token') !== 'my-secret-token') { - return redirect('/home'); - } - - return $next($request); + if ($request->input('token') !== 'my-secret-token') { + return redirect('/home'); } + + return $next($request); } +} +``` 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`. @@ -66,45 +68,49 @@ It's best to envision middleware as a series of "layers" HTTP requests must pass Of course, a middleware can perform tasks before or after passing the request deeper into the application. For example, the following middleware would perform some task **before** the request is handled by the application: - ## Registering Middleware @@ -114,11 +120,13 @@ However, this middleware would perform its task **after** the request is handled If you want a middleware to run during every HTTP request to your application, you may append it to the global middleware stack in your application's `bootstrap/app.php` file: - use App\Http\Middleware\EnsureTokenIsValid; +```php +use App\Http\Middleware\EnsureTokenIsValid; - ->withMiddleware(function (Middleware $middleware) { - $middleware->append(EnsureTokenIsValid::class); - }) +->withMiddleware(function (Middleware $middleware) { + $middleware->append(EnsureTokenIsValid::class); +}) +``` The `$middleware` object provided to the `withMiddleware` closure is an instance of `Illuminate\Foundation\Configuration\Middleware` and is responsible for managing the middleware assigned to your application's routes. The `append` method adds the middleware to the end of the list of global middleware. If you would like to add a middleware to the beginning of the list, you should use the `prepend` method. @@ -127,62 +135,72 @@ The `$middleware` object provided to the `withMiddleware` closure is an instance If you would like to manage Laravel's global middleware stack manually, you may provide Laravel's default stack of global middleware to the `use` method. Then, you may adjust the default middleware stack as necessary: - ->withMiddleware(function (Middleware $middleware) { - $middleware->use([ - \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, - // \Illuminate\Http\Middleware\TrustHosts::class, - \Illuminate\Http\Middleware\TrustProxies::class, - \Illuminate\Http\Middleware\HandleCors::class, - \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class, - \Illuminate\Http\Middleware\ValidatePostSize::class, - \Illuminate\Foundation\Http\Middleware\TrimStrings::class, - \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, - ]); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->use([ + \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, + // \Illuminate\Http\Middleware\TrustHosts::class, + \Illuminate\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Http\Middleware\ValidatePostSize::class, + \Illuminate\Foundation\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]); +}) +``` ### Assigning Middleware to Routes If you would like to assign middleware to specific routes, you may invoke the `middleware` method when defining the route: - use App\Http\Middleware\EnsureTokenIsValid; +```php +use App\Http\Middleware\EnsureTokenIsValid; - Route::get('/profile', function () { - // ... - })->middleware(EnsureTokenIsValid::class); +Route::get('/profile', function () { + // ... +})->middleware(EnsureTokenIsValid::class); +``` You may assign multiple middleware to the route by passing an array of middleware names to the `middleware` method: - Route::get('/', function () { - // ... - })->middleware([First::class, Second::class]); +```php +Route::get('/', function () { + // ... +})->middleware([First::class, Second::class]); +``` #### Excluding Middleware When assigning middleware to a group of routes, you may occasionally need to prevent the middleware from being applied to an individual route within the group. You may accomplish this using the `withoutMiddleware` method: - use App\Http\Middleware\EnsureTokenIsValid; +```php +use App\Http\Middleware\EnsureTokenIsValid; - Route::middleware([EnsureTokenIsValid::class])->group(function () { - Route::get('/', function () { - // ... - }); - - Route::get('/profile', function () { - // ... - })->withoutMiddleware([EnsureTokenIsValid::class]); +Route::middleware([EnsureTokenIsValid::class])->group(function () { + Route::get('/', function () { + // ... }); + Route::get('/profile', function () { + // ... + })->withoutMiddleware([EnsureTokenIsValid::class]); +}); +``` + You may also exclude a given set of middleware from an entire [group](/docs/{{version}}/routing#route-groups) of route definitions: - use App\Http\Middleware\EnsureTokenIsValid; +```php +use App\Http\Middleware\EnsureTokenIsValid; - Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () { - Route::get('/profile', function () { - // ... - }); +Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () { + Route::get('/profile', function () { + // ... }); +}); +``` The `withoutMiddleware` method can only remove route middleware and does not apply to [global middleware](#global-middleware). @@ -191,30 +209,34 @@ The `withoutMiddleware` method can only remove route middleware and does not app Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish this using the `appendToGroup` method within your application's `bootstrap/app.php` file: - use App\Http\Middleware\First; - use App\Http\Middleware\Second; +```php +use App\Http\Middleware\First; +use App\Http\Middleware\Second; - ->withMiddleware(function (Middleware $middleware) { - $middleware->appendToGroup('group-name', [ - First::class, - Second::class, - ]); +->withMiddleware(function (Middleware $middleware) { + $middleware->appendToGroup('group-name', [ + First::class, + Second::class, + ]); - $middleware->prependToGroup('group-name', [ - First::class, - Second::class, - ]); - }) + $middleware->prependToGroup('group-name', [ + First::class, + Second::class, + ]); +}) +``` Middleware groups may be assigned to routes and controller actions using the same syntax as individual middleware: - Route::get('/', function () { - // ... - })->middleware('group-name'); +```php +Route::get('/', function () { + // ... +})->middleware('group-name'); - Route::middleware(['group-name'])->group(function () { - // ... - }); +Route::middleware(['group-name'])->group(function () { + // ... +}); +``` #### Laravel's Default Middleware Groups @@ -244,56 +266,64 @@ Laravel includes predefined `web` and `api` middleware groups that contain commo If you would like to append or prepend middleware to these groups, you may use the `web` and `api` methods within your application's `bootstrap/app.php` file. The `web` and `api` methods are convenient alternatives to the `appendToGroup` method: - use App\Http\Middleware\EnsureTokenIsValid; - use App\Http\Middleware\EnsureUserIsSubscribed; +```php +use App\Http\Middleware\EnsureTokenIsValid; +use App\Http\Middleware\EnsureUserIsSubscribed; - ->withMiddleware(function (Middleware $middleware) { - $middleware->web(append: [ - EnsureUserIsSubscribed::class, - ]); +->withMiddleware(function (Middleware $middleware) { + $middleware->web(append: [ + EnsureUserIsSubscribed::class, + ]); - $middleware->api(prepend: [ - EnsureTokenIsValid::class, - ]); - }) + $middleware->api(prepend: [ + EnsureTokenIsValid::class, + ]); +}) +``` You may even replace one of Laravel's default middleware group entries with a custom middleware of your own: - use App\Http\Middleware\StartCustomSession; - use Illuminate\Session\Middleware\StartSession; +```php +use App\Http\Middleware\StartCustomSession; +use Illuminate\Session\Middleware\StartSession; - $middleware->web(replace: [ - StartSession::class => StartCustomSession::class, - ]); +$middleware->web(replace: [ + StartSession::class => StartCustomSession::class, +]); +``` Or, you may remove a middleware entirely: - $middleware->web(remove: [ - StartSession::class, - ]); +```php +$middleware->web(remove: [ + StartSession::class, +]); +``` #### Manually Managing Laravel's Default Middleware Groups If you would like to manually manage all of the middleware within Laravel's default `web` and `api` middleware groups, you may redefine the groups entirely. The example below will define the `web` and `api` middleware groups with their default middleware, allowing you to customize them as necessary: - ->withMiddleware(function (Middleware $middleware) { - $middleware->group('web', [ - \Illuminate\Cookie\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - // \Illuminate\Session\Middleware\AuthenticateSession::class, - ]); - - $middleware->group('api', [ - // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - // 'throttle:api', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ]); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->group('web', [ + \Illuminate\Cookie\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + // \Illuminate\Session\Middleware\AuthenticateSession::class, + ]); + + $middleware->group('api', [ + // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + // 'throttle:api', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ]); +}) +``` > [!NOTE] > By default, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `bootstrap/app.php` file. @@ -303,19 +333,23 @@ If you would like to manually manage all of the middleware within Laravel's defa You may assign aliases to middleware in your application's `bootstrap/app.php` file. Middleware aliases allow you to define a short alias for a given middleware class, which can be especially useful for middleware with long class names: - use App\Http\Middleware\EnsureUserIsSubscribed; +```php +use App\Http\Middleware\EnsureUserIsSubscribed; - ->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'subscribed' => EnsureUserIsSubscribed::class - ]); - }) +->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'subscribed' => EnsureUserIsSubscribed::class + ]); +}) +``` Once the middleware alias has been defined in your application's `bootstrap/app.php` file, you may use the alias when assigning the middleware to routes: - Route::get('/profile', function () { - // ... - })->middleware('subscribed'); +```php +Route::get('/profile', function () { + // ... +})->middleware('subscribed'); +``` For convenience, some of Laravel's built-in middleware are aliased by default. For example, the `auth` middleware is an alias for the `Illuminate\Auth\Middleware\Authenticate` middleware. Below is a list of the default middleware aliases: @@ -343,22 +377,24 @@ For convenience, some of Laravel's built-in middleware are aliased by default. F Rarely, you may need your middleware to execute in a specific order but not have control over their order when they are assigned to the route. In these situations, you may specify your middleware priority using the `priority` method in your application's `bootstrap/app.php` file: - ->withMiddleware(function (Middleware $middleware) { - $middleware->priority([ - \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, - \Illuminate\Cookie\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, - \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - \Illuminate\Routing\Middleware\ThrottleRequests::class, - \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, - \Illuminate\Auth\Middleware\Authorize::class, - ]); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->priority([ + \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, + \Illuminate\Cookie\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + \Illuminate\Routing\Middleware\ThrottleRequests::class, + \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, + \Illuminate\Auth\Middleware\Authorize::class, + ]); +}) +``` ## Middleware Parameters @@ -367,90 +403,100 @@ Middleware can also receive additional parameters. For example, if your applicat Additional middleware parameters will be passed to the middleware after the `$next` argument: - user()->hasRole($role)) { - // Redirect... - } - - return $next($request); + if (! $request->user()->hasRole($role)) { + // Redirect... } + return $next($request); } +} +``` + Middleware parameters may be specified when defining the route by separating the middleware name and parameters with a `:`: - use App\Http\Middleware\EnsureUserHasRole; +```php +use App\Http\Middleware\EnsureUserHasRole; - Route::put('/post/{id}', function (string $id) { - // ... - })->middleware(EnsureUserHasRole::class.':editor'); +Route::put('/post/{id}', function (string $id) { + // ... +})->middleware(EnsureUserHasRole::class.':editor'); +``` Multiple parameters may be delimited by commas: - Route::put('/post/{id}', function (string $id) { - // ... - })->middleware(EnsureUserHasRole::class.':editor,publisher'); +```php +Route::put('/post/{id}', function (string $id) { + // ... +})->middleware(EnsureUserHasRole::class.':editor,publisher'); +``` ## Terminable Middleware Sometimes a middleware may need to do some work after the HTTP response has been sent to the browser. If you define a `terminate` method on your middleware and your web server is using FastCGI, the `terminate` method will automatically be called after the response is sent to the browser: - app->singleton(TerminatingMiddleware::class); - } +/** + * Register any application services. + */ +public function register(): void +{ + $this->app->singleton(TerminatingMiddleware::class); +} +``` diff --git a/migrations.md b/migrations.md index f77fbd56360..0a44d45fbd1 100644 --- a/migrations.md +++ b/migrations.md @@ -80,55 +80,59 @@ A migration class contains two methods: `up` and `down`. The `up` method is used Within both of these methods, you may use the Laravel schema builder to expressively create and modify tables. To learn about all of the methods available on the `Schema` builder, [check out its documentation](#creating-tables). For example, the following migration creates a `flights` table: - id(); - $table->string('name'); - $table->string('airline'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::drop('flights'); - } - }; - - -#### Setting the Migration Connection +```php +id(); + $table->string('name'); + $table->string('airline'); + $table->timestamps(); + }); + } /** - * Run the migrations. + * Reverse the migrations. */ - public function up(): void + public function down(): void { - // ... + Schema::drop('flights'); } +}; +``` + + +#### Setting the Migration Connection + +If your migration will be interacting with a database connection other than your application's default database connection, you should set the `$connection` property of your migration: + +```php +/** + * The database connection that should be used by the migration. + * + * @var string + */ +protected $connection = 'pgsql'; + +/** + * Run the migrations. + */ +public function up(): void +{ + // ... +} +``` ## Running Migrations @@ -190,9 +194,9 @@ php artisan migrate:rollback --step=5 You may roll back a specific "batch" of migrations by providing the `batch` option to the `rollback` command, where the `batch` option corresponds to a batch value within your application's `migrations` database table. For example, the following command will roll back all migrations in batch three: - ```shell +```shell php artisan migrate:rollback --batch=3 - ``` +``` If you would like to see the SQL statements that will be executed by the migrations without actually running them, you may provide the `--pretend` flag to the `migrate:rollback` command: @@ -252,15 +256,17 @@ php artisan migrate:fresh --database=admin To create a new database table, use the `create` method on the `Schema` facade. The `create` method accepts two arguments: the first is the name of the table, while the second is a closure which receives a `Blueprint` object that may be used to define the new table: - use Illuminate\Database\Schema\Blueprint; - use Illuminate\Support\Facades\Schema; +```php +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; - Schema::create('users', function (Blueprint $table) { - $table->id(); - $table->string('name'); - $table->string('email'); - $table->timestamps(); - }); +Schema::create('users', function (Blueprint $table) { + $table->id(); + $table->string('name'); + $table->string('email'); + $table->timestamps(); +}); +``` When creating the table, you may use any of the schema builder's [column methods](#creating-columns) to define the table's columns. @@ -269,86 +275,104 @@ When creating the table, you may use any of the schema builder's [column methods You may determine the existence of a table, column, or index using the `hasTable`, `hasColumn`, and `hasIndex` methods: - if (Schema::hasTable('users')) { - // The "users" table exists... - } +```php +if (Schema::hasTable('users')) { + // The "users" table exists... +} - if (Schema::hasColumn('users', 'email')) { - // The "users" table exists and has an "email" column... - } +if (Schema::hasColumn('users', 'email')) { + // The "users" table exists and has an "email" column... +} - if (Schema::hasIndex('users', ['email'], 'unique')) { - // The "users" table exists and has a unique index on the "email" column... - } +if (Schema::hasIndex('users', ['email'], 'unique')) { + // The "users" table exists and has a unique index on the "email" column... +} +``` #### Database Connection and Table Options If you want to perform a schema operation on a database connection that is not your application's default connection, use the `connection` method: - Schema::connection('sqlite')->create('users', function (Blueprint $table) { - $table->id(); - }); +```php +Schema::connection('sqlite')->create('users', function (Blueprint $table) { + $table->id(); +}); +``` In addition, a few other properties and methods may be used to define other aspects of the table's creation. The `engine` property may be used to specify the table's storage engine when using MariaDB or MySQL: - Schema::create('users', function (Blueprint $table) { - $table->engine('InnoDB'); +```php +Schema::create('users', function (Blueprint $table) { + $table->engine('InnoDB'); - // ... - }); + // ... +}); +``` The `charset` and `collation` properties may be used to specify the character set and collation for the created table when using MariaDB or MySQL: - Schema::create('users', function (Blueprint $table) { - $table->charset('utf8mb4'); - $table->collation('utf8mb4_unicode_ci'); +```php +Schema::create('users', function (Blueprint $table) { + $table->charset('utf8mb4'); + $table->collation('utf8mb4_unicode_ci'); - // ... - }); + // ... +}); +``` The `temporary` method may be used to indicate that the table should be "temporary". Temporary tables are only visible to the current connection's database session and are dropped automatically when the connection is closed: - Schema::create('calculations', function (Blueprint $table) { - $table->temporary(); +```php +Schema::create('calculations', function (Blueprint $table) { + $table->temporary(); - // ... - }); + // ... +}); +``` If you would like to add a "comment" to a database table, you may invoke the `comment` method on the table instance. Table comments are currently only supported by MariaDB, MySQL, and PostgreSQL: - Schema::create('calculations', function (Blueprint $table) { - $table->comment('Business calculations'); +```php +Schema::create('calculations', function (Blueprint $table) { + $table->comment('Business calculations'); - // ... - }); + // ... +}); +``` ### Updating Tables The `table` method on the `Schema` facade may be used to update existing tables. Like the `create` method, the `table` method accepts two arguments: the name of the table and a closure that receives a `Blueprint` instance you may use to add columns or indexes to the table: - use Illuminate\Database\Schema\Blueprint; - use Illuminate\Support\Facades\Schema; +```php +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; - Schema::table('users', function (Blueprint $table) { - $table->integer('votes'); - }); +Schema::table('users', function (Blueprint $table) { + $table->integer('votes'); +}); +``` ### Renaming / Dropping Tables To rename an existing database table, use the `rename` method: - use Illuminate\Support\Facades\Schema; +```php +use Illuminate\Support\Facades\Schema; - Schema::rename($from, $to); +Schema::rename($from, $to); +``` To drop an existing table, you may use the `drop` or `dropIfExists` methods: - Schema::drop('users'); +```php +Schema::drop('users'); - Schema::dropIfExists('users'); +Schema::dropIfExists('users'); +``` #### Renaming Tables With Foreign Keys @@ -363,12 +387,14 @@ Before renaming a table, you should verify that any foreign key constraints on t The `table` method on the `Schema` facade may be used to update existing tables. Like the `create` method, the `table` method accepts two arguments: the name of the table and a closure that receives an `Illuminate\Database\Schema\Blueprint` instance you may use to add columns to the table: - use Illuminate\Database\Schema\Blueprint; - use Illuminate\Support\Facades\Schema; +```php +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; - Schema::table('users', function (Blueprint $table) { - $table->integer('votes'); - }); +Schema::table('users', function (Blueprint $table) { + $table->integer('votes'); +}); +``` ### Available Column Types @@ -540,125 +566,161 @@ The schema builder blueprint offers a variety of methods that correspond to the The `bigIncrements` method creates an auto-incrementing `UNSIGNED BIGINT` (primary key) equivalent column: - $table->bigIncrements('id'); +```php +$table->bigIncrements('id'); +``` #### `bigInteger()` {.collection-method} The `bigInteger` method creates a `BIGINT` equivalent column: - $table->bigInteger('votes'); +```php +$table->bigInteger('votes'); +``` #### `binary()` {.collection-method} The `binary` method creates a `BLOB` equivalent column: - $table->binary('photo'); +```php +$table->binary('photo'); +``` When utilizing MySQL, MariaDB, or SQL Server, you may pass `length` and `fixed` arguments to create `VARBINARY` or `BINARY` equivalent column: - $table->binary('data', length: 16); // VARBINARY(16) +```php +$table->binary('data', length: 16); // VARBINARY(16) - $table->binary('data', length: 16, fixed: true); // BINARY(16) +$table->binary('data', length: 16, fixed: true); // BINARY(16) +``` #### `boolean()` {.collection-method} The `boolean` method creates a `BOOLEAN` equivalent column: - $table->boolean('confirmed'); +```php +$table->boolean('confirmed'); +``` #### `char()` {.collection-method} The `char` method creates a `CHAR` equivalent column with of a given length: - $table->char('name', length: 100); +```php +$table->char('name', length: 100); +``` #### `dateTimeTz()` {.collection-method} The `dateTimeTz` method creates a `DATETIME` (with timezone) equivalent column with an optional fractional seconds precision: - $table->dateTimeTz('created_at', precision: 0); +```php +$table->dateTimeTz('created_at', precision: 0); +``` #### `dateTime()` {.collection-method} The `dateTime` method creates a `DATETIME` equivalent column with an optional fractional seconds precision: - $table->dateTime('created_at', precision: 0); +```php +$table->dateTime('created_at', precision: 0); +``` #### `date()` {.collection-method} The `date` method creates a `DATE` equivalent column: - $table->date('created_at'); +```php +$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', total: 8, places: 2); +```php +$table->decimal('amount', total: 8, places: 2); +``` #### `double()` {.collection-method} The `double` method creates a `DOUBLE` equivalent column: - $table->double('amount'); +```php +$table->double('amount'); +``` #### `enum()` {.collection-method} The `enum` method creates a `ENUM` equivalent column with the given valid values: - $table->enum('difficulty', ['easy', 'hard']); +```php +$table->enum('difficulty', ['easy', 'hard']); +``` #### `float()` {.collection-method} The `float` method creates a `FLOAT` equivalent column with the given precision: - $table->float('amount', precision: 53); +```php +$table->float('amount', precision: 53); +``` #### `foreignId()` {.collection-method} The `foreignId` method creates an `UNSIGNED BIGINT` equivalent column: - $table->foreignId('user_id'); +```php +$table->foreignId('user_id'); +``` #### `foreignIdFor()` {.collection-method} The `foreignIdFor` method adds a `{column}_id` equivalent column for a given model class. The column type will be `UNSIGNED BIGINT`, `CHAR(36)`, or `CHAR(26)` depending on the model key type: - $table->foreignIdFor(User::class); +```php +$table->foreignIdFor(User::class); +``` #### `foreignUlid()` {.collection-method} The `foreignUlid` method creates a `ULID` equivalent column: - $table->foreignUlid('user_id'); +```php +$table->foreignUlid('user_id'); +``` #### `foreignUuid()` {.collection-method} The `foreignUuid` method creates a `UUID` equivalent column: - $table->foreignUuid('user_id'); +```php +$table->foreignUuid('user_id'); +``` #### `geography()` {.collection-method} The `geography` method creates a `GEOGRAPHY` equivalent column with the given spatial type and SRID (Spatial Reference System Identifier): - $table->geography('coordinates', subtype: 'point', srid: 4326); +```php +$table->geography('coordinates', subtype: 'point', srid: 4326); +``` > [!NOTE] > Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the [PostGIS](https://postgis.net) extension before the `geography` method may be used. @@ -668,7 +730,9 @@ The `geography` method creates a `GEOGRAPHY` equivalent column with the given sp The `geometry` method creates a `GEOMETRY` equivalent column with the given spatial type and SRID (Spatial Reference System Identifier): - $table->geometry('positions', subtype: 'point', srid: 0); +```php +$table->geometry('positions', subtype: 'point', srid: 0); +``` > [!NOTE] > Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the [PostGIS](https://postgis.net) extension before the `geometry` method may be used. @@ -678,28 +742,36 @@ The `geometry` method creates a `GEOMETRY` equivalent column with the given spat 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(); +```php +$table->id(); +``` #### `increments()` {.collection-method} The `increments` method creates an auto-incrementing `UNSIGNED INTEGER` equivalent column as a primary key: - $table->increments('id'); +```php +$table->increments('id'); +``` #### `integer()` {.collection-method} The `integer` method creates an `INTEGER` equivalent column: - $table->integer('votes'); +```php +$table->integer('votes'); +``` #### `ipAddress()` {.collection-method} The `ipAddress` method creates a `VARCHAR` equivalent column: - $table->ipAddress('visitor'); +```php +$table->ipAddress('visitor'); +``` When using PostgreSQL, an `INET` column will be created. @@ -708,7 +780,9 @@ When using PostgreSQL, an `INET` column will be created. The `json` method creates a `JSON` equivalent column: - $table->json('options'); +```php +$table->json('options'); +``` When using SQLite, a `TEXT` column will be created. @@ -717,7 +791,9 @@ When using SQLite, a `TEXT` column will be created. The `jsonb` method creates a `JSONB` equivalent column: - $table->jsonb('options'); +```php +$table->jsonb('options'); +``` When using SQLite, a `TEXT` column will be created. @@ -726,43 +802,57 @@ When using SQLite, a `TEXT` column will be created. The `longText` method creates a `LONGTEXT` equivalent column: - $table->longText('description'); +```php +$table->longText('description'); +``` When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `LONGBLOB` equivalent column: - $table->longText('data')->charset('binary'); // LONGBLOB +```php +$table->longText('data')->charset('binary'); // LONGBLOB +``` #### `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'); +```php +$table->macAddress('device'); +``` #### `mediumIncrements()` {.collection-method} The `mediumIncrements` method creates an auto-incrementing `UNSIGNED MEDIUMINT` equivalent column as a primary key: - $table->mediumIncrements('id'); +```php +$table->mediumIncrements('id'); +``` #### `mediumInteger()` {.collection-method} The `mediumInteger` method creates a `MEDIUMINT` equivalent column: - $table->mediumInteger('votes'); +```php +$table->mediumInteger('votes'); +``` #### `mediumText()` {.collection-method} The `mediumText` method creates a `MEDIUMTEXT` equivalent column: - $table->mediumText('description'); +```php +$table->mediumText('description'); +``` When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `MEDIUMBLOB` equivalent column: - $table->mediumText('data')->charset('binary'); // MEDIUMBLOB +```php +$table->mediumText('data')->charset('binary'); // MEDIUMBLOB +``` #### `morphs()` {.collection-method} @@ -771,190 +861,246 @@ The `morphs` method is a convenience method that adds a `{column}_id` equivalent 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'); +```php +$table->morphs('taggable'); +``` #### `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'); +```php +$table->nullableMorphs('taggable'); +``` #### `nullableUlidMorphs()` {.collection-method} The method is similar to the [ulidMorphs](#column-method-ulidMorphs) method; however, the columns that are created will be "nullable": - $table->nullableUlidMorphs('taggable'); +```php +$table->nullableUlidMorphs('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'); +```php +$table->nullableUuidMorphs('taggable'); +``` #### `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(); +```php +$table->rememberToken(); +``` #### `set()` {.collection-method} The `set` method creates a `SET` equivalent column with the given list of valid values: - $table->set('flavors', ['strawberry', 'vanilla']); +```php +$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'); +```php +$table->smallIncrements('id'); +``` #### `smallInteger()` {.collection-method} The `smallInteger` method creates a `SMALLINT` equivalent column: - $table->smallInteger('votes'); +```php +$table->smallInteger('votes'); +``` #### `softDeletesTz()` {.collection-method} The `softDeletesTz` method adds a nullable `deleted_at` `TIMESTAMP` (with timezone) equivalent column with an optional fractional seconds precision. This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality: - $table->softDeletesTz('deleted_at', precision: 0); +```php +$table->softDeletesTz('deleted_at', precision: 0); +``` #### `softDeletes()` {.collection-method} The `softDeletes` method adds a nullable `deleted_at` `TIMESTAMP` equivalent column with an optional fractional seconds precision. This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality: - $table->softDeletes('deleted_at', precision: 0); +```php +$table->softDeletes('deleted_at', precision: 0); +``` #### `string()` {.collection-method} The `string` method creates a `VARCHAR` equivalent column of the given length: - $table->string('name', length: 100); +```php +$table->string('name', length: 100); +``` #### `text()` {.collection-method} The `text` method creates a `TEXT` equivalent column: - $table->text('description'); +```php +$table->text('description'); +``` When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `BLOB` equivalent column: - $table->text('data')->charset('binary'); // BLOB +```php +$table->text('data')->charset('binary'); // BLOB +``` #### `timeTz()` {.collection-method} The `timeTz` method creates a `TIME` (with timezone) equivalent column with an optional fractional seconds precision: - $table->timeTz('sunrise', precision: 0); +```php +$table->timeTz('sunrise', precision: 0); +``` #### `time()` {.collection-method} The `time` method creates a `TIME` equivalent column with an optional fractional seconds precision: - $table->time('sunrise', precision: 0); +```php +$table->time('sunrise', precision: 0); +``` #### `timestampTz()` {.collection-method} The `timestampTz` method creates a `TIMESTAMP` (with timezone) equivalent column with an optional fractional seconds precision: - $table->timestampTz('added_at', precision: 0); +```php +$table->timestampTz('added_at', precision: 0); +``` #### `timestamp()` {.collection-method} The `timestamp` method creates a `TIMESTAMP` equivalent column with an optional fractional seconds precision: - $table->timestamp('added_at', precision: 0); +```php +$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 fractional seconds precision: - $table->timestampsTz(precision: 0); +```php +$table->timestampsTz(precision: 0); +``` #### `timestamps()` {.collection-method} The `timestamps` method creates `created_at` and `updated_at` `TIMESTAMP` equivalent columns with an optional fractional seconds precision: - $table->timestamps(precision: 0); +```php +$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'); +```php +$table->tinyIncrements('id'); +``` #### `tinyInteger()` {.collection-method} The `tinyInteger` method creates a `TINYINT` equivalent column: - $table->tinyInteger('votes'); +```php +$table->tinyInteger('votes'); +``` #### `tinyText()` {.collection-method} The `tinyText` method creates a `TINYTEXT` equivalent column: - $table->tinyText('notes'); +```php +$table->tinyText('notes'); +``` When utilizing MySQL or MariaDB, you may apply a `binary` character set to the column in order to create a `TINYBLOB` equivalent column: - $table->tinyText('data')->charset('binary'); // TINYBLOB +```php +$table->tinyText('data')->charset('binary'); // TINYBLOB +``` #### `unsignedBigInteger()` {.collection-method} The `unsignedBigInteger` method creates an `UNSIGNED BIGINT` equivalent column: - $table->unsignedBigInteger('votes'); +```php +$table->unsignedBigInteger('votes'); +``` #### `unsignedInteger()` {.collection-method} The `unsignedInteger` method creates an `UNSIGNED INTEGER` equivalent column: - $table->unsignedInteger('votes'); +```php +$table->unsignedInteger('votes'); +``` #### `unsignedMediumInteger()` {.collection-method} The `unsignedMediumInteger` method creates an `UNSIGNED MEDIUMINT` equivalent column: - $table->unsignedMediumInteger('votes'); +```php +$table->unsignedMediumInteger('votes'); +``` #### `unsignedSmallInteger()` {.collection-method} The `unsignedSmallInteger` method creates an `UNSIGNED SMALLINT` equivalent column: - $table->unsignedSmallInteger('votes'); +```php +$table->unsignedSmallInteger('votes'); +``` #### `unsignedTinyInteger()` {.collection-method} The `unsignedTinyInteger` method creates an `UNSIGNED TINYINT` equivalent column: - $table->unsignedTinyInteger('votes'); +```php +$table->unsignedTinyInteger('votes'); +``` #### `ulidMorphs()` {.collection-method} @@ -963,7 +1109,9 @@ The `ulidMorphs` method is a convenience method that adds a `{column}_id` `CHAR( This method is intended to be used when defining the columns necessary for a polymorphic [Eloquent relationship](/docs/{{version}}/eloquent-relationships) that use ULID identifiers. In the following example, `taggable_id` and `taggable_type` columns would be created: - $table->ulidMorphs('taggable'); +```php +$table->ulidMorphs('taggable'); +``` #### `uuidMorphs()` {.collection-method} @@ -972,47 +1120,59 @@ The `uuidMorphs` method is a convenience method that adds a `{column}_id` `CHAR( 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'); +```php +$table->uuidMorphs('taggable'); +``` #### `ulid()` {.collection-method} The `ulid` method creates a `ULID` equivalent column: - $table->ulid('id'); +```php +$table->ulid('id'); +``` #### `uuid()` {.collection-method} The `uuid` method creates a `UUID` equivalent column: - $table->uuid('id'); +```php +$table->uuid('id'); +``` #### `vector()` {.collection-method} The `vector` method creates a `vector` equivalent column: - $table->vector('embedding', dimensions: 100); +```php +$table->vector('embedding', dimensions: 100); +``` #### `year()` {.collection-method} The `year` method creates a `YEAR` equivalent column: - $table->year('birth_year'); +```php +$table->year('birth_year'); +``` ### Column Modifiers 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; +```php +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; - Schema::table('users', function (Blueprint $table) { - $table->string('email')->nullable(); - }); +Schema::table('users', function (Blueprint $table) { + $table->string('email')->nullable(); +}); +``` The following table contains all of the available column modifiers. This list does not include [index modifiers](#creating-indexes): @@ -1045,27 +1205,29 @@ The following table contains all of the available column modifiers. This list do 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: - id(); - $table->json('movies')->default(new Expression('(JSON_ARRAY())')); - $table->timestamps(); - }); - } - }; + Schema::create('flights', function (Blueprint $table) { + $table->id(); + $table->json('movies')->default(new Expression('(JSON_ARRAY())')); + $table->timestamps(); + }); + } +}; +``` > [!WARNING] > Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. @@ -1075,26 +1237,32 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi When using the MariaDB or MySQL database, the `after` method may be used to add columns after an existing column in the schema: - $table->after('password', function (Blueprint $table) { - $table->string('address_line1'); - $table->string('address_line2'); - $table->string('city'); - }); +```php +$table->after('password', function (Blueprint $table) { + $table->string('address_line1'); + $table->string('address_line2'); + $table->string('city'); +}); +``` ### Modifying Columns 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(); - }); +```php +Schema::table('users', function (Blueprint $table) { + $table->string('name', 50)->change(); +}); +``` When modifying a column, you must explicitly include all the modifiers you want to keep on the column definition - any missing attribute will be dropped. For example, to retain the `unsigned`, `default`, and `comment` attributes, you must call each modifier explicitly when changing the column: - Schema::table('users', function (Blueprint $table) { - $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); - }); +```php +Schema::table('users', function (Blueprint $table) { + $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change(); +}); +``` The `change` method does not change the indexes of the column. Therefore, you may use index modifiers to explicitly add or drop an index when modifying the column: @@ -1111,24 +1279,30 @@ $table->char('postal_code', 10)->unique(false)->change(); To rename a column, you may use the `renameColumn` method provided by the schema builder: - Schema::table('users', function (Blueprint $table) { - $table->renameColumn('from', 'to'); - }); +```php +Schema::table('users', function (Blueprint $table) { + $table->renameColumn('from', 'to'); +}); +``` ### Dropping Columns To drop a column, you may use the `dropColumn` method on the schema builder: - Schema::table('users', function (Blueprint $table) { - $table->dropColumn('votes'); - }); +```php +Schema::table('users', function (Blueprint $table) { + $table->dropColumn('votes'); +}); +``` You may drop multiple columns from a table by passing an array of column names to the `dropColumn` method: - Schema::table('users', function (Blueprint $table) { - $table->dropColumn(['votes', 'avatar', 'location']); - }); +```php +Schema::table('users', function (Blueprint $table) { + $table->dropColumn(['votes', 'avatar', 'location']); +}); +``` #### Available Command Aliases @@ -1156,24 +1330,32 @@ Laravel provides several convenient methods related to dropping common types of 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: - use Illuminate\Database\Schema\Blueprint; - use Illuminate\Support\Facades\Schema; +```php +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; - Schema::table('users', function (Blueprint $table) { - $table->string('email')->unique(); - }); +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 method accepts the name of the column that should receive a unique index: - $table->unique('email'); +```php +$table->unique('email'); +``` You may even pass an array of columns to an index method to create a compound (or composite) index: - $table->index(['account_id', 'created_at']); +```php +$table->index(['account_id', 'created_at']); +``` 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'); +```php +$table->unique('email', 'unique_email'); +``` #### Available Index Types @@ -1199,7 +1381,9 @@ Laravel's schema builder blueprint class provides methods for creating each type 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') +```php +$table->renameIndex('from', 'to') +``` ### Dropping Indexes @@ -1220,44 +1404,54 @@ To drop an index, you must specify the index's name. By default, Laravel automat 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' - }); +```php +Schema::table('geo', function (Blueprint $table) { + $table->dropIndex(['state']); // Drops index 'geo_state_index' +}); +``` ### Foreign Key Constraints 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; +```php +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; - Schema::table('posts', function (Blueprint $table) { - $table->unsignedBigInteger('user_id'); +Schema::table('posts', function (Blueprint $table) { + $table->unsignedBigInteger('user_id'); - $table->foreign('user_id')->references('id')->on('users'); - }); + $table->foreign('user_id')->references('id')->on('users'); +}); +``` Since this syntax is rather verbose, Laravel provides additional, terser methods that use conventions to provide a better developer experience. When using the `foreignId` method to create your column, the example above can be rewritten like so: - Schema::table('posts', function (Blueprint $table) { - $table->foreignId('user_id')->constrained(); - }); +```php +Schema::table('posts', function (Blueprint $table) { + $table->foreignId('user_id')->constrained(); +}); +``` The `foreignId` method creates an `UNSIGNED BIGINT` equivalent column, while the `constrained` method will use conventions to determine the table and column being referenced. If your table name does not match Laravel's conventions, you may manually provide it to the `constrained` method. In addition, the name that should be assigned to the generated index may be specified as well: - Schema::table('posts', function (Blueprint $table) { - $table->foreignId('user_id')->constrained( - table: 'users', indexName: 'posts_user_id' - ); - }); +```php +Schema::table('posts', function (Blueprint $table) { + $table->foreignId('user_id')->constrained( + table: 'users', indexName: 'posts_user_id' + ); +}); +``` You may also specify the desired action for the "on delete" and "on update" properties of the constraint: - $table->foreignId('user_id') - ->constrained() - ->onUpdate('cascade') - ->onDelete('cascade'); +```php +$table->foreignId('user_id') + ->constrained() + ->onUpdate('cascade') + ->onDelete('cascade'); +``` An alternative, expressive syntax is also provided for these actions: @@ -1278,33 +1472,41 @@ An alternative, expressive syntax is also provided for these actions: Any additional [column modifiers](#column-modifiers) must be called before the `constrained` method: - $table->foreignId('user_id') - ->nullable() - ->constrained(); +```php +$table->foreignId('user_id') + ->nullable() + ->constrained(); +``` #### 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'); +```php +$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 converted to a foreign key constraint name using Laravel's constraint naming conventions: - $table->dropForeign(['user_id']); +```php +$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(); +```php +Schema::enableForeignKeyConstraints(); - Schema::disableForeignKeyConstraints(); +Schema::disableForeignKeyConstraints(); - Schema::withoutForeignKeyConstraints(function () { - // Constraints disabled within this closure... - }); +Schema::withoutForeignKeyConstraints(function () { + // Constraints disabled within this closure... +}); +``` > [!WARNING] > SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. diff --git a/mocking.md b/mocking.md index 28d8bff165b..dd9673b91dd 100644 --- a/mocking.md +++ b/mocking.md @@ -51,57 +51,65 @@ public function test_something_can_be_mocked(): void 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; - use Mockery\MockInterface; +```php +use App\Service; +use Mockery\MockInterface; - $mock = $this->mock(Service::class, function (MockInterface $mock) { - $mock->shouldReceive('process')->once(); - }); +$mock = $this->mock(Service::class, function (MockInterface $mock) { + $mock->shouldReceive('process')->once(); +}); +``` You may use the `partialMock` method when you only need to mock a few methods of an object. The methods that are not mocked will be executed normally when called: - use App\Service; - use Mockery\MockInterface; +```php +use App\Service; +use Mockery\MockInterface; - $mock = $this->partialMock(Service::class, function (MockInterface $mock) { - $mock->shouldReceive('process')->once(); - }); +$mock = $this->partialMock(Service::class, function (MockInterface $mock) { + $mock->shouldReceive('process')->once(); +}); +``` 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. Spies are similar to mocks; however, spies record any interaction between the spy and the code being tested, allowing you to make assertions after the code is executed: - use App\Service; +```php +use App\Service; - $spy = $this->spy(Service::class); +$spy = $this->spy(Service::class); - // ... +// ... - $spy->shouldHaveReceived('process'); +$spy->shouldHaveReceived('process'); +``` ## Mocking Facades Unlike traditional static method calls, [facades](/docs/{{version}}/facades) (including [real-time facades](/docs/{{version}}/facades#real-time-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: - travel(5)->days(function () { - // Test something five days into the future... - }); +```php +$this->travel(5)->days(function () { + // Test something five days into the future... +}); - $this->travelTo(now()->subDays(10), function () { - // Test something during a given moment... - }); +$this->travelTo(now()->subDays(10), function () { + // Test something during a given moment... +}); +``` The `freezeTime` method may be used to freeze the current time. Similarly, the `freezeSecond` method will freeze the current time but at the start of the current second: - use Illuminate\Support\Carbon; +```php +use Illuminate\Support\Carbon; - // Freeze time and resume normal time after executing closure... - $this->freezeTime(function (Carbon $time) { - // ... - }); +// Freeze time and resume normal time after executing closure... +$this->freezeTime(function (Carbon $time) { + // ... +}); - // Freeze time at the current second and resume normal time after executing closure... - $this->freezeSecond(function (Carbon $time) { - // ... - }) +// Freeze time at the current second and resume normal time after executing closure... +$this->freezeSecond(function (Carbon $time) { + // ... +}) +``` As you would expect, all of the methods discussed above are primarily useful for testing time sensitive application behavior, such as locking inactive posts on a discussion forum: diff --git a/notifications.md b/notifications.md index 58dc24b2b4f..fa9dc7292df 100644 --- a/notifications.md +++ b/notifications.md @@ -77,23 +77,27 @@ This command will place a fresh notification class in your `app/Notifications` d 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)); +$user->notify(new InvoicePaid($invoice)); +``` > [!NOTE] > Remember, you may use the `Notifiable` trait on any of your models. You are not limited to only including it on your `User` model. @@ -103,13 +107,17 @@ The `notify` method that is provided by this trait expects to receive a notifica 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; +```php +use Illuminate\Support\Facades\Notification; - Notification::send($users, new InvoicePaid($invoice)); +Notification::send($users, new InvoicePaid($invoice)); +``` You can also send notifications immediately using the `sendNow` method. This method will send the notification immediately even if the notification implements the `ShouldQueue` interface: - Notification::sendNow($developers, new DeploymentCompleted($deployment)); +```php +Notification::sendNow($developers, new DeploymentCompleted($deployment)); +``` ### Specifying Delivery Channels @@ -121,15 +129,17 @@ Every notification class has a `via` method that determines on which channels th The `via` method receives a `$notifiable` instance, which will be an instance of the class to which the notification is being sent. You may use `$notifiable` to determine which channels the notification should be delivered on: - /** - * Get the notification's delivery channels. - * - * @return array - */ - public function via(object $notifiable): array - { - return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database']; - } +```php +/** + * Get the notification's delivery channels. + * + * @return array + */ +public function via(object $notifiable): array +{ + return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database']; +} +``` ### Queueing Notifications @@ -139,24 +149,28 @@ The `via` method receives a `$notifiable` instance, which will be an instance of 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: - notify(new InvoicePaid($invoice)); +```php +$user->notify(new InvoicePaid($invoice)); +``` When queueing notifications, a queued job will be created for each recipient and channel combination. For example, six jobs will be dispatched to the queue if your notification has three recipients and two channels. @@ -165,111 +179,125 @@ When queueing notifications, a queued job will be created for each recipient and If you would like to delay the delivery of the notification, you may chain the `delay` method onto your notification instantiation: - $delay = now()->addMinutes(10); +```php +$delay = now()->addMinutes(10); - $user->notify((new InvoicePaid($invoice))->delay($delay)); +$user->notify((new InvoicePaid($invoice))->delay($delay)); +``` You may pass an array to the `delay` method to specify the delay amount for specific channels: - $user->notify((new InvoicePaid($invoice))->delay([ - 'mail' => now()->addMinutes(5), - 'sms' => now()->addMinutes(10), - ])); +```php +$user->notify((new InvoicePaid($invoice))->delay([ + 'mail' => now()->addMinutes(5), + 'sms' => now()->addMinutes(10), +])); +``` Alternatively, you may define a `withDelay` method on the notification class itself. The `withDelay` method should return an array of channel names and delay values: - /** - * Determine the notification's delivery delay. - * - * @return array - */ - public function withDelay(object $notifiable): array - { - return [ - 'mail' => now()->addMinutes(5), - 'sms' => now()->addMinutes(10), - ]; - } +```php +/** + * Determine the notification's delivery delay. + * + * @return array + */ +public function withDelay(object $notifiable): array +{ + return [ + 'mail' => now()->addMinutes(5), + 'sms' => now()->addMinutes(10), + ]; +} +``` #### 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 call the `onConnection` method from your notification's constructor: - onConnection('redis'); - } - } +use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Notifications\Notification; -Or, if you would like to specify a specific queue connection that should be used for each notification channel supported by the notification, you may define a `viaConnections` method on your notification. This method should return an array of channel name / queue connection name pairs: +class InvoicePaid extends Notification implements ShouldQueue +{ + use Queueable; /** - * Determine which connections should be used for each notification channel. - * - * @return array + * Create a new notification instance. */ - public function viaConnections(): array + public function __construct() { - return [ - 'mail' => 'redis', - 'database' => 'sync', - ]; + $this->onConnection('redis'); } +} +``` + +Or, if you would like to specify a specific queue connection that should be used for each notification channel supported by the notification, you may define a `viaConnections` method on your notification. This method should return an array of channel name / queue connection name pairs: + +```php +/** + * Determine which connections should be used for each notification channel. + * + * @return array + */ +public function viaConnections(): array +{ + return [ + 'mail' => 'redis', + 'database' => 'sync', + ]; +} +``` #### Customizing Notification Channel Queues If you would like to specify a specific queue that should be used for each notification channel supported by the notification, you may define a `viaQueues` method on your notification. This method should return an array of channel name / queue name pairs: - /** - * Determine which queues should be used for each notification channel. - * - * @return array - */ - public function viaQueues(): array - { - return [ - 'mail' => 'mail-queue', - 'slack' => 'slack-queue', - ]; - } +```php +/** + * Determine which queues should be used for each notification channel. + * + * @return array + */ +public function viaQueues(): array +{ + return [ + 'mail' => 'mail-queue', + 'slack' => 'slack-queue', + ]; +} +``` #### Queued Notification Middleware Queued notifications may define middleware [just like queued jobs](/docs/{{version}}/queues#job-middleware). To get started, define a `middleware` method on your notification class. The `middleware` method will receive `$notifiable` and `$channel` variables, which allow you to customize the returned middleware based on the notification's destination: - use Illuminate\Queue\Middleware\RateLimited; +```php +use Illuminate\Queue\Middleware\RateLimited; - /** - * Get the middleware the notification job should pass through. - * - * @return array - */ - public function middleware(object $notifiable, string $channel) - { - return match ($channel) { - 'email' => [new RateLimited('postmark')], - 'slack' => [new RateLimited('slack')], - default => [], - }; - } +/** + * Get the middleware the notification job should pass through. + * + * @return array + */ +public function middleware(object $notifiable, string $channel) +{ + return match ($channel) { + 'email' => [new RateLimited('postmark')], + 'slack' => [new RateLimited('slack')], + default => [], + }; +} +``` #### Queued Notifications and Database Transactions @@ -278,32 +306,36 @@ When queued notifications are dispatched within database transactions, they may If your queue connection's `after_commit` configuration option is set to `false`, you may still indicate that a particular queued notification should be dispatched after all open database transactions have been committed by calling the `afterCommit` method when sending the notification: - use App\Notifications\InvoicePaid; +```php +use App\Notifications\InvoicePaid; - $user->notify((new InvoicePaid($invoice))->afterCommit()); +$user->notify((new InvoicePaid($invoice))->afterCommit()); +``` Alternatively, you may call the `afterCommit` method from your notification's constructor: - afterCommit(); - } + /** + * Create a new notification instance. + */ + public function __construct() + { + $this->afterCommit(); } +} +``` > [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -315,40 +347,48 @@ After a queued notification has been dispatched for the queue for background pro However, if you would like to make the final determination on whether the queued notification should be sent after it is being processed by a queue worker, you may define a `shouldSend` method on the notification class. If this method returns `false`, the notification will not be sent: - /** - * Determine if the notification should be sent. - */ - public function shouldSend(object $notifiable, string $channel): bool - { - return $this->invoice->isPaid(); - } +```php +/** + * Determine if the notification should be sent. + */ +public function shouldSend(object $notifiable, string $channel): bool +{ + return $this->invoice->isPaid(); +} +``` ### 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` facade's `route` method, you may specify ad-hoc notification routing information before sending the notification: - use Illuminate\Broadcasting\Channel; - use Illuminate\Support\Facades\Notification; +```php +use Illuminate\Broadcasting\Channel; +use Illuminate\Support\Facades\Notification; - Notification::route('mail', 'taylor@example.com') - ->route('vonage', '5555555555') - ->route('slack', '#slack-channel') - ->route('broadcast', [new Channel('channel-name')]) - ->notify(new InvoicePaid($invoice)); +Notification::route('mail', 'taylor@example.com') + ->route('vonage', '5555555555') + ->route('slack', '#slack-channel') + ->route('broadcast', [new Channel('channel-name')]) + ->notify(new InvoicePaid($invoice)); +``` If you would like to provide the recipient's name when sending an on-demand notification to the `mail` route, you may provide an array that contains the email address as the key and the name as the value of the first element in the array: - Notification::route('mail', [ - 'barrett@example.com' => 'Barrett Blair', - ])->notify(new InvoicePaid($invoice)); +```php +Notification::route('mail', [ + 'barrett@example.com' => 'Barrett Blair', +])->notify(new InvoicePaid($invoice)); +``` Using the `routes` method, you may provide ad-hoc routing information for multiple notification channels at once: - Notification::routes([ - 'mail' => ['barrett@example.com' => 'Barrett Blair'], - 'vonage' => '5555555555', - ])->notify(new InvoicePaid($invoice)); +```php +Notification::routes([ + 'mail' => ['barrett@example.com' => 'Barrett Blair'], + 'vonage' => '5555555555', +])->notify(new InvoicePaid($invoice)); +``` ## Mail Notifications @@ -360,20 +400,22 @@ If a notification supports being sent as an email, you should define a `toMail` 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. - */ - public function toMail(object $notifiable): MailMessage - { - $url = url('https://github.com/invoice/'.$this-%3Einvoice-%3Eid); - - return (new MailMessage) - ->greeting('Hello!') - ->line('One of your invoices has been paid!') - ->lineIf($this->amount > 0, "Amount paid: {$this->amount}") - ->action('View Invoice', $url) - ->line('Thank you for using our application!'); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + $url = url('https://github.com/invoice/'.$this-%3Einvoice-%3Eid); + + return (new MailMessage) + ->greeting('Hello!') + ->line('One of your invoices has been paid!') + ->lineIf($this->amount > 0, "Amount paid: {$this->amount}") + ->action('View Invoice', $url) + ->line('Thank you for using our application!'); +} +``` > [!NOTE] > 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. @@ -390,133 +432,149 @@ In this example, we register a greeting, a line of text, a call to action, and t 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. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->error() - ->subject('Invoice Payment Failed') - ->line('...'); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->error() + ->subject('Invoice Payment Failed') + ->line('...'); +} +``` #### 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: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage)->view( - 'mail.invoice.paid', ['invoice' => $this->invoice] - ); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage)->view( + 'mail.invoice.paid', ['invoice' => $this->invoice] + ); +} +``` 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. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage)->view( - ['mail.invoice.paid', 'mail.invoice.paid-text'], - ['invoice' => $this->invoice] - ); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage)->view( + ['mail.invoice.paid', 'mail.invoice.paid-text'], + ['invoice' => $this->invoice] + ); +} +``` Or, if your message only has a plain-text view, you may utilize the `text` method: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage)->text( - 'mail.invoice.paid-text', ['invoice' => $this->invoice] - ); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage)->text( + 'mail.invoice.paid-text', ['invoice' => $this->invoice] + ); +} +``` ### Customizing the Sender By default, the email's sender / from address is defined in the `config/mail.php` configuration file. However, you may specify the from address for a specific notification using the `from` method: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->from('barrett@example.com', 'Barrett Blair') - ->line('...'); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->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 notifiable entity: - |string + */ + public function routeNotificationForMail(Notification $notification): array|string { - use Notifiable; - - /** - * Route notifications for the mail channel. - * - * @return array|string - */ - public function routeNotificationForMail(Notification $notification): array|string - { - // Return email address only... - return $this->email_address; + // Return email address only... + return $this->email_address; - // Return email address and name... - return [$this->email_address => $this->name]; - } + // Return email address and name... + return [$this->email_address => $this->name]; } +} +``` ### 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 a different subject for the message, you may call the `subject` method when building your message: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->subject('Notification Subject') - ->line('...'); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->subject('Notification Subject') + ->line('...'); +} +``` ### Customizing the Mailer 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. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->mailer('postmark') - ->line('...'); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->mailer('postmark') + ->line('...'); +} +``` ### Customizing the Templates @@ -532,98 +590,110 @@ php artisan vendor:publish --tag=laravel-notifications 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. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->greeting('Hello!') - ->attach('/path/to/file'); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->greeting('Hello!') + ->attach('/path/to/file'); +} +``` > [!NOTE] > The `attach` method offered by notification mail messages also accepts [attachable objects](/docs/{{version}}/mail#attachable-objects). Please consult the comprehensive [attachable object documentation](/docs/{{version}}/mail#attachable-objects) to learn more. When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->greeting('Hello!') - ->attach('/path/to/file', [ - 'as' => 'name.pdf', - 'mime' => 'application/pdf', - ]); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->greeting('Hello!') + ->attach('/path/to/file', [ + 'as' => 'name.pdf', + 'mime' => 'application/pdf', + ]); +} +``` 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; +```php +use App\Mail\InvoicePaid as InvoicePaidMailable; - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): Mailable - { - return (new InvoicePaidMailable($this->invoice)) - ->to($notifiable->email) - ->attachFromStorage('/path/to/file'); - } +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): Mailable +{ + return (new InvoicePaidMailable($this->invoice)) + ->to($notifiable->email) + ->attachFromStorage('/path/to/file'); +} +``` When necessary, multiple files may be attached to a message using the `attachMany` method: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->greeting('Hello!') - ->attachMany([ - '/path/to/forge.svg', - '/path/to/vapor.svg' => [ - 'as' => 'Logo.svg', - 'mime' => 'image/svg+xml', - ], - ]); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->greeting('Hello!') + ->attachMany([ + '/path/to/forge.svg', + '/path/to/vapor.svg' => [ + 'as' => 'Logo.svg', + 'mime' => 'image/svg+xml', + ], + ]); +} +``` #### Raw Data Attachments 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. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->greeting('Hello!') - ->attachData($this->pdf, 'name.pdf', [ - 'mime' => 'application/pdf', - ]); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->greeting('Hello!') + ->attachData($this->pdf, 'name.pdf', [ + 'mime' => 'application/pdf', + ]); +} +``` ### Adding Tags and Metadata Some third-party email providers such as Mailgun and Postmark support message "tags" and "metadata", which may be used to group and track emails sent by your application. You may add tags and metadata to an email message via the `tag` and `metadata` methods: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->greeting('Comment Upvoted!') - ->tag('upvote') - ->metadata('comment_id', $this->comment->id); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->greeting('Comment Upvoted!') + ->tag('upvote') + ->metadata('comment_id', $this->comment->id); +} +``` If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) and [metadata](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). @@ -634,74 +704,82 @@ If your application is using Amazon SES to send emails, you should use the `meta The `withSymfonyMessage` method of the `MailMessage` class allows you to register a closure which will be invoked with the Symfony Message instance before sending the message. This gives you an opportunity to deeply customize the message before it is delivered: - use Symfony\Component\Mime\Email; +```php +use Symfony\Component\Mime\Email; - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->withSymfonyMessage(function (Email $message) { - $message->getHeaders()->addTextHeader( - 'Custom-Header', 'Header Value' - ); - }); - } +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->withSymfonyMessage(function (Email $message) { + $message->getHeaders()->addTextHeader( + 'Custom-Header', 'Header Value' + ); + }); +} +``` ### Using Mailables If needed, you may return a full [mailable object](/docs/{{version}}/mail) from your notification's `toMail` method. When returning a `Mailable` instead of a `MailMessage`, you will need to specify the message recipient using the mailable object's `to` method: - use App\Mail\InvoicePaid as InvoicePaidMailable; - use Illuminate\Mail\Mailable; +```php +use App\Mail\InvoicePaid as InvoicePaidMailable; +use Illuminate\Mail\Mailable; - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): Mailable - { - return (new InvoicePaidMailable($this->invoice)) - ->to($notifiable->email); - } +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): Mailable +{ + return (new InvoicePaidMailable($this->invoice)) + ->to($notifiable->email); +} +``` #### Mailables and On-Demand Notifications If you are sending an [on-demand notification](#on-demand-notifications), the `$notifiable` instance given to the `toMail` method will be an instance of `Illuminate\Notifications\AnonymousNotifiable`, which offers a `routeNotificationFor` method that may be used to retrieve the email address the on-demand notification should be sent to: - use App\Mail\InvoicePaid as InvoicePaidMailable; - use Illuminate\Notifications\AnonymousNotifiable; - use Illuminate\Mail\Mailable; +```php +use App\Mail\InvoicePaid as InvoicePaidMailable; +use Illuminate\Notifications\AnonymousNotifiable; +use Illuminate\Mail\Mailable; - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): Mailable - { - $address = $notifiable instanceof AnonymousNotifiable - ? $notifiable->routeNotificationFor('mail') - : $notifiable->email; +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): Mailable +{ + $address = $notifiable instanceof AnonymousNotifiable + ? $notifiable->routeNotificationFor('mail') + : $notifiable->email; - return (new InvoicePaidMailable($this->invoice)) - ->to($address); - } + return (new InvoicePaidMailable($this->invoice)) + ->to($address); +} +``` ### 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: - use App\Models\Invoice; - use App\Notifications\InvoicePaid; +```php +use App\Models\Invoice; +use App\Notifications\InvoicePaid; - Route::get('/notification', function () { - $invoice = Invoice::find(1); +Route::get('/notification', function () { + $invoice = Invoice::find(1); - return (new InvoicePaid($invoice)) - ->toMail($invoice->user); - }); + return (new InvoicePaid($invoice)) + ->toMail($invoice->user); +}); +``` ## Markdown Mail Notifications @@ -719,17 +797,19 @@ 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('https://github.com/invoice/'.$this-%3Einvoice-%3Eid); +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + $url = url('https://github.com/invoice/'.$this-%3Einvoice-%3Eid); - 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 @@ -807,16 +887,18 @@ If you would like to build an entirely new theme for Laravel's Markdown componen To customize the theme for an individual notification, you may call the `theme` method while building the notification's mail message. The `theme` method accepts the name of the theme that should be used when sending the notification: - /** - * Get the mail representation of the notification. - */ - public function toMail(object $notifiable): MailMessage - { - return (new MailMessage) - ->theme('invoice') - ->subject('Invoice Paid') - ->markdown('mail.invoice.paid', ['url' => $url]); - } +```php +/** + * Get the mail representation of the notification. + */ +public function toMail(object $notifiable): MailMessage +{ + return (new MailMessage) + ->theme('invoice') + ->subject('Invoice Paid') + ->markdown('mail.invoice.paid', ['url' => $url]); +} +``` ## Database Notifications @@ -842,30 +924,34 @@ php artisan migrate If a notification supports being stored in a database table, you should define a `toDatabase` or `toArray` method on the notification class. This method will receive a `$notifiable` entity and should return a plain PHP array. The returned array will be encoded as JSON and stored in the `data` column of your `notifications` table. Let's take a look at an example `toArray` method: - /** - * Get the array representation of the notification. - * - * @return array - */ - public function toArray(object $notifiable): array - { - return [ - 'invoice_id' => $this->invoice->id, - 'amount' => $this->invoice->amount, - ]; - } +```php +/** + * Get the array representation of the notification. + * + * @return array + */ +public function toArray(object $notifiable): array +{ + return [ + 'invoice_id' => $this->invoice->id, + 'amount' => $this->invoice->amount, + ]; +} +``` When the notification is stored in your application's database, the `type` column will be populated with the notification's class name. However, you may customize this behavior by defining a `databaseType` method on your notification class: - /** - * Get the notification's database type. - * - * @return string - */ - public function databaseType(object $notifiable): string - { - return 'invoice-paid'; - } +```php +/** + * Get the notification's database type. + * + * @return string + */ +public function databaseType(object $notifiable): string +{ + return 'invoice-paid'; +} +``` #### `toDatabase` vs. `toArray` @@ -877,19 +963,23 @@ The `toArray` method is also used by the `broadcast` channel to determine which 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); +```php +$user = App\Models\User::find(1); - foreach ($user->notifications as $notification) { - echo $notification->type; - } +foreach ($user->notifications as $notification) { + 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 with the most recent notifications at the beginning of the collection: - $user = App\Models\User::find(1); +```php +$user = App\Models\User::find(1); - foreach ($user->unreadNotifications as $notification) { - echo $notification->type; - } +foreach ($user->unreadNotifications as $notification) { + echo $notification->type; +} +``` > [!NOTE] > 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. @@ -899,25 +989,33 @@ If you want to retrieve only the "unread" notifications, you may use the `unread Typically, you will want to mark a notification as "read" when a user views it. The `Illuminate\Notifications\Notifiable` trait provides a `markAsRead` method, which updates the `read_at` column on the notification's database record: - $user = App\Models\User::find(1); +```php +$user = App\Models\User::find(1); - foreach ($user->unreadNotifications as $notification) { - $notification->markAsRead(); - } +foreach ($user->unreadNotifications as $notification) { + $notification->markAsRead(); +} +``` However, instead of looping through each notification, you may use the `markAsRead` method directly on a collection of notifications: - $user->unreadNotifications->markAsRead(); +```php +$user->unreadNotifications->markAsRead(); +``` You may also use a mass-update query to mark all of the notifications as read without retrieving them from the database: - $user = App\Models\User::find(1); +```php +$user = App\Models\User::find(1); - $user->unreadNotifications()->update(['read_at' => now()]); +$user->unreadNotifications()->update(['read_at' => now()]); +``` You may `delete` the notifications to remove them from the table entirely: - $user->notifications()->delete(); +```php +$user->notifications()->delete(); +``` ## Broadcast Notifications @@ -932,76 +1030,86 @@ Before broadcasting notifications, you should configure and be familiar with Lar 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; +```php +use Illuminate\Notifications\Messages\BroadcastMessage; - /** - * Get the broadcastable representation of the notification. - */ - public function toBroadcast(object $notifiable): BroadcastMessage - { - return new BroadcastMessage([ - 'invoice_id' => $this->invoice->id, - 'amount' => $this->invoice->amount, - ]); - } +/** + * Get the broadcastable representation of the notification. + */ +public function toBroadcast(object $notifiable): BroadcastMessage +{ + return new BroadcastMessage([ + 'invoice_id' => $this->invoice->id, + 'amount' => $this->invoice->amount, + ]); +} +``` #### Broadcast Queue Configuration All broadcast notifications are queued for broadcasting. If you would like to configure the queue connection or queue name that is used to queue the broadcast operation, you may use the `onConnection` and `onQueue` methods of the `BroadcastMessage`: - return (new BroadcastMessage($data)) - ->onConnection('sqs') - ->onQueue('broadcasts'); +```php +return (new BroadcastMessage($data)) + ->onConnection('sqs') + ->onQueue('broadcasts'); +``` #### 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`, you may define a `broadcastType` method on the notification class: - /** - * Get the type of the notification being broadcast. - */ - public function broadcastType(): string - { - return 'broadcast.message'; - } +```php +/** + * Get the type of the notification being broadcast. + */ +public function broadcastType(): string +{ + return 'broadcast.message'; +} +``` ### 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#client-side-installation), you may easily listen for notifications on a channel using the `notification` method: - Echo.private('App.Models.User.' + userId) - .notification((notification) => { - console.log(notification.type); - }); +```js +Echo.private('App.Models.User.' + userId) + .notification((notification) => { + console.log(notification.type); + }); +``` #### Customizing the Notification Channel 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: - id; - } + /** + * The channels the user receives notification broadcasts on. + */ + public function receivesBroadcastNotificationsOn(): string + { + return 'users.'.$this->id; } +} +``` ## SMS Notifications @@ -1011,106 +1119,120 @@ If you would like to customize which channel that an entity's broadcast notifica 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/vonage-notification-channel` and `guzzlehttp/guzzle` packages: - composer require laravel/vonage-notification-channel guzzlehttp/guzzle +```shell +composer require laravel/vonage-notification-channel guzzlehttp/guzzle +``` The package includes a [configuration file](https://github.com/laravel/vonage-notification-channel/blob/3.x/config/vonage.php). However, you are not required to export this configuration file to your own application. You can simply use the `VONAGE_KEY` and `VONAGE_SECRET` environment variables to define your Vonage public and secret keys. After defining your keys, you should set a `VONAGE_SMS_FROM` environment variable that defines the phone number that your SMS messages should be sent from by default. You may generate this phone number within the Vonage control panel: - VONAGE_SMS_FROM=15556666666 +```ini +VONAGE_SMS_FROM=15556666666 +``` ### Formatting SMS Notifications If a notification supports being sent as an SMS, you should define a `toVonage` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\VonageMessage` instance: - use Illuminate\Notifications\Messages\VonageMessage; +```php +use Illuminate\Notifications\Messages\VonageMessage; - /** - * Get the Vonage / SMS representation of the notification. - */ - public function toVonage(object $notifiable): VonageMessage - { - return (new VonageMessage) - ->content('Your SMS message content'); - } +/** + * Get the Vonage / SMS representation of the notification. + */ +public function toVonage(object $notifiable): VonageMessage +{ + return (new VonageMessage) + ->content('Your SMS message content'); +} +``` #### Unicode Content If your SMS message will contain unicode characters, you should call the `unicode` method when constructing the `VonageMessage` instance: - use Illuminate\Notifications\Messages\VonageMessage; +```php +use Illuminate\Notifications\Messages\VonageMessage; - /** - * Get the Vonage / SMS representation of the notification. - */ - public function toVonage(object $notifiable): VonageMessage - { - return (new VonageMessage) - ->content('Your unicode message') - ->unicode(); - } +/** + * Get the Vonage / SMS representation of the notification. + */ +public function toVonage(object $notifiable): VonageMessage +{ + return (new VonageMessage) + ->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 by your `VONAGE_SMS_FROM` environment variable, you may call the `from` method on a `VonageMessage` instance: - use Illuminate\Notifications\Messages\VonageMessage; +```php +use Illuminate\Notifications\Messages\VonageMessage; - /** - * Get the Vonage / SMS representation of the notification. - */ - public function toVonage(object $notifiable): VonageMessage - { - return (new VonageMessage) - ->content('Your SMS message content') - ->from('15554443333'); - } +/** + * Get the Vonage / SMS representation of the notification. + */ +public function toVonage(object $notifiable): VonageMessage +{ + return (new VonageMessage) + ->content('Your SMS message content') + ->from('15554443333'); +} +``` ### Adding a Client Reference If you would like to keep track of costs per user, team, or client, you may add a "client reference" to the notification. Vonage will allow you to generate reports using this client reference so that you can better understand a particular customer's SMS usage. The client reference can be any string up to 40 characters: - use Illuminate\Notifications\Messages\VonageMessage; +```php +use Illuminate\Notifications\Messages\VonageMessage; - /** - * Get the Vonage / SMS representation of the notification. - */ - public function toVonage(object $notifiable): VonageMessage - { - return (new VonageMessage) - ->clientReference((string) $notifiable->id) - ->content('Your SMS message content'); - } +/** + * Get the Vonage / SMS representation of the notification. + */ +public function toVonage(object $notifiable): VonageMessage +{ + return (new VonageMessage) + ->clientReference((string) $notifiable->id) + ->content('Your SMS message content'); +} +``` ### Routing SMS Notifications To route Vonage notifications to the proper phone number, define a `routeNotificationForVonage` method on your notifiable entity: - phone_number; - } + /** + * Route notifications for the Vonage channel. + */ + public function routeNotificationForVonage(Notification $notification): string + { + return $this->phone_number; } +} +``` ## Slack Notifications @@ -1130,12 +1252,14 @@ 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'), - ], +```php +'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), ], +], +``` #### App Distribution @@ -1147,70 +1271,74 @@ If your application will be sending notifications to external Slack workspaces t If a notification supports being sent as a Slack message, you should define a `toSlack` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Slack\SlackMessage` instance. You can construct rich notifications using [Slack's Block Kit API](https://api.slack.com/block-kit). The following example may be previewed in [Slack's Block Kit builder](https://app.slack.com/block-kit-builder/T01KWS6K23Z#%7B%22blocks%22:%5B%7B%22type%22:%22header%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Invoice%20Paid%22%7D%7D,%7B%22type%22:%22context%22,%22elements%22:%5B%7B%22type%22:%22plain_text%22,%22text%22:%22Customer%20%231234%22%7D%5D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22An%20invoice%20has%20been%20paid.%22%7D,%22fields%22:%5B%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Invoice%20No:*%5Cn1000%22%7D,%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Invoice%20Recipient:*%5Cntaylor@laravel.com%22%7D%5D%7D,%7B%22type%22:%22divider%22%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Congratulations!%22%7D%7D%5D%7D): - use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; - use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; - use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject; - use Illuminate\Notifications\Slack\SlackMessage; +```php +use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; +use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; +use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject; +use Illuminate\Notifications\Slack\SlackMessage; - /** - * Get the Slack representation of the notification. - */ - public function toSlack(object $notifiable): SlackMessage - { - return (new SlackMessage) - ->text('One of your invoices has been paid!') - ->headerBlock('Invoice Paid') - ->contextBlock(function (ContextBlock $block) { - $block->text('Customer #1234'); - }) - ->sectionBlock(function (SectionBlock $block) { - $block->text('An invoice has been paid.'); - $block->field("*Invoice No:*\n1000")->markdown(); - $block->field("*Invoice Recipient:*\ntaylor@laravel.com")->markdown(); - }) - ->dividerBlock() - ->sectionBlock(function (SectionBlock $block) { - $block->text('Congratulations!'); - }); - } +/** + * Get the Slack representation of the notification. + */ +public function toSlack(object $notifiable): SlackMessage +{ + return (new SlackMessage) + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->contextBlock(function (ContextBlock $block) { + $block->text('Customer #1234'); + }) + ->sectionBlock(function (SectionBlock $block) { + $block->text('An invoice has been paid.'); + $block->field("*Invoice No:*\n1000")->markdown(); + $block->field("*Invoice Recipient:*\ntaylor@laravel.com")->markdown(); + }) + ->dividerBlock() + ->sectionBlock(function (SectionBlock $block) { + $block->text('Congratulations!'); + }); +} +``` #### Using Slack's Block Kit Builder Template Instead of using the fluent message builder methods to construct your Block Kit message, you may provide the raw JSON payload generated by Slack's Block Kit Builder to the `usingBlockKitTemplate` method: - use Illuminate\Notifications\Slack\SlackMessage; - use Illuminate\Support\Str; +```php +use Illuminate\Notifications\Slack\SlackMessage; +use Illuminate\Support\Str; - /** - * Get the Slack representation of the notification. - */ - public function toSlack(object $notifiable): SlackMessage - { - $template = <<usingBlockKitTemplate($template); - } + return (new SlackMessage) + ->usingBlockKitTemplate($template); +} +``` ### Slack Interactivity @@ -1219,81 +1347,87 @@ Slack's Block Kit notification system provides powerful features to [handle user In the following example, which utilizes the `actionsBlock` method, Slack will send a `POST` request to your "Request URL" with a payload containing the Slack user who clicked the button, the ID of the clicked button, and more. Your application can then determine the action to take based on the payload. You should also [verify the request](https://api.slack.com/authentication/verifying-requests-from-slack) was made by Slack: - use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock; - use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; - use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; - use Illuminate\Notifications\Slack\SlackMessage; +```php +use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock; +use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; +use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; +use Illuminate\Notifications\Slack\SlackMessage; - /** - * Get the Slack representation of the notification. - */ - public function toSlack(object $notifiable): SlackMessage - { - return (new SlackMessage) - ->text('One of your invoices has been paid!') - ->headerBlock('Invoice Paid') - ->contextBlock(function (ContextBlock $block) { - $block->text('Customer #1234'); - }) - ->sectionBlock(function (SectionBlock $block) { - $block->text('An invoice has been paid.'); - }) - ->actionsBlock(function (ActionsBlock $block) { - // ID defaults to "button_acknowledge_invoice"... - $block->button('Acknowledge Invoice')->primary(); - - // Manually configure the ID... - $block->button('Deny')->danger()->id('deny_invoice'); - }); - } +/** + * Get the Slack representation of the notification. + */ +public function toSlack(object $notifiable): SlackMessage +{ + return (new SlackMessage) + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->contextBlock(function (ContextBlock $block) { + $block->text('Customer #1234'); + }) + ->sectionBlock(function (SectionBlock $block) { + $block->text('An invoice has been paid.'); + }) + ->actionsBlock(function (ActionsBlock $block) { + // ID defaults to "button_acknowledge_invoice"... + $block->button('Acknowledge Invoice')->primary(); + + // Manually configure the ID... + $block->button('Deny')->danger()->id('deny_invoice'); + }); +} +``` #### Confirmation Modals If you would like users to be required to confirm an action before it is performed, you may invoke the `confirm` method when defining your button. The `confirm` method accepts a message and a closure which receives a `ConfirmObject` instance: - use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock; - use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; - use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; - use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject; - use Illuminate\Notifications\Slack\SlackMessage; - - /** - * Get the Slack representation of the notification. - */ - public function toSlack(object $notifiable): SlackMessage - { - return (new SlackMessage) - ->text('One of your invoices has been paid!') - ->headerBlock('Invoice Paid') - ->contextBlock(function (ContextBlock $block) { - $block->text('Customer #1234'); - }) - ->sectionBlock(function (SectionBlock $block) { - $block->text('An invoice has been paid.'); - }) - ->actionsBlock(function (ActionsBlock $block) { - $block->button('Acknowledge Invoice') - ->primary() - ->confirm( - 'Acknowledge the payment and send a thank you email?', - function (ConfirmObject $dialog) { - $dialog->confirm('Yes'); - $dialog->deny('No'); - } - ); - }); - } +```php +use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock; +use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock; +use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock; +use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject; +use Illuminate\Notifications\Slack\SlackMessage; + +/** + * Get the Slack representation of the notification. + */ +public function toSlack(object $notifiable): SlackMessage +{ + return (new SlackMessage) + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->contextBlock(function (ContextBlock $block) { + $block->text('Customer #1234'); + }) + ->sectionBlock(function (SectionBlock $block) { + $block->text('An invoice has been paid.'); + }) + ->actionsBlock(function (ActionsBlock $block) { + $block->button('Acknowledge Invoice') + ->primary() + ->confirm( + 'Acknowledge the payment and send a thank you email?', + function (ConfirmObject $dialog) { + $dialog->confirm('Yes'); + $dialog->deny('No'); + } + ); + }); +} +``` #### Inspecting Slack Blocks If you would like to quickly inspect the blocks you've been building, you can invoke the `dd` method on the `SlackMessage` instance. The `dd` method will generate and dump a URL to Slack's [Block Kit Builder](https://app.slack.com/block-kit-builder/), which displays a preview of the payload and notification in your browser. You may pass `true` to the `dd` method to dump the raw payload: - return (new SlackMessage) - ->text('One of your invoices has been paid!') - ->headerBlock('Invoice Paid') - ->dd(); +```php +return (new SlackMessage) + ->text('One of your invoices has been paid!') + ->headerBlock('Invoice Paid') + ->dd(); +``` ### Routing Slack Notifications @@ -1306,26 +1440,28 @@ To direct Slack notifications to the appropriate Slack team and channel, define For instance, returning `#support-channel` from the `routeNotificationForSlack` method will send the notification to the `#support-channel` channel in the workspace associated with the Bot User OAuth token located in your application's `services.php` configuration file: - ### Notifying External Slack Workspaces @@ -1337,27 +1473,29 @@ Of course, you will often want to send notifications to the Slack workspaces own Once you have obtained the bot token and stored it within your application's database, you may utilize the `SlackRoute::make` method to route a notification to the user's workspace. In addition, your application will likely need to offer an opportunity for the user to specify which channel notifications should be sent to: - slack_channel, $this->slack_token); - } + /** + * Route notifications for the Slack channel. + */ + public function routeNotificationForSlack(Notification $notification): mixed + { + return SlackRoute::make($this->slack_channel, $this->slack_token); } +} +``` ## Localizing Notifications @@ -1366,35 +1504,43 @@ Laravel allows you to send notifications in a locale other than the HTTP request 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')); +```php +$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) - ); +```php +Notification::locale('es')->send( + $users, new InvoicePaid($invoice) +); +``` ### User Preferred Locales Sometimes, applications store each user's preferred locale. By implementing the `HasLocalePreference` contract on your notifiable model, you may instruct Laravel to use this stored locale when sending a notification: - use Illuminate\Contracts\Translation\HasLocalePreference; +```php +use Illuminate\Contracts\Translation\HasLocalePreference; - class User extends Model implements HasLocalePreference +class User extends Model implements HasLocalePreference +{ + /** + * Get the user's preferred locale. + */ + public function preferredLocale(): string { - /** - * Get the user's preferred locale. - */ - public function preferredLocale(): string - { - return $this->locale; - } + return $this->locale; } +} +``` Once you have implemented the interface, Laravel will automatically use the preferred locale when sending notifications and mailables to the model. Therefore, there is no need to call the `locale` method when using this interface: - $user->notify(new InvoicePaid($invoice)); +```php +$user->notify(new InvoicePaid($invoice)); +``` ## Testing @@ -1470,28 +1616,34 @@ class ExampleTest extends TestCase 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: - Notification::assertSentTo( - $user, - function (OrderShipped $notification, array $channels) use ($order) { - return $notification->order->id === $order->id; - } - ); +```php +Notification::assertSentTo( + $user, + function (OrderShipped $notification, array $channels) use ($order) { + return $notification->order->id === $order->id; + } +); +``` #### On-Demand Notifications If the code you are testing sends [on-demand notifications](#on-demand-notifications), you can test that the on-demand notification was sent via the `assertSentOnDemand` method: - Notification::assertSentOnDemand(OrderShipped::class); +```php +Notification::assertSentOnDemand(OrderShipped::class); +``` By passing a closure as the second argument to the `assertSentOnDemand` method, you may determine if an on-demand notification was sent to the correct "route" address: - Notification::assertSentOnDemand( - OrderShipped::class, - function (OrderShipped $notification, array $channels, object $notifiable) use ($user) { - return $notifiable->routes['mail'] === $user->email; - } - ); +```php +Notification::assertSentOnDemand( + OrderShipped::class, + function (OrderShipped $notification, array $channels, object $notifiable) use ($user) { + return $notifiable->routes['mail'] === $user->email; + } +); +``` ## Notification Events @@ -1501,71 +1653,81 @@ By passing a closure as the second argument to the `assertSentOnDemand` method, When a notification is sending, the `Illuminate\Notifications\Events\NotificationSending` event is dispatched by the notification system. This contains the "notifiable" entity and the notification instance itself. You may create [event listeners](/docs/{{version}}/events) for this event within your application: - use Illuminate\Notifications\Events\NotificationSending; - - class CheckNotificationStatus - { - /** - * Handle the given event. - */ - public function handle(NotificationSending $event): void - { - // ... - } - } - -The notification will not be sent if an event listener for the `NotificationSending` event returns `false` from its `handle` method: +```php +use Illuminate\Notifications\Events\NotificationSending; +class CheckNotificationStatus +{ /** * Handle the given event. */ - public function handle(NotificationSending $event): bool + public function handle(NotificationSending $event): void { - return false; + // ... } +} +``` + +The notification will not be sent if an event listener for the `NotificationSending` event returns `false` from its `handle` method: + +```php +/** + * Handle the given event. + */ +public function handle(NotificationSending $event): bool +{ + return false; +} +``` Within an event listener, you may access the `notifiable`, `notification`, and `channel` properties on the event to learn more about the notification recipient or the notification itself: - /** - * Handle the given event. - */ - public function handle(NotificationSending $event): void - { - // $event->channel - // $event->notifiable - // $event->notification - } +```php +/** + * Handle the given event. + */ +public function handle(NotificationSending $event): void +{ + // $event->channel + // $event->notifiable + // $event->notification +} +``` #### Notification Sent Event When a notification is sent, the `Illuminate\Notifications\Events\NotificationSent` [event](/docs/{{version}}/events) is dispatched by the notification system. This contains the "notifiable" entity and the notification instance itself. You may create [event listeners](/docs/{{version}}/events) for this event within your application: - use Illuminate\Notifications\Events\NotificationSent; - - class LogNotification - { - /** - * Handle the given event. - */ - public function handle(NotificationSent $event): void - { - // ... - } - } - -Within an event listener, you may access the `notifiable`, `notification`, `channel`, and `response` properties on the event to learn more about the notification recipient or the notification itself: +```php +use Illuminate\Notifications\Events\NotificationSent; +class LogNotification +{ /** * Handle the given event. */ public function handle(NotificationSent $event): void { - // $event->channel - // $event->notifiable - // $event->notification - // $event->response + // ... } +} +``` + +Within an event listener, you may access the `notifiable`, `notification`, `channel`, and `response` properties on the event to learn more about the notification recipient or the notification itself: + +```php +/** + * Handle the given event. + */ +public function handle(NotificationSent $event): void +{ + // $event->channel + // $event->notifiable + // $event->notification + // $event->response +} +``` ## Custom Channels @@ -1574,54 +1736,58 @@ Laravel ships with a handful of notification channels, but you may want to write 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: - toVoice($notifiable); + $message = $notification->toVoice($notifiable); - // Send notification to the $notifiable instance... - } + // Send notification to the $notifiable instance... } +} +``` Once your notification channel class has been defined, you may return the class name from the `via` method of any of your notifications. In this example, the `toVoice` method of your notification can return whatever object you choose to represent voice messages. For example, you might define your own `VoiceMessage` class to represent these messages: - publishes([ - __DIR__.'/../config/courier.php' => config_path('courier.php'), - ]); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->publishes([ + __DIR__.'/../config/courier.php' => config_path('courier.php'), + ]); +} +``` Now, when users of your package execute Laravel's `vendor:publish` command, your file will be copied to the specified publish location. Once your configuration has been published, its values may be accessed like any other configuration file: - $value = config('courier.option'); +```php +$value = config('courier.option'); +``` > [!WARNING] > You should not define closures in your configuration files. They cannot be serialized correctly when users execute the `config:cache` Artisan command. @@ -117,15 +121,17 @@ You may also merge your own package configuration file with the application's pu 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. - */ - public function register(): void - { - $this->mergeConfigFrom( - __DIR__.'/../config/courier.php', 'courier' - ); - } +```php +/** + * Register any application services. + */ +public function register(): void +{ + $this->mergeConfigFrom( + __DIR__.'/../config/courier.php', 'courier' + ); +} +``` > [!WARNING] > This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. @@ -135,45 +141,53 @@ The `mergeConfigFrom` method accepts the path to your package's configuration fi 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 package services. - */ - public function boot(): void - { - $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); +} +``` ### Migrations If your package contains [database migrations](/docs/{{version}}/migrations), you may use the `publishesMigrations` method to inform Laravel that the given directory or file contains migrations. When Laravel publishes the migrations, it will automatically update the timestamp within their filename to reflect the current date and time: - /** - * Bootstrap any package services. - */ - public function boot(): void - { - $this->publishesMigrations([ - __DIR__.'/../database/migrations' => database_path('migrations'), - ]); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->publishesMigrations([ + __DIR__.'/../database/migrations' => database_path('migrations'), + ]); +} +``` ### Language Files If your package contains [language 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 package services. - */ - public function boot(): void - { - $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); +} +``` Package translation lines 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: - echo trans('courier::messages.welcome'); +```php +echo trans('courier::messages.welcome'); +``` You can register JSON translation files for your package using the `loadJsonTranslationsFrom` method. This method accepts the path to the directory that contains your package's JSON translation files: @@ -192,17 +206,19 @@ public function boot(): void If you would like to publish your package's language files to the application's `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 language files for the `courier` package, you may do the following: - /** - * Bootstrap any package services. - */ - public function boot(): void - { - $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); - $this->publishes([ - __DIR__.'/../lang' => $this->app->langPath('vendor/courier'), - ]); - } + $this->publishes([ + __DIR__.'/../lang' => $this->app->langPath('vendor/courier'), + ]); +} +``` Now, when users of your package execute Laravel's `vendor:publish` Artisan command, your package's language files will be published to the specified publish location. @@ -211,19 +227,23 @@ 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 package services. - */ - public function boot(): void - { - $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $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 `dashboard` view from the `courier` package like so: - Route::get('/dashboard', function () { - return view('courier::dashboard'); - }); +```php +Route::get('/dashboard', function () { + return view('courier::dashboard'); +}); +``` #### Overriding Package Views @@ -235,17 +255,19 @@ 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 the package services. - */ - public function boot(): void - { - $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); +```php +/** + * Bootstrap the package services. + */ +public function boot(): void +{ + $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); - $this->publishes([ - __DIR__.'/../resources/views' => resource_path('views/vendor/courier'), - ]); - } + $this->publishes([ + __DIR__.'/../resources/views' => resource_path('views/vendor/courier'), + ]); +} +``` Now, when users of your package execute Laravel's `vendor:publish` Artisan command, your package's views will be copied to the specified publish location. @@ -254,16 +276,18 @@ Now, when users of your package execute Laravel's `vendor:publish` Artisan comma 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; +```php +use Illuminate\Support\Facades\Blade; +use VendorPackage\View\Components\AlertComponent; - /** - * Bootstrap your package's services. - */ - public function boot(): void - { - Blade::component('package-alert', AlertComponent::class); - } +/** + * Bootstrap your package's services. + */ +public function boot(): void +{ + Blade::component('package-alert', AlertComponent::class); +} +``` Once your component has been registered, it may be rendered using its tag alias: @@ -276,15 +300,17 @@ Once your component has been registered, it may be rendered using its tag alias: 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 `Nightshade\Views\Components` namespace: - use Illuminate\Support\Facades\Blade; +```php +use Illuminate\Support\Facades\Blade; - /** - * Bootstrap your package's services. - */ - public function boot(): void - { - Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); - } +/** + * Bootstrap your package's services. + */ +public function boot(): void +{ + Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); +} +``` This will allow the usage of package components by their vendor namespace using the `package-name::` syntax: @@ -309,69 +335,77 @@ If your package contains anonymous components, they must be placed within a `com Laravel's built-in `about` Artisan command provides a synopsis of the application's environment and configuration. Packages may push additional information to this command's output via the `AboutCommand` class. Typically, this information may be added from your package service provider's `boot` method: - use Illuminate\Foundation\Console\AboutCommand; +```php +use Illuminate\Foundation\Console\AboutCommand; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']); +} +``` ## Commands 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 any package services. - */ - public function boot(): void - { - if ($this->app->runningInConsole()) { - $this->commands([ - InstallCommand::class, - NetworkCommand::class, - ]); - } +```php +use Courier\Console\Commands\InstallCommand; +use Courier\Console\Commands\NetworkCommand; + +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + if ($this->app->runningInConsole()) { + $this->commands([ + InstallCommand::class, + NetworkCommand::class, + ]); } +} +``` ### Optimize Commands Laravel's [`optimize` command](/docs/{{version}}/deployment#optimization) caches the application's configuration, events, routes, and views. Using the `optimizes` method, you may register your package's own Artisan commands that should be invoked when the `optimize` and `optimize:clear` commands are executed: - /** - * Bootstrap any package services. - */ - public function boot(): void - { - if ($this->app->runningInConsole()) { - $this->optimizes( - optimize: 'package:optimize', - clear: 'package:clear-optimizations', - ); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + if ($this->app->runningInConsole()) { + $this->optimizes( + optimize: 'package:optimize', + clear: 'package:clear-optimizations', + ); } +} +``` ## 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 easily publish groups of related assets: - /** - * Bootstrap any package services. - */ - public function boot(): void - { - $this->publishes([ - __DIR__.'/../public' => public_path('vendor/courier'), - ], 'public'); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->publishes([ + __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 users will typically need to overwrite the assets every time the package is updated, you may use the `--force` flag: @@ -384,19 +418,21 @@ php artisan vendor:publish --tag=public --force 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 for the `courier` package (`courier-config` and `courier-migrations`) in the `boot` method of the package's service provider: - /** - * Bootstrap any package services. - */ - public function boot(): void - { - $this->publishes([ - __DIR__.'/../config/package.php' => config_path('package.php') - ], 'courier-config'); - - $this->publishesMigrations([ - __DIR__.'/../database/migrations/' => database_path('migrations') - ], 'courier-migrations'); - } +```php +/** + * Bootstrap any package services. + */ +public function boot(): void +{ + $this->publishes([ + __DIR__.'/../config/package.php' => config_path('package.php') + ], 'courier-config'); + + $this->publishesMigrations([ + __DIR__.'/../database/migrations/' => database_path('migrations') + ], 'courier-migrations'); +} +``` Now your users may publish these groups separately by referencing their tag when executing the `vendor:publish` command: diff --git a/pagination.md b/pagination.md index 553cc9167c0..cf83aa612de 100644 --- a/pagination.md +++ b/pagination.md @@ -46,26 +46,28 @@ There are several ways to paginate items. The simplest is by using the `paginate 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: - DB::table('users')->paginate(15) - ]); - } + return view('user.index', [ + 'users' => DB::table('users')->paginate(15) + ]); } +} +``` #### Simple Pagination @@ -74,39 +76,51 @@ The `paginate` method counts the total number of records matched by the query be 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); +```php +$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 `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: - use App\Models\User; +```php +use App\Models\User; - $users = User::paginate(15); +$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); +```php +$users = User::where('votes', '>', 100)->paginate(15); +``` You may also use the `simplePaginate` method when paginating Eloquent models: - $users = User::where('votes', '>', 100)->simplePaginate(15); +```php +$users = User::where('votes', '>', 100)->simplePaginate(15); +``` Similarly, you may use the `cursorPaginate` method to cursor paginate Eloquent models: - $users = User::where('votes', '>', 100)->cursorPaginate(15); +```php +$users = User::where('votes', '>', 100)->cursorPaginate(15); +``` #### Multiple Paginator Instances per Page Sometimes you may need to render two separate paginators on a single screen that is rendered by your application. However, if both paginator instances use the `page` query string parameter to store the current page, the two paginator's will conflict. To resolve this conflict, you may pass the name of the query string parameter you wish to use to store the paginator's current page via the third argument provided to the `paginate`, `simplePaginate`, and `cursorPaginate` methods: - use App\Models\User; +```php +use App\Models\User; - $users = User::where('votes', '>', 100)->paginate( - $perPage = 15, $columns = ['*'], $pageName = 'users' - ); +$users = User::where('votes', '>', 100)->paginate( + $perPage = 15, $columns = ['*'], $pageName = 'users' +); +``` ### Cursor Pagination @@ -121,7 +135,9 @@ http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0 You may create a cursor based paginator instance via the `cursorPaginate` method offered by the query builder. This method returns an instance of `Illuminate\Pagination\CursorPaginator`: - $users = DB::table('users')->orderBy('id')->cursorPaginate(15); +```php +$users = DB::table('users')->orderBy('id')->cursorPaginate(15); +``` Once you have retrieved a cursor paginator instance, you may [display the pagination results](#displaying-pagination-results) as you typically would when using the `paginate` and `simplePaginate` methods. For more information on the instance methods offered by the cursor paginator, please consult the [cursor paginator instance method documentation](#cursor-paginator-instance-methods). @@ -170,41 +186,49 @@ In other words, the `Paginator` corresponds to the `simplePaginate` method on th 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; +```php +use App\Models\User; - Route::get('/users', function () { - $users = User::paginate(15); +Route::get('/users', function () { + $users = User::paginate(15); - $users->withPath('/admin/users'); + $users->withPath('/admin/users'); - // ... - }); + // ... +}); +``` #### Appending Query String Values 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; +```php +use App\Models\User; - Route::get('/users', function () { - $users = User::paginate(15); +Route::get('/users', function () { + $users = User::paginate(15); - $users->appends(['sort' => 'votes']); + $users->appends(['sort' => 'votes']); - // ... - }); + // ... +}); +``` 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: - $users = User::paginate(15)->withQueryString(); +```php +$users = User::paginate(15)->withQueryString(); +``` #### Appending Hash Fragments 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: - $users = User::paginate(15)->fragment('users'); +```php +$users = User::paginate(15)->fragment('users'); +``` ## Displaying Pagination Results @@ -239,35 +263,39 @@ When the paginator displays pagination links, the current page number is display 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: - use App\Models\User; +```php +use App\Models\User; - Route::get('/users', function () { - return User::paginate(); - }); +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 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, - "per_page": 15, - "current_page": 1, - "last_page": 4, - "first_page_url": "http://laravel.app/?page=1", - "last_page_url": "http://laravel.app/?page=4", - "next_page_url": "http://laravel.app/?page=2", - "prev_page_url": null, - "path": "http://laravel.app/", - "from": 1, - "to": 15, - "data":[ - { - // Record... - }, - { - // Record... - } - ] - } +```json +{ + "total": 50, + "per_page": 15, + "current_page": 1, + "last_page": 4, + "first_page_url": "http://laravel.app/?page=1", + "last_page_url": "http://laravel.app/?page=4", + "next_page_url": "http://laravel.app/?page=2", + "prev_page_url": null, + "path": "http://laravel.app/", + "from": 1, + "to": 15, + "data":[ + { + // Record... + }, + { + // Record... + } + ] +} +``` ## Customizing the Pagination View @@ -291,41 +319,45 @@ This command will place the views in your application's `resources/views/vendor/ 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: - ### 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 `useBootstrapFour` or `useBootstrapFive` methods within the `boot` method of your `App\Providers\AppServiceProvider` class: - use Illuminate\Pagination\Paginator; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Paginator::useBootstrapFive(); - Paginator::useBootstrapFour(); - } +```php +use Illuminate\Pagination\Paginator; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Paginator::useBootstrapFive(); + Paginator::useBootstrapFour(); +} +``` ## Paginator / LengthAwarePaginator Instance Methods diff --git a/passport.md b/passport.md index 9aaa57cb908..26729dd3ec0 100644 --- a/passport.md +++ b/passport.md @@ -73,33 +73,37 @@ Additionally, this command will ask if you would like to use UUIDs as the primar 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', - ], +```php +'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], - 'api' => [ - 'driver' => 'passport', - 'provider' => 'users', - ], + 'api' => [ + 'driver' => 'passport', + 'provider' => 'users', ], +], +``` ### Deploying Passport @@ -112,13 +116,15 @@ 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. Typically, this method should be called from the `boot` method of your application's `App\Providers\AppServiceProvider` class: - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); +} +``` #### Loading Keys From the Environment @@ -154,9 +160,11 @@ When upgrading to a new major version of Passport, it's important that you caref 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\AppServiceProvider` class: - use Laravel\Passport\Passport; +```php +use Laravel\Passport\Passport; - Passport::hashClientSecrets(); +Passport::hashClientSecrets(); +``` 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. @@ -165,15 +173,17 @@ Once enabled, all of your client secrets will only be displayable to the user im 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\AppServiceProvider` class: - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Passport::tokensExpireIn(now()->addDays(15)); - Passport::refreshTokensExpireIn(now()->addDays(30)); - Passport::personalAccessTokensExpireIn(now()->addMonths(6)); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::tokensExpireIn(now()->addDays(15)); + Passport::refreshTokensExpireIn(now()->addDays(30)); + Passport::personalAccessTokensExpireIn(now()->addMonths(6)); +} +``` > [!WARNING] > 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). @@ -183,57 +193,65 @@ By default, Passport issues long-lived access tokens that expire after one year. 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; +```php +use Laravel\Passport\Client as PassportClient; - class Client extends PassportClient - { - // ... - } +class Client extends PassportClient +{ + // ... +} +``` 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\AppServiceProvider` class: - use App\Models\Passport\AuthCode; - use App\Models\Passport\Client; - use App\Models\Passport\PersonalAccessClient; - use App\Models\Passport\RefreshToken; - use App\Models\Passport\Token; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Passport::useTokenModel(Token::class); - Passport::useRefreshTokenModel(RefreshToken::class); - Passport::useAuthCodeModel(AuthCode::class); - Passport::useClientModel(Client::class); - Passport::usePersonalAccessClientModel(PersonalAccessClient::class); - } +```php +use App\Models\Passport\AuthCode; +use App\Models\Passport\Client; +use App\Models\Passport\PersonalAccessClient; +use App\Models\Passport\RefreshToken; +use App\Models\Passport\Token; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::useTokenModel(Token::class); + Passport::useRefreshTokenModel(RefreshToken::class); + Passport::useAuthCodeModel(AuthCode::class); + Passport::useClientModel(Client::class); + Passport::usePersonalAccessClientModel(PersonalAccessClient::class); +} +``` ### Overriding Routes Sometimes you may wish to customize the routes defined by Passport. To achieve this, you first need to ignore the routes registered by Passport by adding `Passport::ignoreRoutes` to the `register` method of your application's `AppServiceProvider`: - use Laravel\Passport\Passport; +```php +use Laravel\Passport\Passport; - /** - * Register any application services. - */ - public function register(): void - { - Passport::ignoreRoutes(); - } +/** + * Register any application services. + */ +public function register(): void +{ + Passport::ignoreRoutes(); +} +``` Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/11.x/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: - Route::group([ - 'as' => 'passport.', - 'prefix' => config('passport.path', 'oauth'), - 'namespace' => '\Laravel\Passport\Http\Controllers', - ], function () { - // Passport routes... - }); +```php +Route::group([ + 'as' => 'passport.', + 'prefix' => config('passport.path', 'oauth'), + 'namespace' => '\Laravel\Passport\Http\Controllers', +], function () { + // Passport routes... +}); +``` ## Issuing Access Tokens @@ -345,23 +363,25 @@ axios.delete('/oauth/clients/' + clientId) 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; +```php +use Illuminate\Http\Request; +use Illuminate\Support\Str; - Route::get('/redirect', function (Request $request) { - $request->session()->put('state', $state = Str::random(40)); +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://third-party-app.com/callback', - 'response_type' => 'code', - 'scope' => '', - 'state' => $state, - // 'prompt' => '', // "none", "consent", or "login" - ]); + $query = http_build_query([ + 'client_id' => 'client-id', + 'redirect_uri' => 'http://third-party-app.com/callback', + 'response_type' => 'code', + 'scope' => '', + 'state' => $state, + // 'prompt' => '', // "none", "consent", or "login" + ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); - }); + return redirect('http://passport-app.test/oauth/authorize?'.$query); +}); +``` The `prompt` parameter may be used to specify the authentication behavior of the Passport application. @@ -385,50 +405,54 @@ php artisan vendor:publish --tag=passport-views Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately, unless the consuming application has explicitly set the `prompt` parameter when redirecting for authorization: - firstParty(); - } + return $this->firstParty(); } +} +``` #### 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 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; +```php +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Http; - Route::get('/callback', function (Request $request) { - $state = $request->session()->pull('state'); +Route::get('/callback', function (Request $request) { + $state = $request->session()->pull('state'); - throw_unless( - strlen($state) > 0 && $state === $request->state, - InvalidArgumentException::class, - 'Invalid state value.' - ); + throw_unless( + strlen($state) > 0 && $state === $request->state, + InvalidArgumentException::class, + 'Invalid state value.' + ); - $response = Http::asForm()->post('http://passport-app.test/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, - ]); + $response = Http::asForm()->post('http://passport-app.test/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 $response->json(); - }); + 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. @@ -466,17 +490,19 @@ axios.delete('/oauth/tokens/' + tokenId); 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: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ - '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.test/oauth/token', [ + 'grant_type' => 'refresh_token', + 'refresh_token' => 'the-refresh-token', + 'client_id' => 'client-id', + 'client_secret' => 'client-secret', + 'scope' => '', +]); - return $response->json(); +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. @@ -485,17 +511,19 @@ This `/oauth/token` route will return a JSON response containing `access_token`, 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; +```php +use Laravel\Passport\TokenRepository; +use Laravel\Passport\RefreshTokenRepository; - $tokenRepository = app(TokenRepository::class); - $refreshTokenRepository = app(RefreshTokenRepository::class); +$tokenRepository = app(TokenRepository::class); +$refreshTokenRepository = app(RefreshTokenRepository::class); - // Revoke an access token... - $tokenRepository->revokeAccessToken($tokenId); +// Revoke an access token... +$tokenRepository->revokeAccessToken($tokenId); - // Revoke all of the token's refresh tokens... - $refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId); +// Revoke all of the token's refresh tokens... +$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId); +``` ### Purging Tokens @@ -518,9 +546,11 @@ php artisan passport:purge --expired You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your application's `routes/console.php` file to automatically prune your tokens on a schedule: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('passport:purge')->hourly(); +Schedule::command('passport:purge')->hourly(); +``` ## Authorization Code Grant With PKCE @@ -548,42 +578,46 @@ The code verifier should be a random string of between 43 and 128 characters con 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. - $encoded = base64_encode(hash('sha256', $code_verifier, true)); +```php +$encoded = base64_encode(hash('sha256', $code_verifier, true)); - $codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_'); +$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_'); +``` #### Redirecting for Authorization 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; +```php +use Illuminate\Http\Request; +use Illuminate\Support\Str; - Route::get('/redirect', function (Request $request) { - $request->session()->put('state', $state = Str::random(40)); +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)) - , '='), '+/', '-_'); + $codeChallenge = strtr(rtrim( + base64_encode(hash('sha256', $code_verifier, true)) + , '='), '+/', '-_'); - $query = http_build_query([ - 'client_id' => 'client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', - 'response_type' => 'code', - 'scope' => '', - 'state' => $state, - 'code_challenge' => $codeChallenge, - 'code_challenge_method' => 'S256', - // 'prompt' => '', // "none", "consent", or "login" - ]); + $query = http_build_query([ + 'client_id' => 'client-id', + 'redirect_uri' => 'http://third-party-app.com/callback', + 'response_type' => 'code', + 'scope' => '', + 'state' => $state, + 'code_challenge' => $codeChallenge, + 'code_challenge_method' => 'S256', + // 'prompt' => '', // "none", "consent", or "login" + ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); - }); + return redirect('http://passport-app.test/oauth/authorize?'.$query); +}); +``` #### Converting Authorization Codes to Access Tokens @@ -592,29 +626,31 @@ 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; +```php +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Http; - Route::get('/callback', function (Request $request) { - $state = $request->session()->pull('state'); +Route::get('/callback', function (Request $request) { + $state = $request->session()->pull('state'); - $codeVerifier = $request->session()->pull('code_verifier'); + $codeVerifier = $request->session()->pull('code_verifier'); - throw_unless( - strlen($state) > 0 && $state === $request->state, - InvalidArgumentException::class - ); + throw_unless( + strlen($state) > 0 && $state === $request->state, + InvalidArgumentException::class + ); - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ - 'grant_type' => 'authorization_code', - 'client_id' => 'client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', - 'code_verifier' => $codeVerifier, - 'code' => $request->code, - ]); + $response = Http::asForm()->post('http://passport-app.test/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 $response->json(); - }); + return $response->json(); +}); +``` ## Password Grant Tokens @@ -626,13 +662,15 @@ The OAuth2 password grant allows your other first-party clients, such as a mobil To enable the password grant, call the `enablePasswordGrant` method in the `boot` method of your application's `App\Providers\AppServiceProvider` class: - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Passport::enablePasswordGrant(); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::enablePasswordGrant(); +} +``` ### Creating a Password Grant Client @@ -648,18 +686,20 @@ php artisan passport:client --password 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 Passport 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: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ - '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.test/oauth/token', [ + 'grant_type' => 'password', + 'client_id' => 'client-id', + 'client_secret' => 'client-secret', + 'username' => 'taylor@laravel.com', + 'password' => 'my-password', + 'scope' => '', +]); - return $response->json(); +return $response->json(); +``` > [!NOTE] > Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. @@ -669,16 +709,18 @@ 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: - use Illuminate\Support\Facades\Http; - - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ - 'grant_type' => 'password', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', - 'username' => 'taylor@laravel.com', - 'password' => 'my-password', - 'scope' => '*', - ]); +```php +use Illuminate\Support\Facades\Http; + +$response = Http::asForm()->post('http://passport-app.test/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 @@ -690,53 +732,57 @@ If your application uses more than one [authentication user provider](/docs/{{ve 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: - where('username', $username)->first(); - } + return $this->where('username', $username)->first(); } +} +``` ### Customizing the Password Validation When authenticating using the password grant, Passport will use the `password` attribute of your model to validate the given password. If your model does not have a `password` attribute or you wish to customize the password validation logic, you can define a `validateForPassportPasswordGrant` method on your model: - password); - } + return Hash::check($password, $this->password); } +} +``` ## Implicit Grant Tokens @@ -746,32 +792,36 @@ When authenticating using the password grant, Passport will use the `password` a 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 application's `App\Providers\AppServiceProvider` class: - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Passport::enableImplicitGrant(); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::enableImplicitGrant(); +} +``` 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; +```php +use Illuminate\Http\Request; - Route::get('/redirect', function (Request $request) { - $request->session()->put('state', $state = Str::random(40)); +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://third-party-app.com/callback', - 'response_type' => 'token', - 'scope' => '', - 'state' => $state, - // 'prompt' => '', // "none", "consent", or "login" - ]); + $query = http_build_query([ + 'client_id' => 'client-id', + 'redirect_uri' => 'http://third-party-app.com/callback', + 'response_type' => 'token', + 'scope' => '', + 'state' => $state, + // 'prompt' => '', // "none", "consent", or "login" + ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); - }); + return redirect('http://passport-app.test/oauth/authorize?'.$query); +}); +``` > [!NOTE] > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. @@ -789,41 +839,49 @@ 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: - use Laravel\Passport\Http\Middleware\CheckClientCredentials; +```php +use Laravel\Passport\Http\Middleware\CheckClientCredentials; - ->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'client' => CheckClientCredentials::class - ]); - }) +->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'client' => CheckClientCredentials::class + ]); +}) +``` Then, attach the middleware to a route: - Route::get('/orders', function (Request $request) { - ... - })->middleware('client'); +```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: - Route::get('/orders', function (Request $request) { - ... - })->middleware('client:check-status,your-scope'); +```php +Route::get('/orders', function (Request $request) { + ... +})->middleware('client:check-status,your-scope'); +``` ### Retrieving Tokens To retrieve a token using this grant type, make a request to the `oauth/token` endpoint: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ - 'grant_type' => 'client_credentials', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', - 'scope' => 'your-scope', - ]); +$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ + 'grant_type' => 'client_credentials', + 'client_id' => 'client-id', + 'client_secret' => 'client-secret', + 'scope' => 'your-scope', +]); - return $response->json()['access_token']; +return $response->json()['access_token']; +``` ## Personal Access Tokens @@ -854,15 +912,17 @@ PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value" 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; +```php +use App\Models\User; - $user = User::find(1); +$user = User::find(1); - // Creating a token without scopes... - $token = $user->createToken('Token Name')->accessToken; +// Creating a token without scopes... +$token = $user->createToken('Token Name')->accessToken; - // Creating a token with scopes... - $token = $user->createToken('My Token', ['place-orders'])->accessToken; +// Creating a token with scopes... +$token = $user->createToken('My Token', ['place-orders'])->accessToken; +``` #### JSON API @@ -932,9 +992,11 @@ axios.delete('/oauth/personal-access-tokens/' + tokenId); 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 () { - // ... - })->middleware('auth:api'); +```php +Route::get('/user', function () { + // ... +})->middleware('auth:api'); +``` > [!WARNING] > If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. @@ -944,21 +1006,25 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add If your application authenticates different types of users that perhaps use entirely different Eloquent models, you will likely need to define a guard configuration for each user provider type in your application. This allows you to protect requests intended for specific user providers. For example, given the following guard configuration the `config/auth.php` configuration file: - 'api' => [ - 'driver' => 'passport', - 'provider' => 'users', - ], +```php +'api' => [ + 'driver' => 'passport', + 'provider' => 'users', +], - 'api-customers' => [ - 'driver' => 'passport', - 'provider' => 'customers', - ], +'api-customers' => [ + 'driver' => 'passport', + 'provider' => 'customers', +], +``` The following route will utilize the `api-customers` guard, which uses the `customers` user provider, to authenticate incoming requests: - Route::get('/customer', function () { - // ... - })->middleware('auth:api-customers'); +```php +Route::get('/customer', function () { + // ... +})->middleware('auth:api-customers'); +``` > [!NOTE] > For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). @@ -968,14 +1034,16 @@ 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: - use Illuminate\Support\Facades\Http; +```php +use Illuminate\Support\Facades\Http; - $response = Http::withHeaders([ - 'Accept' => 'application/json', - 'Authorization' => 'Bearer '.$accessToken, - ])->get('https://passport-app.test/api/user'); +$response = Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer '.$accessToken, +])->get('https://passport-app.test/api/user'); - return $response->json(); +return $response->json(); +``` ## Token Scopes @@ -987,33 +1055,37 @@ Scopes allow your API clients to request a specific set of permissions when requ You may define your API's scopes using the `Passport::tokensCan` method in the `boot` method of your application's `App\Providers\AppServiceProvider` 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: - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Passport::tokensCan([ - 'place-orders' => 'Place orders', - 'check-status' => 'Check order status', - ]); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + 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 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\AppServiceProvider` class: - use Laravel\Passport\Passport; +```php +use Laravel\Passport\Passport; - Passport::tokensCan([ - 'place-orders' => 'Place orders', - 'check-status' => 'Check order status', - ]); +Passport::tokensCan([ + 'place-orders' => 'Place orders', + 'check-status' => 'Check order status', +]); - Passport::setDefaultScope([ - 'check-status', - 'place-orders', - ]); +Passport::setDefaultScope([ + 'check-status', + 'place-orders', +]); +``` > [!NOTE] > Passport's default scopes do not apply to personal access tokens that are generated by the user. @@ -1026,90 +1098,110 @@ If a client does not request any specific scopes, you may configure your Passpor When requesting an access token using the authorization code grant, consumers should specify their desired scopes as the `scope` query string parameter. The `scope` parameter should be a space-delimited list of scopes: - Route::get('/redirect', function () { - $query = http_build_query([ - 'client_id' => 'client-id', - 'redirect_uri' => 'http://example.com/callback', - 'response_type' => 'code', - 'scope' => 'place-orders check-status', - ]); +```php +Route::get('/redirect', function () { + $query = http_build_query([ + 'client_id' => 'client-id', + 'redirect_uri' => 'http://example.com/callback', + 'response_type' => 'code', + 'scope' => 'place-orders check-status', + ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); - }); + return redirect('http://passport-app.test/oauth/authorize?'.$query); +}); +``` #### When Issuing Personal Access Tokens 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; +```php +$token = $user->createToken('My Token', ['place-orders'])->accessToken; +``` ### Checking Scopes Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, define the following middleware aliases in your application's `bootstrap/app.php` file: - use Laravel\Passport\Http\Middleware\CheckForAnyScope; - use Laravel\Passport\Http\Middleware\CheckScopes; +```php +use Laravel\Passport\Http\Middleware\CheckForAnyScope; +use Laravel\Passport\Http\Middleware\CheckScopes; - ->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'scopes' => CheckScopes::class, - 'scope' => CheckForAnyScope::class, - ]); - }) +->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'scopes' => CheckScopes::class, + 'scope' => CheckForAnyScope::class, + ]); +}) +``` #### 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: - Route::get('/orders', function () { - // Access token has both "check-status" and "place-orders" scopes... - })->middleware(['auth:api', 'scopes:check-status,place-orders']); +```php +Route::get('/orders', function () { + // Access token has both "check-status" and "place-orders" scopes... +})->middleware(['auth:api', 'scopes:check-status,place-orders']); +``` #### Check for Any Scopes The `scope` middleware may be assigned to a route to verify that the incoming request's access token has *at least one* of the listed scopes: - Route::get('/orders', function () { - // Access token has either "check-status" or "place-orders" scope... - })->middleware(['auth:api', 'scope:check-status,place-orders']); +```php +Route::get('/orders', function () { + // Access token has either "check-status" or "place-orders" scope... +})->middleware(['auth:api', 'scope:check-status,place-orders']); +``` #### 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 `App\Models\User` instance: - use Illuminate\Http\Request; +```php +use Illuminate\Http\Request; - Route::get('/orders', function (Request $request) { - if ($request->user()->tokenCan('place-orders')) { - // ... - } - }); +Route::get('/orders', function (Request $request) { + if ($request->user()->tokenCan('place-orders')) { + // ... + } +}); +``` #### Additional Scope Methods The `scopeIds` method will return an array of all defined IDs / names: - use Laravel\Passport\Passport; +```php +use Laravel\Passport\Passport; - Passport::scopeIds(); +Passport::scopeIds(); +``` The `scopes` method will return an array of all defined scopes as instances of `Laravel\Passport\Scope`: - Passport::scopes(); +```php +Passport::scopes(); +``` The `scopesFor` method will return an array of `Laravel\Passport\Scope` instances matching the given IDs / names: - Passport::scopesFor(['place-orders', 'check-status']); +```php +Passport::scopesFor(['place-orders', 'check-status']); +``` You may determine if a given scope has been defined using the `hasScope` method: - Passport::hasScope('place-orders'); +```php +Passport::hasScope('place-orders'); +``` ## Consuming Your API With JavaScript @@ -1118,36 +1210,42 @@ When building an API, it can be extremely useful to be able to consume your own Typically, if you want to consume your API from your JavaScript application, you would need to manually send an access token to the application and pass it with each request to your application. However, Passport includes a middleware that can handle this for you. All you need to do is append the `CreateFreshApiToken` middleware to the `web` middleware group in your application's `bootstrap/app.php` file: - use Laravel\Passport\Http\Middleware\CreateFreshApiToken; +```php +use Laravel\Passport\Http\Middleware\CreateFreshApiToken; - ->withMiddleware(function (Middleware $middleware) { - $middleware->web(append: [ - CreateFreshApiToken::class, - ]); - }) +->withMiddleware(function (Middleware $middleware) { + $middleware->web(append: [ + CreateFreshApiToken::class, + ]); +}) +``` > [!WARNING] > You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. 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 => { - console.log(response.data); - }); +```js +axios.get('/api/user') + .then(response => { + console.log(response.data); + }); +``` #### 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 application's `App\Providers\AppServiceProvider` class: - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Passport::cookie('custom_name'); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::cookie('custom_name'); +} +``` #### CSRF Protection diff --git a/passwords.md b/passwords.md index 21a5833e329..e9682f30634 100644 --- a/passwords.md +++ b/passwords.md @@ -52,9 +52,11 @@ To properly implement support for allowing users to reset their passwords, we wi First, we will define the routes that are needed to request password reset links. To get started, we will define a route that returns a view with the password reset link request form: - Route::get('/forgot-password', function () { - return view('auth.forgot-password'); - })->middleware('guest')->name('password.request'); +```php +Route::get('/forgot-password', function () { + return view('auth.forgot-password'); +})->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. @@ -63,20 +65,22 @@ The view that is returned by this route should have a form containing an `email` 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; +```php +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Password; - Route::post('/forgot-password', function (Request $request) { - $request->validate(['email' => 'required|email']); +Route::post('/forgot-password', function (Request $request) { + $request->validate(['email' => 'required|email']); - $status = Password::sendResetLink( - $request->only('email') - ); + $status = Password::sendResetLink( + $request->only('email') + ); - return $status === Password::ResetLinkSent - ? back()->with(['status' => __($status)]) - : back()->withErrors(['email' => __($status)]); - })->middleware('guest')->name('password.email'); + return $status === Password::ResetLinkSent + ? back()->with(['status' => __($status)]) + : back()->withErrors(['email' => __($status)]); +})->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). @@ -98,9 +102,11 @@ You may be wondering how Laravel knows how to retrieve the user record from your Next, we will define the routes necessary to actually reset the password once the user clicks on the password reset link that has been emailed to them and provides a new password. First, let's define the route that will display the reset password form that is displayed when the user clicks the reset password link. This route will receive a `token` parameter that we will use later to verify the password reset request: - Route::get('/reset-password/{token}', function (string $token) { - return view('auth.reset-password', ['token' => $token]); - })->middleware('guest')->name('password.reset'); +```php +Route::get('/reset-password/{token}', function (string $token) { + return view('auth.reset-password', ['token' => $token]); +})->middleware('guest')->name('password.reset'); +``` 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. @@ -109,37 +115,39 @@ The view that is returned by this route should display a form containing an `ema Of course, we need to define a route to actually handle the password reset form submission. This route will be responsible for validating the incoming request and updating the user's password in the database: - use App\Models\User; - use Illuminate\Auth\Events\PasswordReset; - use Illuminate\Http\Request; - use Illuminate\Support\Facades\Hash; - use Illuminate\Support\Facades\Password; - use Illuminate\Support\Str; - - Route::post('/reset-password', function (Request $request) { - $request->validate([ - 'token' => 'required', - 'email' => 'required|email', - 'password' => 'required|min:8|confirmed', - ]); - - $status = Password::reset( - $request->only('email', 'password', 'password_confirmation', 'token'), - function (User $user, string $password) { - $user->forceFill([ - 'password' => Hash::make($password) - ])->setRememberToken(Str::random(60)); - - $user->save(); - - event(new PasswordReset($user)); - } - ); - - return $status === Password::PasswordReset - ? redirect()->route('login')->with('status', __($status)) - : back()->withErrors(['email' => [__($status)]]); - })->middleware('guest')->name('password.update'); +```php +use App\Models\User; +use Illuminate\Auth\Events\PasswordReset; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Password; +use Illuminate\Support\Str; + +Route::post('/reset-password', function (Request $request) { + $request->validate([ + 'token' => 'required', + 'email' => 'required|email', + 'password' => 'required|min:8|confirmed', + ]); + + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function (User $user, string $password) { + $user->forceFill([ + 'password' => Hash::make($password) + ])->setRememberToken(Str::random(60)); + + $user->save(); + + event(new PasswordReset($user)); + } + ); + + return $status === Password::PasswordReset + ? redirect()->route('login')->with('status', __($status)) + : 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. @@ -160,9 +168,11 @@ php artisan auth:clear-resets If you would like to automate this process, consider adding the command to your application's [scheduler](/docs/{{version}}/scheduling): - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('auth:clear-resets')->everyFifteenMinutes(); +Schedule::command('auth:clear-resets')->everyFifteenMinutes(); +``` ## Customization @@ -172,34 +182,38 @@ If you would like to automate this process, consider adding the command to your 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\AppServiceProvider` service provider's `boot` method: - use App\Models\User; - use Illuminate\Auth\Notifications\ResetPassword; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - ResetPassword::createUrlUsing(function (User $user, string $token) { - return 'https://example.com/reset-password?token='.$token; - }); - } +```php +use App\Models\User; +use Illuminate\Auth\Notifications\ResetPassword; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + ResetPassword::createUrlUsing(function (User $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 `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; +```php +use App\Notifications\ResetPasswordNotification; - /** - * Send a password reset notification to the user. - * - * @param string $token - */ - public function sendPasswordResetNotification($token): void - { - $url = 'https://example.com/reset-password?token='.$token; +/** + * Send a password reset notification to the user. + * + * @param string $token + */ +public function sendPasswordResetNotification($token): void +{ + $url = 'https://example.com/reset-password?token='.$token; - $this->notify(new ResetPasswordNotification($url)); - } + $this->notify(new ResetPasswordNotification($url)); +} +``` diff --git a/pennant.md b/pennant.md index 619b2707924..f158490a59c 100644 --- a/pennant.md +++ b/pennant.md @@ -273,37 +273,41 @@ class PodcastController The `when` method may be used to fluently execute a given closure if a feature is active. Additionally, a second closure may be provided and will be executed if the feature is inactive: - $this->resolveNewApiResponse($request), - fn () => $this->resolveLegacyApiResponse($request), - ); - } - - // ... + return Feature::when(NewApi::class, + fn () => $this->resolveNewApiResponse($request), + fn () => $this->resolveLegacyApiResponse($request), + ); } + // ... +} +``` + The `unless` method serves as the inverse of the `when` method, executing the first closure if the feature is inactive: - return Feature::unless(NewApi::class, - fn () => $this->resolveLegacyApiResponse($request), - fn () => $this->resolveNewApiResponse($request), - ); +```php +return Feature::unless(NewApi::class, + fn () => $this->resolveLegacyApiResponse($request), + fn () => $this->resolveNewApiResponse($request), +); +``` ### The `HasFeatures` Trait @@ -497,7 +501,9 @@ When checking a feature, Pennant will create an in-memory cache of the result. I If you need to manually flush the in-memory cache, you may use the `flushCache` method offered by the `Feature` facade: - Feature::flushCache(); +```php +Feature::flushCache(); +``` ## Scope @@ -698,17 +704,21 @@ Pennant's included Blade directive also makes it easy to conditionally render co When calling the [conditional `when`](#conditional-execution) method, the feature's rich value will be provided to the first closure: - Feature::when('purchase-button', - fn ($color) => /* ... */, - fn () => /* ... */, - ); +```php +Feature::when('purchase-button', + fn ($color) => /* ... */, + fn () => /* ... */, +); +``` Likewise, when calling the conditional `unless` method, the feature's rich value will be provided to the optional second closure: - Feature::unless('purchase-button', - fn () => /* ... */, - fn ($color) => /* ... */, - ); +```php +Feature::unless('purchase-button', + fn () => /* ... */, + fn ($color) => /* ... */, +); +``` ## Retrieving Multiple Features @@ -740,25 +750,27 @@ However, class based features are dynamically registered and are not known by Pe If you would like to ensure that feature classes are always included when using the `all` method, you may use Pennant's feature discovery capabilities. To get started, invoke the `discover` method in one of your application's service providers: - [ +```php +'stores' => [ - 'redis' => [ - 'driver' => 'redis', - 'connection' => null, - ], + 'redis' => [ + 'driver' => 'redis', + 'connection' => null, + ], - // ... + // ... - ], +], +``` ### Defining Features Externally diff --git a/processes.md b/processes.md index 7195f182029..eefc6acf766 100644 --- a/processes.md +++ b/processes.md @@ -618,16 +618,18 @@ Process::assertRanTimes(function (PendingProcess $process, ProcessResult $result If you would like to ensure that all invoked processes have been faked throughout your individual test or complete test suite, you can call the `preventStrayProcesses` method. After calling this method, any processes that do not have a corresponding fake result will throw an exception rather than starting an actual process: - use Illuminate\Support\Facades\Process; +```php +use Illuminate\Support\Facades\Process; - Process::preventStrayProcesses(); +Process::preventStrayProcesses(); - Process::fake([ - 'ls *' => 'Test output...', - ]); +Process::fake([ + 'ls *' => 'Test output...', +]); - // Fake response is returned... - Process::run('ls -la'); +// Fake response is returned... +Process::run('ls -la'); - // An exception is thrown... - Process::run('bash import.sh'); +// An exception is thrown... +Process::run('bash import.sh'); +``` diff --git a/prompts.md b/prompts.md index 441f5c6a1b6..22bf02e6083 100644 --- a/prompts.md +++ b/prompts.md @@ -922,7 +922,7 @@ $progress->finish(); The `clear` function may be used to clear the user's terminal: -``` +```php use function Laravel\Prompts\clear; clear(); diff --git a/providers.md b/providers.md index f68718dfb67..2aadd5a9a99 100644 --- a/providers.md +++ b/providers.md @@ -39,26 +39,28 @@ As mentioned previously, within the `register` method, you should only bind thin Let's take a look at a basic service provider. Within any of your service provider methods, you always have access to the `$app` property which provides access to the service container: - app->singleton(Connection::class, function (Application $app) { - return new Connection(config('riak')); - }); - } + $this->app->singleton(Connection::class, function (Application $app) { + return new Connection(config('riak')); + }); } +} +``` 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're not yet familiar with Laravel's service container, check out [its documentation](/docs/{{version}}/container). @@ -67,100 +69,110 @@ This service provider only defines a `register` method, and uses that method to If your service provider registers many simple bindings, you may wish to use the `bindings` and `singletons` properties instead of manually registering each container binding. When the service provider is loaded by the framework, it will automatically check for these properties and register their bindings: - DigitalOceanServerProvider::class, - ]; - - /** - * All of the container singletons that should be registered. - * - * @var array - */ - public $singletons = [ - DowntimeNotifier::class => PingdomDowntimeNotifier::class, - ServerProvider::class => ServerToolsProvider::class, - ]; - } +class AppServiceProvider extends ServiceProvider +{ + /** + * All of the container bindings that should be registered. + * + * @var array + */ + public $bindings = [ + ServerProvider::class => DigitalOceanServerProvider::class, + ]; + + /** + * All of the container singletons that should be registered. + * + * @var array + */ + public $singletons = [ + DowntimeNotifier::class => PingdomDowntimeNotifier::class, + ServerProvider::class => ServerToolsProvider::class, + ]; +} +``` ### The Boot Method So, what if we need to register a [view composer](/docs/{{version}}/views#view-composers) within our service provider? This should be done within the `boot` method. **This method is called after all other service providers have been registered**, meaning you have access to all other services that have been registered by the framework: - #### Boot Method Dependency Injection You may type-hint dependencies for your service provider's `boot` method. The [service container](/docs/{{version}}/container) will automatically inject any dependencies you need: - use Illuminate\Contracts\Routing\ResponseFactory; - - /** - * Bootstrap any application services. - */ - public function boot(ResponseFactory $response): void - { - $response->macro('serialized', function (mixed $value) { - // ... - }); - } +```php +use Illuminate\Contracts\Routing\ResponseFactory; + +/** + * Bootstrap any application services. + */ +public function boot(ResponseFactory $response): void +{ + $response->macro('serialized', function (mixed $value) { + // ... + }); +} +``` ## Registering Providers All service providers are registered in the `bootstrap/providers.php` configuration file. This file returns an array that contains the class names of your application's service providers: - ## Deferred Providers @@ -171,34 +183,36 @@ Laravel compiles and stores a list of all of the services supplied by deferred s To defer the loading of a provider, implement the `\Illuminate\Contracts\Support\DeferrableProvider` interface and define a `provides` method. The `provides` method should return the service container bindings registered by the provider: - app->singleton(Connection::class, function (Application $app) { + return new Connection($app['config']['riak']); + }); + } - class RiakServiceProvider extends ServiceProvider implements DeferrableProvider + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array { - /** - * Register any application services. - */ - public function register(): void - { - $this->app->singleton(Connection::class, function (Application $app) { - return new Connection($app['config']['riak']); - }); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides(): array - { - return [Connection::class]; - } + return [Connection::class]; } +} +``` diff --git a/pulse.md b/pulse.md index 4d870ce3c13..e129001bfc2 100644 --- a/pulse.md +++ b/pulse.md @@ -729,7 +729,7 @@ class TopSellers extends Card The `aggregate` method returns a collection of PHP `stdClass` objects. Each object will contain the `key` property captured earlier, along with keys for each of the requested aggregates: -``` +```blade @foreach ($topSellers as $seller) {{ $seller->key }} {{ $seller->sum }} diff --git a/queries.md b/queries.md index 72e474e858e..7902e65c969 100644 --- a/queries.md +++ b/queries.md @@ -53,35 +53,39 @@ The Laravel query builder uses PDO parameter binding to protect your application 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(); - - return view('user.index', ['users' => $users]); - } + $users = DB::table('users')->get(); + + return view('user.index', ['users' => $users]); } +} +``` The `get` method returns an `Illuminate\Support\Collection` instance containing the results of the query where each result is an instance of the PHP `stdClass` object. You may access each column's value by accessing the column as a property of the object: - use Illuminate\Support\Facades\DB; +```php +use Illuminate\Support\Facades\DB; - $users = DB::table('users')->get(); +$users = DB::table('users')->get(); - foreach ($users as $user) { - echo $user->name; - } +foreach ($users as $user) { + echo $user->name; +} +``` > [!NOTE] > Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel collections, check out the [collection documentation](/docs/{{version}}/collections). @@ -91,75 +95,93 @@ The `get` method returns an `Illuminate\Support\Collection` instance containing 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(); +```php +$user = DB::table('users')->where('name', 'John')->first(); - return $user->email; +return $user->email; +``` If you would like to retrieve a single row from a database table, but throw an `Illuminate\Database\RecordNotFoundException` if no matching row is found, you may use the `firstOrFail` method. If the `RecordNotFoundException` is not caught, a 404 HTTP response is automatically sent back to the client: - $user = DB::table('users')->where('name', 'John')->firstOrFail(); +```php +$user = DB::table('users')->where('name', 'John')->firstOrFail(); +``` 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'); +```php +$email = DB::table('users')->where('name', 'John')->value('email'); +``` To retrieve a single row by its `id` column value, use the `find` method: - $user = DB::table('users')->find(3); +```php +$user = DB::table('users')->find(3); +``` #### Retrieving a List of Column Values 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 user titles: - use Illuminate\Support\Facades\DB; +```php +use Illuminate\Support\Facades\DB; - $titles = DB::table('users')->pluck('title'); +$titles = DB::table('users')->pluck('title'); - foreach ($titles as $title) { - echo $title; - } +foreach ($titles as $title) { + echo $title; +} +``` You may specify the column that the resulting collection should use as its keys by providing a second argument to the `pluck` method: - $titles = DB::table('users')->pluck('title', 'name'); +```php +$titles = DB::table('users')->pluck('title', 'name'); - foreach ($titles as $name => $title) { - echo $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 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\Collection; - use Illuminate\Support\Facades\DB; +```php +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; - DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { - foreach ($users as $user) { - // ... - } - }); +DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { + foreach ($users as $user) { + // ... + } +}); +``` You may stop further chunks from being processed by returning `false` from the closure: - DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { - // Process the records... +```php +DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { + // Process the records... - return false; - }); + return false; +}); +``` 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 (Collection $users) { - foreach ($users as $user) { - DB::table('users') - ->where('id', $user->id) - ->update(['active' => true]); - } - }); +```php +DB::table('users')->where('active', false) + ->chunkById(100, function (Collection $users) { + foreach ($users as $user) { + DB::table('users') + ->where('id', $user->id) + ->update(['active' => true]); + } + }); +``` Since the `chunkById` and `lazyById` methods add their own "where" conditions to the query being executed, you should typically [logically group](#logical-grouping) your own conditions within a closure: @@ -210,30 +232,36 @@ DB::table('users')->where('active', false) 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; +```php +use Illuminate\Support\Facades\DB; - $users = DB::table('users')->count(); +$users = DB::table('users')->count(); - $price = DB::table('orders')->max('price'); +$price = DB::table('orders')->max('price'); +``` 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) - ->avg('price'); +```php +$price = DB::table('orders') + ->where('finalized', 1) + ->avg('price'); +``` #### Determining if Records Exist 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: - if (DB::table('orders')->where('finalized', 1)->exists()) { - // ... - } +```php +if (DB::table('orders')->where('finalized', 1)->exists()) { + // ... +} - if (DB::table('orders')->where('finalized', 1)->doesntExist()) { - // ... - } +if (DB::table('orders')->where('finalized', 1)->doesntExist()) { + // ... +} +``` ## Select Statements @@ -243,32 +271,40 @@ Instead of using the `count` method to determine if any records exist that match 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: - use Illuminate\Support\Facades\DB; +```php +use Illuminate\Support\Facades\DB; - $users = DB::table('users') - ->select('name', 'email as user_email') - ->get(); +$users = DB::table('users') + ->select('name', 'email as user_email') + ->get(); +``` The `distinct` method allows you to force the query to return distinct results: - $users = DB::table('users')->distinct()->get(); +```php +$users = DB::table('users')->distinct()->get(); +``` If you already have a query builder instance and you wish to add a column to its existing select clause, you may use the `addSelect` method: - $query = DB::table('users')->select('name'); +```php +$query = DB::table('users')->select('name'); - $users = $query->addSelect('age')->get(); +$users = $query->addSelect('age')->get(); +``` ## Raw Expressions 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(); +```php +$users = DB::table('users') + ->select(DB::raw('count(*) as user_count, status')) + ->where('status', '<>', 1) + ->groupBy('status') + ->get(); +``` > [!WARNING] > Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities. @@ -283,48 +319,58 @@ Instead of using the `DB::raw` method, you may also use the following methods to The `selectRaw` method can be used in place of `addSelect(DB::raw(/* ... */))`. This method accepts an optional array of bindings as its second argument: - $orders = DB::table('orders') - ->selectRaw('price * ? as price_with_tax', [1.0825]) - ->get(); +```php +$orders = DB::table('orders') + ->selectRaw('price * ? as price_with_tax', [1.0825]) + ->get(); +``` #### `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: - $orders = DB::table('orders') - ->whereRaw('price > IF(state = "TX", ?, 100)', [200]) - ->get(); +```php +$orders = DB::table('orders') + ->whereRaw('price > IF(state = "TX", ?, 100)', [200]) + ->get(); +``` #### `havingRaw / orHavingRaw` 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')) - ->groupBy('department') - ->havingRaw('SUM(price) > ?', [2500]) - ->get(); +```php +$orders = DB::table('orders') + ->select('department', DB::raw('SUM(price) as total_sales')) + ->groupBy('department') + ->havingRaw('SUM(price) > ?', [2500]) + ->get(); +``` #### `orderByRaw` 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') - ->get(); +```php +$orders = DB::table('orders') + ->orderByRaw('updated_at - created_at DESC') + ->get(); +``` ### `groupByRaw` 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') - ->groupByRaw('city, state') - ->get(); +```php +$orders = DB::table('orders') + ->select('city', 'state') + ->groupByRaw('city, state') + ->get(); +``` ## Joins @@ -334,70 +380,82 @@ The `groupByRaw` method may be used to provide a raw string as the value of the 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 multiple tables in a single query: - use Illuminate\Support\Facades\DB; +```php +use Illuminate\Support\Facades\DB; - $users = DB::table('users') - ->join('contacts', 'users.id', '=', 'contacts.user_id') - ->join('orders', 'users.id', '=', 'orders.user_id') - ->select('users.*', 'contacts.phone', 'orders.price') - ->get(); +$users = DB::table('users') + ->join('contacts', 'users.id', '=', 'contacts.user_id') + ->join('orders', 'users.id', '=', 'orders.user_id') + ->select('users.*', 'contacts.phone', 'orders.price') + ->get(); +``` #### Left Join / Right Join Clause If you would like to perform a "left join" or "right join" instead of an "inner join", use the `leftJoin` or `rightJoin` methods. These methods have the same signature as the `join` method: - $users = DB::table('users') - ->leftJoin('posts', 'users.id', '=', 'posts.user_id') - ->get(); +```php +$users = DB::table('users') + ->leftJoin('posts', 'users.id', '=', 'posts.user_id') + ->get(); - $users = DB::table('users') - ->rightJoin('posts', 'users.id', '=', 'posts.user_id') - ->get(); +$users = DB::table('users') + ->rightJoin('posts', 'users.id', '=', 'posts.user_id') + ->get(); +``` #### Cross Join Clause 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') - ->get(); +```php +$sizes = DB::table('sizes') + ->crossJoin('colors') + ->get(); +``` #### Advanced Join Clauses 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 (JoinClause $join) { - $join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */); - }) - ->get(); +```php +DB::table('users') + ->join('contacts', function (JoinClause $join) { + $join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */); + }) + ->get(); +``` 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 (JoinClause $join) { - $join->on('users.id', '=', 'contacts.user_id') - ->where('contacts.user_id', '>', 5); - }) - ->get(); +```php +DB::table('users') + ->join('contacts', function (JoinClause $join) { + $join->on('users.id', '=', 'contacts.user_id') + ->where('contacts.user_id', '>', 5); + }) + ->get(); +``` #### Subquery Joins You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a query to a subquery. Each of these methods receives 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')) - ->where('is_published', true) - ->groupBy('user_id'); - - $users = DB::table('users') - ->joinSub($latestPosts, 'latest_posts', function (JoinClause $join) { - $join->on('users.id', '=', 'latest_posts.user_id'); - })->get(); +```php +$latestPosts = DB::table('posts') + ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at')) + ->where('is_published', true) + ->groupBy('user_id'); + +$users = DB::table('users') + ->joinSub($latestPosts, 'latest_posts', function (JoinClause $join) { + $join->on('users.id', '=', 'latest_posts.user_id'); + })->get(); +``` #### Lateral Joins @@ -409,30 +467,34 @@ You may use the `joinLateral` and `leftJoinLateral` methods to perform a "latera In this example, we will retrieve a collection of users as well as the user's three most recent blog posts. Each user can produce up to three rows in the result set: one for each of their most recent blog posts. The join condition is specified with a `whereColumn` clause within the subquery, referencing the current user row: - $latestPosts = DB::table('posts') - ->select('id as post_id', 'title as post_title', 'created_at as post_created_at') - ->whereColumn('user_id', 'users.id') - ->orderBy('created_at', 'desc') - ->limit(3); - - $users = DB::table('users') - ->joinLateral($latestPosts, 'latest_posts') - ->get(); +```php +$latestPosts = DB::table('posts') + ->select('id as post_id', 'title as post_title', 'created_at as post_created_at') + ->whereColumn('user_id', 'users.id') + ->orderBy('created_at', 'desc') + ->limit(3); + +$users = DB::table('users') + ->joinLateral($latestPosts, 'latest_posts') + ->get(); +``` ## Unions 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; +```php +use Illuminate\Support\Facades\DB; - $first = DB::table('users') - ->whereNull('first_name'); +$first = DB::table('users') + ->whereNull('first_name'); - $users = DB::table('users') - ->whereNull('last_name') - ->union($first) - ->get(); +$users = DB::table('users') + ->whereNull('last_name') + ->union($first) + ->get(); +``` 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. @@ -446,35 +508,43 @@ You may use the query builder's `where` method to add "where" clauses to the que 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) - ->where('age', '>', 35) - ->get(); +```php +$users = DB::table('users') + ->where('votes', '=', 100) + ->where('age', '>', 35) + ->get(); +``` 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(); +```php +$users = DB::table('users')->where('votes', 100)->get(); +``` As previously mentioned, you may use any operator that is supported by your database system: - $users = DB::table('users') - ->where('votes', '>=', 100) - ->get(); +```php +$users = DB::table('users') + ->where('votes', '>=', 100) + ->get(); - $users = DB::table('users') - ->where('votes', '<>', 100) - ->get(); +$users = DB::table('users') + ->where('votes', '<>', 100) + ->get(); - $users = DB::table('users') - ->where('name', 'like', 'T%') - ->get(); +$users = DB::table('users') + ->where('name', 'like', 'T%') + ->get(); +``` 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(); +```php +$users = DB::table('users')->where([ + ['status', '=', '1'], + ['subscribed', '<>', '1'], +])->get(); +``` > [!WARNING] > 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. @@ -487,20 +557,24 @@ You may also pass an array of conditions to the `where` function. Each element o 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(); +```php +$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: - $users = DB::table('users') - ->where('votes', '>', 100) - ->orWhere(function (Builder $query) { - $query->where('name', 'Abigail') - ->where('votes', '>', 50); - }) - ->get(); +```php +$users = DB::table('users') + ->where('votes', '>', 100) + ->orWhere(function (Builder $query) { + $query->where('name', 'Abigail') + ->where('votes', '>', 50); + }) + ->get(); +``` The example above will produce the following SQL: @@ -516,26 +590,30 @@ select * from users where votes > 100 or (name = 'Abigail' and votes > 50) The `whereNot` and `orWhereNot` methods may be used to negate a given group of query constraints. For example, the following query excludes products that are on clearance or which have a price that is less than ten: - $products = DB::table('products') - ->whereNot(function (Builder $query) { - $query->where('clearance', true) - ->orWhere('price', '<', 10); - }) - ->get(); +```php +$products = DB::table('products') + ->whereNot(function (Builder $query) { + $query->where('clearance', true) + ->orWhere('price', '<', 10); + }) + ->get(); +``` ### Where Any / All / None Clauses Sometimes you may need to apply the same query constraints to multiple columns. For example, you may want to retrieve all records where any columns in a given list are `LIKE` a given value. You may accomplish this using the `whereAny` method: - $users = DB::table('users') - ->where('active', true) - ->whereAny([ - 'name', - 'email', - 'phone', - ], 'like', 'Example%') - ->get(); +```php +$users = DB::table('users') + ->where('active', true) + ->whereAny([ + 'name', + 'email', + 'phone', + ], 'like', 'Example%') + ->get(); +``` The query above will result in the following SQL: @@ -551,13 +629,15 @@ WHERE active = true AND ( Similarly, the `whereAll` method may be used to retrieve records where all of the given columns match a given constraint: - $posts = DB::table('posts') - ->where('published', true) - ->whereAll([ - 'title', - 'content', - ], 'like', '%Laravel%') - ->get(); +```php +$posts = DB::table('posts') + ->where('published', true) + ->whereAll([ + 'title', + 'content', + ], 'like', '%Laravel%') + ->get(); +``` The query above will result in the following SQL: @@ -572,14 +652,16 @@ WHERE published = true AND ( The `whereNone` method may be used to retrieve records where none of the given columns match a given constraint: - $posts = DB::table('albums') - ->where('published', true) - ->whereNone([ - 'title', - 'lyrics', - 'tags', - ], 'like', '%explicit%') - ->get(); +```php +$posts = DB::table('albums') + ->where('published', true) + ->whereNone([ + 'title', + 'lyrics', + 'tags', + ], 'like', '%explicit%') + ->get(); +``` The query above will result in the following SQL: @@ -598,31 +680,39 @@ WHERE published = true AND NOT ( Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MariaDB 10.3+, MySQL 8.0+, PostgreSQL 12.0+, SQL Server 2017+, and SQLite 3.39.0+. To query a JSON column, use the `->` operator: - $users = DB::table('users') - ->where('preferences->dining->meal', 'salad') - ->get(); +```php +$users = DB::table('users') + ->where('preferences->dining->meal', 'salad') + ->get(); +``` You may use `whereJsonContains` to query JSON arrays: - $users = DB::table('users') - ->whereJsonContains('options->languages', 'en') - ->get(); +```php +$users = DB::table('users') + ->whereJsonContains('options->languages', 'en') + ->get(); +``` If your application uses the MariaDB, 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(); +```php +$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(); +```php +$users = DB::table('users') + ->whereJsonLength('options->languages', 0) + ->get(); - $users = DB::table('users') - ->whereJsonLength('options->languages', '>', 1) - ->get(); +$users = DB::table('users') + ->whereJsonLength('options->languages', '>', 1) + ->get(); +``` ### Additional Where Clauses @@ -631,35 +721,45 @@ You may use `whereJsonLength` method to query JSON arrays by their length: The `whereLike` method allows you to add "LIKE" clauses to your query for pattern matching. These methods provide a database-agnostic way of performing string matching queries, with the ability to toggle case-sensitivity. By default, string matching is case-insensitive: - $users = DB::table('users') - ->whereLike('name', '%John%') - ->get(); +```php +$users = DB::table('users') + ->whereLike('name', '%John%') + ->get(); +``` You can enable a case-sensitive search via the `caseSensitive` argument: - $users = DB::table('users') - ->whereLike('name', '%John%', caseSensitive: true) - ->get(); +```php +$users = DB::table('users') + ->whereLike('name', '%John%', caseSensitive: true) + ->get(); +``` The `orWhereLike` method allows you to add an "or" clause with a LIKE condition: - $users = DB::table('users') - ->where('votes', '>', 100) - ->orWhereLike('name', '%John%') - ->get(); +```php +$users = DB::table('users') + ->where('votes', '>', 100) + ->orWhereLike('name', '%John%') + ->get(); +``` The `whereNotLike` method allows you to add "NOT LIKE" clauses to your query: - $users = DB::table('users') - ->whereNotLike('name', '%John%') - ->get(); +```php +$users = DB::table('users') + ->whereNotLike('name', '%John%') + ->get(); +``` Similarly, you can use `orWhereNotLike` to add an "or" clause with a NOT LIKE condition: - $users = DB::table('users') - ->where('votes', '>', 100) - ->orWhereNotLike('name', '%John%') - ->get(); +```php +$users = DB::table('users') + ->where('votes', '>', 100) + ->orWhereNotLike('name', '%John%') + ->get(); +``` > [!WARNING] > The `whereLike` case-sensitive search option is currently not supported on SQL Server. @@ -668,23 +768,29 @@ Similarly, you can use `orWhereNotLike` to add an "or" clause with a NOT LIKE co The `whereIn` method verifies that a given column's value is contained within the given array: - $users = DB::table('users') - ->whereIn('id', [1, 2, 3]) - ->get(); +```php +$users = DB::table('users') + ->whereIn('id', [1, 2, 3]) + ->get(); +``` 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(); +```php +$users = DB::table('users') + ->whereNotIn('id', [1, 2, 3]) + ->get(); +``` You may also provide a query object as the `whereIn` method's second argument: - $activeUsers = DB::table('users')->select('id')->where('is_active', 1); +```php +$activeUsers = DB::table('users')->select('id')->where('is_active', 1); - $users = DB::table('comments') - ->whereIn('user_id', $activeUsers) - ->get(); +$users = DB::table('comments') + ->whereIn('user_id', $activeUsers) + ->get(); +``` The example above will produce the following SQL: @@ -703,158 +809,196 @@ select * from comments where user_id in ( The `whereBetween` method verifies that a column's value is between two values: - $users = DB::table('users') - ->whereBetween('votes', [1, 100]) - ->get(); +```php +$users = DB::table('users') + ->whereBetween('votes', [1, 100]) + ->get(); +``` **whereNotBetween / orWhereNotBetween** The `whereNotBetween` method verifies that a column's value lies outside of two values: - $users = DB::table('users') - ->whereNotBetween('votes', [1, 100]) - ->get(); +```php +$users = DB::table('users') + ->whereNotBetween('votes', [1, 100]) + ->get(); +``` **whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns** The `whereBetweenColumns` method verifies that a column's value is between the two values of two columns in the same table row: - $patients = DB::table('patients') - ->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) - ->get(); +```php +$patients = DB::table('patients') + ->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); +``` The `whereNotBetweenColumns` method verifies that a column's value lies outside the two values of two columns in the same table row: - $patients = DB::table('patients') - ->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) - ->get(); +```php +$patients = DB::table('patients') + ->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); +``` **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(); +```php +$users = DB::table('users') + ->whereNull('updated_at') + ->get(); +``` The `whereNotNull` method verifies that the column's value is not `NULL`: - $users = DB::table('users') - ->whereNotNull('updated_at') - ->get(); +```php +$users = DB::table('users') + ->whereNotNull('updated_at') + ->get(); +``` **whereDate / whereMonth / whereDay / whereYear / whereTime** The `whereDate` method may be used to compare a column's value against a date: - $users = DB::table('users') - ->whereDate('created_at', '2016-12-31') - ->get(); +```php +$users = DB::table('users') + ->whereDate('created_at', '2016-12-31') + ->get(); +``` 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(); +```php +$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 the month: - $users = DB::table('users') - ->whereDay('created_at', '31') - ->get(); +```php +$users = DB::table('users') + ->whereDay('created_at', '31') + ->get(); +``` The `whereYear` method may be used to compare a column's value against a specific year: - $users = DB::table('users') - ->whereYear('created_at', '2016') - ->get(); +```php +$users = DB::table('users') + ->whereYear('created_at', '2016') + ->get(); +``` The `whereTime` method may be used to compare a column's value against a specific time: - $users = DB::table('users') - ->whereTime('created_at', '=', '11:20:45') - ->get(); +```php +$users = DB::table('users') + ->whereTime('created_at', '=', '11:20:45') + ->get(); +``` **wherePast / whereFuture / whereToday / whereBeforeToday / whereAfterToday** The `wherePast` and `whereFuture` methods may be used to determine if a column's value is in the past or future: - $invoices = DB::table('invoices') - ->wherePast('due_at') - ->get(); +```php +$invoices = DB::table('invoices') + ->wherePast('due_at') + ->get(); - $invoices = DB::table('invoices') - ->whereFuture('due_at') - ->get(); +$invoices = DB::table('invoices') + ->whereFuture('due_at') + ->get(); +``` The `whereNowOrPast` and `whereNowOrFuture` methods may be used to determine if a column's value is in the past or future, inclusive of the current date and time: - $invoices = DB::table('invoices') - ->whereNowOrPast('due_at') - ->get(); +```php +$invoices = DB::table('invoices') + ->whereNowOrPast('due_at') + ->get(); - $invoices = DB::table('invoices') - ->whereNowOrFuture('due_at') - ->get(); +$invoices = DB::table('invoices') + ->whereNowOrFuture('due_at') + ->get(); +``` The `whereToday`, `whereBeforeToday`, and `whereAfterToday` methods may be used to determine if a column's value is today, before today, or after today, respectively: - $invoices = DB::table('invoices') - ->whereToday('due_at') - ->get(); +```php +$invoices = DB::table('invoices') + ->whereToday('due_at') + ->get(); - $invoices = DB::table('invoices') - ->whereBeforeToday('due_at') - ->get(); +$invoices = DB::table('invoices') + ->whereBeforeToday('due_at') + ->get(); - $invoices = DB::table('invoices') - ->whereAfterToday('due_at') - ->get(); +$invoices = DB::table('invoices') + ->whereAfterToday('due_at') + ->get(); +``` Similarly, the `whereTodayOrBefore` and `whereTodayOrAfter` methods may be used to determine if a column's value is before today or after today, inclusive of today's date: - $invoices = DB::table('invoices') - ->whereTodayOrBefore('due_at') - ->get(); +```php +$invoices = DB::table('invoices') + ->whereTodayOrBefore('due_at') + ->get(); - $invoices = DB::table('invoices') - ->whereTodayOrAfter('due_at') - ->get(); +$invoices = DB::table('invoices') + ->whereTodayOrAfter('due_at') + ->get(); +``` **whereColumn / orWhereColumn** The `whereColumn` method may be used to verify that two columns are equal: - $users = DB::table('users') - ->whereColumn('first_name', 'last_name') - ->get(); +```php +$users = DB::table('users') + ->whereColumn('first_name', 'last_name') + ->get(); +``` You may also pass a comparison operator to the `whereColumn` method: - $users = DB::table('users') - ->whereColumn('updated_at', '>', 'created_at') - ->get(); +```php +$users = DB::table('users') + ->whereColumn('updated_at', '>', 'created_at') + ->get(); +``` 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([ - ['first_name', '=', 'last_name'], - ['updated_at', '>', 'created_at'], - ])->get(); +```php +$users = DB::table('users') + ->whereColumn([ + ['first_name', '=', 'last_name'], + ['updated_at', '>', 'created_at'], + ])->get(); +``` ### Logical Grouping 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') - ->where(function (Builder $query) { - $query->where('votes', '>', 100) - ->orWhere('title', '=', 'Admin'); - }) - ->get(); +```php +$users = DB::table('users') + ->where('name', '=', 'John') + ->where(function (Builder $query) { + $query->where('votes', '>', 100) + ->orWhere('title', '=', 'Admin'); + }) + ->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: @@ -873,23 +1017,27 @@ select * from users where name = 'John' and (votes > 100 or title = 'Admin') 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 (Builder $query) { - $query->select(DB::raw(1)) - ->from('orders') - ->whereColumn('orders.user_id', 'users.id'); - }) - ->get(); +```php +$users = DB::table('users') + ->whereExists(function (Builder $query) { + $query->select(DB::raw(1)) + ->from('orders') + ->whereColumn('orders.user_id', 'users.id'); + }) + ->get(); +``` Alternatively, you may provide a query object to the `whereExists` method instead of a closure: - $orders = DB::table('orders') - ->select(DB::raw(1)) - ->whereColumn('orders.user_id', 'users.id'); +```php +$orders = DB::table('orders') + ->select(DB::raw(1)) + ->whereColumn('orders.user_id', 'users.id'); - $users = DB::table('users') - ->whereExists($orders) - ->get(); +$users = DB::table('users') + ->whereExists($orders) + ->get(); +``` Both of the examples above will produce the following SQL: @@ -907,25 +1055,29 @@ where exists ( 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; - use Illuminate\Database\Query\Builder; - - $users = User::where(function (Builder $query) { - $query->select('type') - ->from('membership') - ->whereColumn('membership.user_id', 'users.id') - ->orderByDesc('membership.start_date') - ->limit(1); - }, 'Pro')->get(); +```php +use App\Models\User; +use Illuminate\Database\Query\Builder; + +$users = User::where(function (Builder $query) { + $query->select('type') + ->from('membership') + ->whereColumn('membership.user_id', 'users.id') + ->orderByDesc('membership.start_date') + ->limit(1); +}, 'Pro')->get(); +``` Or, you may need to construct a "where" clause that compares a column to the results of a subquery. You may accomplish this by passing a column, operator, and closure to the `where` method. For example, the following query will retrieve all income records where the amount is less than average; - use App\Models\Income; - use Illuminate\Database\Query\Builder; +```php +use App\Models\Income; +use Illuminate\Database\Query\Builder; - $incomes = Income::where('amount', '<', function (Builder $query) { - $query->selectRaw('avg(i.amount)')->from('incomes as i'); - })->get(); +$incomes = Income::where('amount', '<', function (Builder $query) { + $query->selectRaw('avg(i.amount)')->from('incomes as i'); +})->get(); +``` ### Full Text Where Clauses @@ -935,9 +1087,11 @@ Or, you may need to construct a "where" clause that compares a column to the res The `whereFullText` and `orWhereFullText` methods may be used to add full text "where" clauses to a query for columns that have [full text indexes](/docs/{{version}}/migrations#available-index-types). These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a `MATCH AGAINST` clause will be generated for applications utilizing MariaDB or MySQL: - $users = DB::table('users') - ->whereFullText('bio', 'web developer') - ->get(); +```php +$users = DB::table('users') + ->whereFullText('bio', 'web developer') + ->get(); +``` ## Ordering, Grouping, Limit and Offset @@ -950,49 +1104,61 @@ The `whereFullText` and `orWhereFullText` methods may be used to add full text " 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(); +```php +$users = DB::table('users') + ->orderBy('name', 'desc') + ->get(); +``` To sort by multiple columns, you may simply invoke `orderBy` as many times as necessary: - $users = DB::table('users') - ->orderBy('name', 'desc') - ->orderBy('email', 'asc') - ->get(); +```php +$users = DB::table('users') + ->orderBy('name', 'desc') + ->orderBy('email', 'asc') + ->get(); +``` #### The `latest` and `oldest` Methods The `latest` and `oldest` methods allow you to easily order results by date. By default, the 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(); +```php +$user = DB::table('users') + ->latest() + ->first(); +``` #### 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: - $randomUser = DB::table('users') - ->inRandomOrder() - ->first(); +```php +$randomUser = DB::table('users') + ->inRandomOrder() + ->first(); +``` #### Removing Existing Orderings The `reorder` method removes all of the "order by" clauses that have previously been applied to the query: - $query = DB::table('users')->orderBy('name'); +```php +$query = DB::table('users')->orderBy('name'); - $unorderedUsers = $query->reorder()->get(); +$unorderedUsers = $query->reorder()->get(); +``` 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'); +```php +$query = DB::table('users')->orderBy('name'); - $usersOrderedByEmail = $query->reorder('email', 'desc')->get(); +$usersOrderedByEmail = $query->reorder('email', 'desc')->get(); +``` ### Grouping @@ -1002,25 +1168,31 @@ You may pass a column and direction when calling the `reorder` method in order t 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') - ->having('account_id', '>', 100) - ->get(); +```php +$users = DB::table('users') + ->groupBy('account_id') + ->having('account_id', '>', 100) + ->get(); +``` You can use the `havingBetween` method to filter the results within a given range: - $report = DB::table('orders') - ->selectRaw('count(id) as number_of_orders, customer_id') - ->groupBy('customer_id') - ->havingBetween('number_of_orders', [5, 15]) - ->get(); +```php +$report = DB::table('orders') + ->selectRaw('count(id) as number_of_orders, customer_id') + ->groupBy('customer_id') + ->havingBetween('number_of_orders', [5, 15]) + ->get(); +``` You may pass multiple arguments to the `groupBy` method to group by multiple columns: - $users = DB::table('users') - ->groupBy('first_name', 'status') - ->having('account_id', '>', 100) - ->get(); +```php +$users = DB::table('users') + ->groupBy('first_name', 'status') + ->having('account_id', '>', 100) + ->get(); +``` To build more advanced `having` statements, see the [`havingRaw`](#raw-methods) method. @@ -1032,82 +1204,100 @@ To build more advanced `having` statements, see the [`havingRaw`](#raw-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(); +```php +$users = DB::table('users')->skip(10)->take(5)->get(); +``` 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) - ->limit(5) - ->get(); +```php +$users = DB::table('users') + ->offset(10) + ->limit(5) + ->get(); +``` ## Conditional Clauses 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'); +```php +$role = $request->input('role'); - $users = DB::table('users') - ->when($role, function (Builder $query, string $role) { - $query->where('role_id', $role); - }) - ->get(); +$users = DB::table('users') + ->when($role, function (Builder $query, string $role) { + $query->where('role_id', $role); + }) + ->get(); +``` 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 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: - $sortByVotes = $request->boolean('sort_by_votes'); - - $users = DB::table('users') - ->when($sortByVotes, function (Builder $query, bool $sortByVotes) { - $query->orderBy('votes'); - }, function (Builder $query) { - $query->orderBy('name'); - }) - ->get(); +```php +$sortByVotes = $request->boolean('sort_by_votes'); + +$users = DB::table('users') + ->when($sortByVotes, function (Builder $query, bool $sortByVotes) { + $query->orderBy('votes'); + }, function (Builder $query) { + $query->orderBy('name'); + }) + ->get(); +``` ## Insert Statements 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' => 'kayla@example.com', - 'votes' => 0 - ]); +```php +DB::table('users')->insert([ + 'email' => 'kayla@example.com', + 'votes' => 0 +]); +``` 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' => 'picard@example.com', 'votes' => 0], - ['email' => 'janeway@example.com', 'votes' => 0], - ]); +```php +DB::table('users')->insert([ + ['email' => 'picard@example.com', 'votes' => 0], + ['email' => 'janeway@example.com', 'votes' => 0], +]); +``` The `insertOrIgnore` method will ignore errors while inserting records into the database. When using this method, you should be aware that duplicate record errors will be ignored and other types of errors may also be ignored depending on the database engine. For example, `insertOrIgnore` will [bypass MySQL's strict mode](https://dev.mysql.com/doc/refman/en/sql-mode.html#ignore-effect-on-execution): - DB::table('users')->insertOrIgnore([ - ['id' => 1, 'email' => 'sisko@example.com'], - ['id' => 2, 'email' => 'archer@example.com'], - ]); +```php +DB::table('users')->insertOrIgnore([ + ['id' => 1, 'email' => 'sisko@example.com'], + ['id' => 2, 'email' => 'archer@example.com'], +]); +``` The `insertUsing` method will insert new records into the table while using a subquery to determine the data that should be inserted: - DB::table('pruned_users')->insertUsing([ - 'id', 'name', 'email', 'email_verified_at' - ], DB::table('users')->select( - 'id', 'name', 'email', 'email_verified_at' - )->where('updated_at', '<=', now()->subMonth())); +```php +DB::table('pruned_users')->insertUsing([ + 'id', 'name', 'email', 'email_verified_at' +], DB::table('users')->select( + 'id', 'name', 'email', 'email_verified_at' +)->where('updated_at', '<=', now()->subMonth())); +``` #### Auto-Incrementing IDs If the table has an auto-incrementing id, use the `insertGetId` method to insert a record and then retrieve the ID: - $id = DB::table('users')->insertGetId( - ['email' => 'john@example.com', 'votes' => 0] - ); +```php +$id = DB::table('users')->insertGetId( + ['email' => 'john@example.com', 'votes' => 0] +); +``` > [!WARNING] > 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. @@ -1117,14 +1307,16 @@ If the table has an auto-incrementing id, use the `insertGetId` method to insert 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: - DB::table('flights')->upsert( - [ - ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], - ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] - ], - ['departure', 'destination'], - ['price'] - ); +```php +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. @@ -1136,9 +1328,11 @@ In the example above, Laravel will attempt to insert two records. If a record al 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. The `update` method returns the number of affected rows. You may constrain the `update` query using `where` clauses: - $affected = DB::table('users') - ->where('id', 1) - ->update(['votes' => 1]); +```php +$affected = DB::table('users') + ->where('id', 1) + ->update(['votes' => 1]); +``` #### Update or Insert @@ -1147,11 +1341,13 @@ Sometimes you may want to update an existing record in the database or create it 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 cannot be found, a new record will be inserted with the merged attributes of both arguments: - DB::table('users') - ->updateOrInsert( - ['email' => 'john@example.com', 'name' => 'John'], - ['votes' => '2'] - ); +```php +DB::table('users') + ->updateOrInsert( + ['email' => 'john@example.com', 'name' => 'John'], + ['votes' => '2'] + ); +``` You may provide a closure to the `updateOrInsert` method to customize the attributes that are updated or inserted into the database based on the existence of a matching record: @@ -1174,99 +1370,119 @@ DB::table('users')->updateOrInsert( When updating a JSON column, you should use `->` syntax to update the appropriate key in the JSON object. This operation is supported on MariaDB 10.3+, MySQL 5.7+, and PostgreSQL 9.5+: - $affected = DB::table('users') - ->where('id', 1) - ->update(['options->enabled' => true]); +```php +$affected = DB::table('users') + ->where('id', 1) + ->update(['options->enabled' => true]); +``` ### Increment and Decrement 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'); +```php +DB::table('users')->increment('votes'); - DB::table('users')->increment('votes', 5); +DB::table('users')->increment('votes', 5); - DB::table('users')->decrement('votes'); +DB::table('users')->decrement('votes'); - DB::table('users')->decrement('votes', 5); +DB::table('users')->decrement('votes', 5); +``` If needed, you may also specify additional columns to update during the increment or decrement operation: - DB::table('users')->increment('votes', 1, ['name' => 'John']); +```php +DB::table('users')->increment('votes', 1, ['name' => 'John']); +``` In addition, you may increment or decrement multiple columns at once using the `incrementEach` and `decrementEach` methods: - DB::table('users')->incrementEach([ - 'votes' => 5, - 'balance' => 100, - ]); +```php +DB::table('users')->incrementEach([ + 'votes' => 5, + 'balance' => 100, +]); +``` ## Delete Statements The query builder's `delete` method may be used to delete records from the table. The `delete` method returns the number of affected rows. You may constrain `delete` statements by adding "where" clauses before calling the `delete` method: - $deleted = DB::table('users')->delete(); +```php +$deleted = DB::table('users')->delete(); - $deleted = DB::table('users')->where('votes', '>', 100)->delete(); +$deleted = DB::table('users')->where('votes', '>', 100)->delete(); +``` ## Pessimistic Locking 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(); +```php +DB::table('users') + ->where('votes', '>', 100) + ->sharedLock() + ->get(); +``` 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(); +```php +DB::table('users') + ->where('votes', '>', 100) + ->lockForUpdate() + ->get(); +``` While not obligatory, it is recommended to wrap pessimistic locks within a [transaction](/docs/{{version}}/database#database-transactions). This ensures that the data retrieved remains unaltered in the database until the entire operation completes. In case of a failure, the transaction will roll back any changes and release the locks automatically: - DB::transaction(function () { - $sender = DB::table('users') - ->lockForUpdate() - ->find(1); +```php +DB::transaction(function () { + $sender = DB::table('users') + ->lockForUpdate() + ->find(1); - $receiver = DB::table('users') - ->lockForUpdate(); - ->find(2); + $receiver = DB::table('users') + ->lockForUpdate(); + ->find(2); - if ($sender->balance < 100) { - throw new RuntimeException('Balance too low.'); - } - - DB::table('users') - ->where('id', $sender->id) - ->update([ - 'balance' => $sender->balance - 100 - ]); + if ($sender->balance < 100) { + throw new RuntimeException('Balance too low.'); + } + + DB::table('users') + ->where('id', $sender->id) + ->update([ + 'balance' => $sender->balance - 100 + ]); - DB::table('users') - ->where('id', $receiver->id) - ->update([ - 'balance' => $receiver->balance + 100 - ]); - }); + DB::table('users') + ->where('id', $receiver->id) + ->update([ + 'balance' => $receiver->balance + 100 + ]); +}); +``` ## Debugging 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(); +```php +DB::table('users')->where('votes', '>', 100)->dd(); - DB::table('users')->where('votes', '>', 100)->dump(); +DB::table('users')->where('votes', '>', 100)->dump(); +``` The `dumpRawSql` and `ddRawSql` methods may be invoked on a query to dump the query's SQL with all parameter bindings properly substituted: - DB::table('users')->where('votes', '>', 100)->dumpRawSql(); +```php +DB::table('users')->where('votes', '>', 100)->dumpRawSql(); - DB::table('users')->where('votes', '>', 100)->ddRawSql(); +DB::table('users')->where('votes', '>', 100)->ddRawSql(); +``` diff --git a/queues.md b/queues.md index edef55942a1..7f17862d426 100644 --- a/queues.md +++ b/queues.md @@ -74,13 +74,15 @@ Before getting started with Laravel queues, it is important to understand the di 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: - use App\Jobs\ProcessPodcast; +```php +use App\Jobs\ProcessPodcast; - // This job is sent to the default connection's default queue... - ProcessPodcast::dispatch(); +// This job is sent to the default connection's default queue... +ProcessPodcast::dispatch(); - // This job is sent to the default connection's "emails" queue... - ProcessPodcast::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: @@ -114,14 +116,16 @@ In order to use the `redis` queue driver, you should configure a Redis database If your Redis queue connection uses a Redis Cluster, your queue names must contain a [key hash tag](https://redis.io/docs/reference/cluster-spec/#hash-tags). This is required in order to ensure all of the Redis keys for a given queue are placed into the same hash slot: - 'redis' => [ - 'driver' => 'redis', - 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), - 'queue' => env('REDIS_QUEUE', '{default}'), - 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), - 'block_for' => null, - 'after_commit' => false, - ], +```php +'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', '{default}'), + 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, +], +``` **Blocking** @@ -129,14 +133,16 @@ When using the Redis queue, you may use the `block_for` configuration option to Adjusting this value based on your queue load can be more efficient than continually polling the Redis database for new jobs. For instance, you may set the value to `5` to indicate that the driver should block for five seconds while waiting for a job to become available: - 'redis' => [ - 'driver' => 'redis', - 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), - 'queue' => env('REDIS_QUEUE', 'default'), - 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), - 'block_for' => 5, - 'after_commit' => false, - ], +```php +'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => 5, + 'after_commit' => false, +], +``` > [!WARNING] > Setting `block_for` to `0` will cause queue workers to block indefinitely until a job is available. This will also prevent signals such as `SIGTERM` from being handled until the next job has been processed. @@ -177,34 +183,36 @@ The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue` 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: - app->bindMethod([ProcessPodcast::class, 'handle'], function (ProcessPodcast $job, Application $app) { - return $job->handle($app->make(AudioProcessor::class)); - }); +$this->app->bindMethod([ProcessPodcast::class, 'handle'], function (ProcessPodcast $job, Application $app) { + return $job->handle($app->make(AudioProcessor::class)); +}); +``` > [!WARNING] > Binary data, such as raw image contents, should be passed through the `base64_encode` function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue. @@ -235,26 +245,30 @@ Because all loaded Eloquent model relationships also get serialized when a job i Or, 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. - */ - public function __construct( - Podcast $podcast, - ) { - $this->podcast = $podcast->withoutRelations(); - } +```php +/** + * Create a new job instance. + */ +public function __construct( + Podcast $podcast, +) { + $this->podcast = $podcast->withoutRelations(); +} +``` If you are using PHP constructor property promotion and would like to indicate that an Eloquent model should not have its relations serialized, you may use the `WithoutRelations` attribute: - use Illuminate\Queue\Attributes\WithoutRelations; +```php +use Illuminate\Queue\Attributes\WithoutRelations; - /** - * Create a new job instance. - */ - public function __construct( - #[WithoutRelations] - public Podcast $podcast, - ) {} +/** + * Create a new job instance. + */ +public function __construct( + #[WithoutRelations] + public Podcast $podcast, +) {} +``` If a job receives a collection or array of Eloquent models instead of a single model, the models within that collection will not have their relationships restored when the job is deserialized and executed. This is to prevent excessive resource usage on jobs that deal with large numbers of models. @@ -266,50 +280,54 @@ If a job receives a collection or array of Eloquent models instead of a single m 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: - product->id; - } + return $this->product->id; } +} +``` In the example above, the `UpdateSearchIndex` job is unique by a product ID. So, any new dispatches of the job with the same product ID will be ignored until the existing job has completed processing. In addition, if the existing job is not processed within one hour, the unique lock will be released and another job with the same unique key can be dispatched to the queue. @@ -321,37 +339,41 @@ In the example above, the `UpdateSearchIndex` job is unique by a product ID. So, 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 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 that returns the cache driver that should be used: - use Illuminate\Contracts\Cache\Repository; - use Illuminate\Support\Facades\Cache; +```php +use Illuminate\Contracts\Cache\Repository; +use Illuminate\Support\Facades\Cache; - class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique +class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique +{ + ... + + /** + * Get the cache driver for the unique job lock. + */ + public function uniqueVia(): Repository { - ... - - /** - * Get the cache driver for the unique job lock. - */ - public function uniqueVia(): Repository - { - return Cache::driver('redis'); - } + return Cache::driver('redis'); } +} +``` > [!NOTE] > If you only need to limit the concurrent processing of a job, use the [`WithoutOverlapping`](/docs/{{version}}/queues#preventing-job-overlaps) job middleware instead. @@ -361,88 +383,96 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t Laravel allows you to ensure the privacy and integrity of a job's data via [encryption](/docs/{{version}}/encryption). To get started, simply add the `ShouldBeEncrypted` interface to the job class. Once this interface has been added to the class, Laravel will automatically encrypt your job before pushing it onto a queue: - ## Job Middleware 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; +```php +use Illuminate\Support\Facades\Redis; - /** - * Execute the job. - */ - public function handle(): void - { - Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () { - info('Lock obtained...'); +/** + * Execute the job. + */ +public function handle(): void +{ + Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () { + info('Lock obtained...'); - // Handle job... - }, function () { - // Could not obtain lock... + // Handle job... + }, function () { + // Could not obtain lock... - return $this->release(5); - }); - } + return $this->release(5); + }); +} +``` 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 an `app/Jobs/Middleware` directory: - block(0)->allow(1)->every(5) - ->then(function () use ($job, $next) { - // Lock obtained... - - $next($job); - }, function () use ($job) { - // Could not obtain lock... - - $job->release(5); - }); - } + Redis::throttle('key') + ->block(0)->allow(1)->every(5) + ->then(function () use ($job, $next) { + // Lock obtained... + + $next($job); + }, function () use ($job) { + // Could not obtain lock... + + $job->release(5); + }); } +} +``` 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 manually add it to your job class: - use App\Jobs\Middleware\RateLimited; +```php +use App\Jobs\Middleware\RateLimited; - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [new RateLimited]; - } +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [new RateLimited]; +} +``` > [!NOTE] > Job middleware can also be assigned to queueable event listeners, mailables, and notifications. @@ -454,52 +484,60 @@ Although we just demonstrated how to write your own rate limiting job middleware 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; +```php +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Support\Facades\RateLimiter; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - RateLimiter::for('backups', function (object $job) { - return $job->user->vipCustomer() - ? Limit::none() - : Limit::perHour(1)->by($job->user->id); - }); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + RateLimiter::for('backups', function (object $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); +```php +return Limit::perMinute(50)->by($job->user->id); +``` Once you have defined your rate limit, you may attach the rate limiter to your 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; +```php +use Illuminate\Queue\Middleware\RateLimited; - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [new RateLimited('backups')]; - } +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [new RateLimited('backups')]; +} +``` 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. If you do not want a job to be retried when it is rate limited, you may use the `dontRelease` method: - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [(new RateLimited('backups'))->dontRelease()]; - } +```php +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new RateLimited('backups'))->dontRelease()]; +} +``` > [!NOTE] > 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. @@ -511,53 +549,61 @@ Laravel includes an `Illuminate\Queue\Middleware\WithoutOverlapping` middleware 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; +```php +use Illuminate\Queue\Middleware\WithoutOverlapping; - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [new WithoutOverlapping($this->user->id)]; - } +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [new WithoutOverlapping($this->user->id)]; +} +``` Any overlapping jobs of the same type 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. - * - * @return array - */ - public function middleware(): array - { - return [(new WithoutOverlapping($this->order->id))->releaseAfter(60)]; - } +```php +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new WithoutOverlapping($this->order->id))->releaseAfter(60)]; +} +``` 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. - * - * @return array - */ - public function middleware(): array - { - return [(new WithoutOverlapping($this->order->id))->dontRelease()]; - } +```php +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new WithoutOverlapping($this->order->id))->dontRelease()]; +} +``` The `WithoutOverlapping` middleware is powered by Laravel's atomic lock feature. Sometimes, your job may unexpectedly fail or timeout in such a way that the lock is not released. Therefore, you may explicitly define a lock expiration time using the `expireAfter` method. For example, the example below will instruct Laravel to release the `WithoutOverlapping` lock three minutes after the job has started processing: - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; - } +```php +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; +} +``` > [!WARNING] > 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. @@ -602,90 +648,100 @@ Laravel includes a `Illuminate\Queue\Middleware\ThrottlesExceptions` middleware For example, let's imagine a queued job that interacts with a third-party API that begins throwing exceptions. To throttle exceptions, you can return the `ThrottlesExceptions` middleware from your job's `middleware` method. Typically, this middleware should be paired with a job that implements [time based attempts](#time-based-attempts): - use DateTime; - use Illuminate\Queue\Middleware\ThrottlesExceptions; +```php +use DateTime; +use Illuminate\Queue\Middleware\ThrottlesExceptions; - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [new ThrottlesExceptions(10, 5 * 60)]; - } +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [new ThrottlesExceptions(10, 5 * 60)]; +} - /** - * Determine the time at which the job should timeout. - */ - public function retryUntil(): DateTime - { - return now()->addMinutes(30); - } +/** + * Determine the time at which the job should timeout. + */ +public function retryUntil(): DateTime +{ + return now()->addMinutes(30); +} +``` The first constructor argument accepted by the middleware is the number of exceptions the job can throw before being throttled, while the second constructor argument is the number of seconds that should elapse before the job is attempted again once it has been throttled. In the code example above, if the job throws 10 consecutive exceptions, we will wait 5 minutes before attempting the job again, constrained by the 30-minute time limit. When a job throws an exception but the exception threshold has not yet been reached, the job will typically be retried immediately. However, you may specify the number of minutes such a job should be delayed by calling the `backoff` method when attaching the middleware to the job: - use Illuminate\Queue\Middleware\ThrottlesExceptions; +```php +use Illuminate\Queue\Middleware\ThrottlesExceptions; - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [(new ThrottlesExceptions(10, 5 * 60))->backoff(5)]; - } +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new ThrottlesExceptions(10, 5 * 60))->backoff(5)]; +} +``` Internally, this middleware uses Laravel's cache system to implement rate limiting, and the job's class name is utilized as the cache "key". You may override this key by calling the `by` method when attaching the middleware to your job. This may be useful if you have multiple jobs interacting with the same third-party service and you would like them to share a common throttling "bucket": - use Illuminate\Queue\Middleware\ThrottlesExceptions; +```php +use Illuminate\Queue\Middleware\ThrottlesExceptions; - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [(new ThrottlesExceptions(10, 10 * 60))->by('key')]; - } +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new ThrottlesExceptions(10, 10 * 60))->by('key')]; +} +``` By default, this middleware will throttle every exception. You can modify this behaviour by invoking the `when` method when attaching the middleware to your job. The exception will then only be throttled if closure provided to the `when` method returns `true`: - use Illuminate\Http\Client\HttpClientException; - use Illuminate\Queue\Middleware\ThrottlesExceptions; - - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [(new ThrottlesExceptions(10, 10 * 60))->when( - fn (Throwable $throwable) => $throwable instanceof HttpClientException - )]; - } +```php +use Illuminate\Http\Client\HttpClientException; +use Illuminate\Queue\Middleware\ThrottlesExceptions; + +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new ThrottlesExceptions(10, 10 * 60))->when( + fn (Throwable $throwable) => $throwable instanceof HttpClientException + )]; +} +``` If you would like to have the throttled exceptions reported to your application's exception handler, you can do so by invoking the `report` method when attaching the middleware to your job. Optionally, you may provide a closure to the `report` method and the exception will only be reported if the given closure returns `true`: - use Illuminate\Http\Client\HttpClientException; - use Illuminate\Queue\Middleware\ThrottlesExceptions; +```php +use Illuminate\Http\Client\HttpClientException; +use Illuminate\Queue\Middleware\ThrottlesExceptions; - /** - * Get the middleware the job should pass through. - * - * @return array - */ - public function middleware(): array - { - return [(new ThrottlesExceptions(10, 10 * 60))->report( - fn (Throwable $throwable) => $throwable instanceof HttpClientException - )]; - } +/** + * Get the middleware the job should pass through. + * + * @return array + */ +public function middleware(): array +{ + return [(new ThrottlesExceptions(10, 10 * 60))->report( + fn (Throwable $throwable) => $throwable instanceof HttpClientException + )]; +} +``` > [!NOTE] > If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. @@ -695,71 +751,79 @@ If you would like to have the throttled exceptions reported to your application' The `Skip` middleware allows you to specify that a job should be skipped / deleted without needing to modify the job's logic. The `Skip::when` method will delete the job if the given condition evaluates to `true`, while the `Skip::unless` method will delete the job if the condition evaluates to `false`: - use Illuminate\Queue\Middleware\Skip; +```php +use Illuminate\Queue\Middleware\Skip; - /** - * Get the middleware the job should pass through. - */ - public function middleware(): array - { - return [ - Skip::when($someCondition), - ]; - } +/** + * Get the middleware the job should pass through. + */ +public function middleware(): array +{ + return [ + Skip::when($someCondition), + ]; +} +``` You can also pass a `Closure` to the `when` and `unless` methods for more complex conditional evaluation: - use Illuminate\Queue\Middleware\Skip; +```php +use Illuminate\Queue\Middleware\Skip; - /** - * Get the middleware the job should pass through. - */ - public function middleware(): array - { - return [ - Skip::when(function (): bool { - return $this->shouldSkip(); - }), - ]; - } +/** + * Get the middleware the job should pass through. + */ +public function middleware(): array +{ + return [ + Skip::when(function (): bool { + return $this->shouldSkip(); + }), + ]; +} +``` ## Dispatching Jobs Once you have written your job class, you may dispatch it using the `dispatch` method on the job itself. The arguments passed to the `dispatch` method will be given to the job's constructor: - delay(now()->addMinutes(10)); + ProcessPodcast::dispatch($podcast) + ->delay(now()->addMinutes(10)); - return redirect('/podcasts'); - } + return redirect('/podcasts'); } +} +``` In some cases, jobs may have a default delay configured. If you need to bypass this delay and dispatch a job for immediate processing, you may use the `withoutDelay` method: - ProcessPodcast::dispatch($podcast)->withoutDelay(); +```php +ProcessPodcast::dispatch($podcast)->withoutDelay(); +``` > [!WARNING] > The Amazon SQS queue service has a maximum delay time of 15 minutes. @@ -808,50 +876,56 @@ In some cases, jobs may have a default delay configured. If you need to bypass t Alternatively, the `dispatchAfterResponse` method delays dispatching a job until after the HTTP response is sent to the user's browser if your web server is using FastCGI. 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; +```php +use App\Jobs\SendNotification; - SendNotification::dispatchAfterResponse(); +SendNotification::dispatchAfterResponse(); +``` 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; +```php +use App\Mail\WelcomeMessage; +use Illuminate\Support\Facades\Mail; - dispatch(function () { - Mail::to('taylor@example.com')->send(new WelcomeMessage); - })->afterResponse(); +dispatch(function () { + 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 executed immediately within the current process: - ### Jobs & Database Transactions @@ -860,11 +934,13 @@ While it is perfectly fine to dispatch jobs within database transactions, you sh Thankfully, Laravel provides several methods of working around this problem. First, you may set the `after_commit` connection option in your queue connection's configuration array: - 'redis' => [ - 'driver' => 'redis', - // ... - 'after_commit' => true, - ], +```php +'redis' => [ + 'driver' => 'redis', + // ... + 'after_commit' => true, +], +``` When the `after_commit` option is `true`, you may dispatch jobs within database transactions; however, Laravel will wait until the open parent database transactions have been committed before actually dispatching the job. Of course, if no database transactions are currently open, the job will be dispatched immediately. @@ -878,39 +954,47 @@ If a transaction is rolled back due to an exception that occurs during the trans If you do not set the `after_commit` queue connection configuration option to `true`, you may still indicate that a specific job should be dispatched after all open database transactions have been committed. To accomplish this, you may chain the `afterCommit` method onto your dispatch operation: - use App\Jobs\ProcessPodcast; +```php +use App\Jobs\ProcessPodcast; - ProcessPodcast::dispatch($podcast)->afterCommit(); +ProcessPodcast::dispatch($podcast)->afterCommit(); +``` Likewise, if the `after_commit` configuration option is set to `true`, you may indicate that a specific job should be dispatched immediately without waiting for any open database transactions to commit: - ProcessPodcast::dispatch($podcast)->beforeCommit(); +```php +ProcessPodcast::dispatch($podcast)->beforeCommit(); +``` ### 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. 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; +```php +use App\Jobs\OptimizePodcast; +use App\Jobs\ProcessPodcast; +use App\Jobs\ReleasePodcast; +use Illuminate\Support\Facades\Bus; - Bus::chain([ - new ProcessPodcast, - new OptimizePodcast, - new ReleasePodcast, - ])->dispatch(); +Bus::chain([ + new ProcessPodcast, + new OptimizePodcast, + new ReleasePodcast, +])->dispatch(); +``` In addition to chaining job class instances, you may also chain closures: - Bus::chain([ - new ProcessPodcast, - new OptimizePodcast, - function () { - Podcast::update(/* ... */); - }, - ])->dispatch(); +```php +Bus::chain([ + new ProcessPodcast, + new OptimizePodcast, + function () { + Podcast::update(/* ... */); + }, +])->dispatch(); +``` > [!WARNING] > 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. @@ -920,11 +1004,13 @@ In addition to chaining job class instances, you may also chain closures: If you would like to specify the connection and queue that should be used for the chained jobs, you may use the `onConnection` and `onQueue` methods. These methods specify the queue connection and queue name that should be used unless the queued job is explicitly assigned a different connection / queue: - Bus::chain([ - new ProcessPodcast, - new OptimizePodcast, - new ReleasePodcast, - ])->onConnection('redis')->onQueue('podcasts')->dispatch(); +```php +Bus::chain([ + new ProcessPodcast, + new OptimizePodcast, + new ReleasePodcast, +])->onConnection('redis')->onQueue('podcasts')->dispatch(); +``` #### Adding Jobs to the Chain @@ -952,16 +1038,18 @@ public function handle(): void 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; +```php +use Illuminate\Support\Facades\Bus; +use Throwable; - Bus::chain([ - new ProcessPodcast, - new OptimizePodcast, - new ReleasePodcast, - ])->catch(function (Throwable $e) { - // A job within the chain has failed... - })->dispatch(); +Bus::chain([ + new ProcessPodcast, + new OptimizePodcast, + new ReleasePodcast, +])->catch(function (Throwable $e) { + // A job within the chain has failed... +})->dispatch(); +``` > [!WARNING] > Since chain callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within chain callbacks. @@ -974,114 +1062,124 @@ When chaining jobs, you may use the `catch` method to specify a closure that sho By pushing jobs to different queues, you may "categorize" your queued jobs and even prioritize how many workers you assign to various queues. Keep in mind, this does not push jobs to different queue "connections" as defined by your queue configuration file, but only to specific queues within a single connection. To specify the queue, use the `onQueue` method when dispatching the job: - onQueue('processing'); + ProcessPodcast::dispatch($podcast)->onQueue('processing'); - return redirect('/podcasts'); - } + return redirect('/podcasts'); } +} +``` Alternatively, you may specify the job's queue by calling the `onQueue` method within the job's constructor: - onQueue('processing'); - } + $this->onQueue('processing'); } +} +``` #### Dispatching to a Particular Connection If your application interacts with multiple queue connections, you may specify which connection to push a job to using the `onConnection` method: - onConnection('sqs'); + ProcessPodcast::dispatch($podcast)->onConnection('sqs'); - return redirect('/podcasts'); - } + return redirect('/podcasts'); } +} +``` 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'); +```php +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'); - } + $this->onConnection('sqs'); } +} +``` ### Specifying Max Job Attempts / Timeout Values @@ -1101,44 +1199,50 @@ If a job exceeds its maximum number of attempts, it will be considered a "failed 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 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: - use DateTime; +```php +use DateTime; - /** - * Determine the time at which the job should timeout. - */ - public function retryUntil(): DateTime - { - return now()->addMinutes(10); - } +/** + * Determine the time at which the job should timeout. + */ +public function retryUntil(): DateTime +{ + return now()->addMinutes(10); +} +``` > [!NOTE] > You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). @@ -1148,41 +1252,43 @@ As an alternative to defining how many times a job may be attempted before it fa 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: - allow(10)->every(60)->then(function () { - // Lock obtained, process the podcast... - }, function () { - // Unable to obtain lock... - return $this->release(10); - }); - } + Redis::throttle('key')->allow(10)->every(60)->then(function () { + // Lock obtained, process the podcast... + }, function () { + // Unable to obtain lock... + return $this->release(10); + }); } +} +``` In this example, the job is released for ten seconds if the application is unable to obtain a Redis lock and will continue to be retried up to 25 times. However, the job will fail if three unhandled exceptions are thrown by the job. @@ -1201,19 +1307,21 @@ If the job exceeds its maximum attempts by continually timing out, it will be ma 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: - release(); - } + $this->release(); +} +``` By default, the `release` method will release the job back onto the queue for immediate processing. However, you may instruct the queue to not make the job available for processing until a given number of seconds has elapsed by passing an integer or date instance to the `release` method: - $this->release(10); +```php +$this->release(10); - $this->release(now()->addSeconds(10)); +$this->release(now()->addSeconds(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. - */ - public function handle(): void - { - // ... +```php +/** + * Execute the job. + */ +public function handle(): void +{ + // ... - $this->fail(); - } + $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. Or, for convenience, you may pass a string error message which will be converted to an exception for you: - $this->fail($exception); +```php +$this->fail($exception); - $this->fail('Something went wrong.'); +$this->fail('Something went wrong.'); +``` > [!NOTE] > For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). @@ -1300,62 +1416,66 @@ php artisan migrate 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()) { - // Determine if the batch has been cancelled... - - return; - } + if ($this->batch()->cancelled()) { + // Determine if the batch has been cancelled... - // Import a portion of the CSV file... + return; } + + // 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. 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\ImportCsv; - use Illuminate\Bus\Batch; - use Illuminate\Support\Facades\Bus; - use Throwable; - - $batch = Bus::batch([ - new ImportCsv(1, 100), - new ImportCsv(101, 200), - new ImportCsv(201, 300), - new ImportCsv(301, 400), - new ImportCsv(401, 500), - ])->before(function (Batch $batch) { - // The batch has been created but no jobs have been added... - })->progress(function (Batch $batch) { - // A single job has completed successfully... - })->then(function (Batch $batch) { - // All jobs completed successfully... - })->catch(function (Batch $batch, Throwable $e) { - // First batch job failure detected... - })->finally(function (Batch $batch) { - // The batch has finished executing... - })->dispatch(); - - return $batch->id; +```php +use App\Jobs\ImportCsv; +use Illuminate\Bus\Batch; +use Illuminate\Support\Facades\Bus; +use Throwable; + +$batch = Bus::batch([ + new ImportCsv(1, 100), + new ImportCsv(101, 200), + new ImportCsv(201, 300), + new ImportCsv(301, 400), + new ImportCsv(401, 500), +])->before(function (Batch $batch) { + // The batch has been created but no jobs have been added... +})->progress(function (Batch $batch) { + // A single job has completed successfully... +})->then(function (Batch $batch) { + // All jobs completed successfully... +})->catch(function (Batch $batch, Throwable $e) { + // First batch job failure detected... +})->finally(function (Batch $batch) { + // The batch has finished executing... +})->dispatch(); + +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. @@ -1367,97 +1487,109 @@ The batch's ID, which may be accessed via the `$batch->id` property, may be used Some tools such as Laravel Horizon and Laravel Telescope may provide more user-friendly debug information for batches if batches are named. To assign an arbitrary name to a batch, you may call the `name` method while defining the batch: - $batch = Bus::batch([ - // ... - ])->then(function (Batch $batch) { - // All jobs completed successfully... - })->name('Import CSV')->dispatch(); +```php +$batch = Bus::batch([ + // ... +])->then(function (Batch $batch) { + // All jobs completed successfully... +})->name('Import CSV')->dispatch(); +``` #### Batch Connection and 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. 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('imports')->dispatch(); +```php +$batch = Bus::batch([ + // ... +])->then(function (Batch $batch) { + // All jobs completed successfully... +})->onConnection('redis')->onQueue('imports')->dispatch(); +``` ### Chains and Batches 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([ - [ - new ReleasePodcast(1), - new SendPodcastReleaseNotification(1), - ], - [ - new ReleasePodcast(2), - new SendPodcastReleaseNotification(2), - ], - ])->then(function (Batch $batch) { - // ... - })->dispatch(); +```php +use App\Jobs\ReleasePodcast; +use App\Jobs\SendPodcastReleaseNotification; +use Illuminate\Bus\Batch; +use Illuminate\Support\Facades\Bus; + +Bus::batch([ + [ + new ReleasePodcast(1), + new SendPodcastReleaseNotification(1), + ], + [ + new ReleasePodcast(2), + new SendPodcastReleaseNotification(2), + ], +])->then(function (Batch $batch) { + // ... +})->dispatch(); +``` Conversely, you may run batches of jobs within a [chain](#job-chaining) by defining batches within the chain. For example, you could first run a batch of jobs to release multiple podcasts then a batch of jobs to send the release notifications: - use App\Jobs\FlushPodcastCache; - use App\Jobs\ReleasePodcast; - use App\Jobs\SendPodcastReleaseNotification; - use Illuminate\Support\Facades\Bus; - - Bus::chain([ - new FlushPodcastCache, - Bus::batch([ - new ReleasePodcast(1), - new ReleasePodcast(2), - ]), - Bus::batch([ - new SendPodcastReleaseNotification(1), - new SendPodcastReleaseNotification(2), - ]), - ])->dispatch(); +```php +use App\Jobs\FlushPodcastCache; +use App\Jobs\ReleasePodcast; +use App\Jobs\SendPodcastReleaseNotification; +use Illuminate\Support\Facades\Bus; + +Bus::chain([ + new FlushPodcastCache, + Bus::batch([ + new ReleasePodcast(1), + new ReleasePodcast(2), + ]), + Bus::batch([ + new SendPodcastReleaseNotification(1), + new SendPodcastReleaseNotification(2), + ]), +])->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 even more jobs: - $batch = Bus::batch([ - new LoadImportBatch, - new LoadImportBatch, - new LoadImportBatch, - ])->then(function (Batch $batch) { - // All jobs completed successfully... - })->name('Import Contacts')->dispatch(); +```php +$batch = Bus::batch([ + new LoadImportBatch, + new LoadImportBatch, + new LoadImportBatch, +])->then(function (Batch $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 may be accessed via the job's `batch` method: - use App\Jobs\ImportContacts; - use Illuminate\Support\Collection; - - /** - * Execute the job. - */ - public function handle(): void - { - if ($this->batch()->cancelled()) { - return; - } +```php +use App\Jobs\ImportContacts; +use Illuminate\Support\Collection; - $this->batch()->add(Collection::times(1000, function () { - return new ImportContacts; - })); +/** + * Execute the job. + */ +public function handle(): void +{ + if ($this->batch()->cancelled()) { + return; } + $this->batch()->add(Collection::times(1000, function () { + return new ImportContacts; + })); +} +``` + > [!WARNING] > You may only add jobs to a batch from within a job that belongs to the same batch. @@ -1466,35 +1598,37 @@ In this example, we will use the `LoadImportBatch` job to hydrate the batch with 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; +```php +// The UUID of the batch... +$batch->id; - // The name of the batch (if applicable)... - $batch->name; +// The name of the batch (if applicable)... +$batch->name; - // The number of jobs assigned to the batch... - $batch->totalJobs; +// The number of jobs assigned to the batch... +$batch->totalJobs; - // The number of jobs that have not been processed by the queue... - $batch->pendingJobs; +// The number of jobs that have not been processed by the queue... +$batch->pendingJobs; - // The number of jobs that have failed... - $batch->failedJobs; +// The number of jobs that have failed... +$batch->failedJobs; - // The number of jobs that have been processed thus far... - $batch->processedJobs(); +// The number of jobs that have been processed thus far... +$batch->processedJobs(); - // The completion percentage of the batch (0-100)... - $batch->progress(); +// The completion percentage of the batch (0-100)... +$batch->progress(); - // Indicates if the batch has finished executing... - $batch->finished(); +// Indicates if the batch has finished executing... +$batch->finished(); - // Cancel the execution of the batch... - $batch->cancel(); +// Cancel the execution of the batch... +$batch->cancel(); - // Indicates if the batch has been cancelled... - $batch->cancelled(); +// Indicates if the batch has been cancelled... +$batch->cancelled(); +``` #### Returning Batches From Routes @@ -1503,43 +1637,49 @@ All `Illuminate\Bus\Batch` instances are JSON serializable, meaning you can retu 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; +```php +use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Route; - Route::get('/batch/{batchId}', function (string $batchId) { - return Bus::findBatch($batchId); - }); +Route::get('/batch/{batchId}', function (string $batchId) { + return Bus::findBatch($batchId); +}); +``` ### Cancelling Batches Sometimes you may need to cancel a given batch's execution. This can be accomplished by calling the `cancel` method on the `Illuminate\Bus\Batch` instance: - /** - * Execute the job. - */ - public function handle(): void - { - if ($this->user->exceedsImportLimit()) { - return $this->batch()->cancel(); - } +```php +/** + * Execute the job. + */ +public function handle(): void +{ + if ($this->user->exceedsImportLimit()) { + return $this->batch()->cancel(); + } - if ($this->batch()->cancelled()) { - return; - } + if ($this->batch()->cancelled()) { + return; } +} +``` As you may have noticed in the previous examples, batched jobs should typically determine if their corresponding batch has been cancelled before continuing execution. However, for convenience, you may assign the `SkipIfBatchCancelled` [middleware](#job-middleware) to the job instead. As its name indicates, this middleware will instruct Laravel to not process the job if its corresponding batch has been cancelled: - use Illuminate\Queue\Middleware\SkipIfBatchCancelled; +```php +use Illuminate\Queue\Middleware\SkipIfBatchCancelled; - /** - * Get the middleware the job should pass through. - */ - public function middleware(): array - { - return [new SkipIfBatchCancelled]; - } +/** + * Get the middleware the job should pass through. + */ +public function middleware(): array +{ + return [new SkipIfBatchCancelled]; +} +``` ### Batch Failures @@ -1551,11 +1691,13 @@ When a batched job fails, the `catch` callback (if assigned) will be invoked. Th When a job within a batch fails, Laravel will automatically mark the batch as "cancelled". If you wish, you may disable this behavior so that a job failure does not automatically mark the batch as cancelled. This may be accomplished by calling the `allowFailures` method while dispatching the batch: - $batch = Bus::batch([ - // ... - ])->then(function (Batch $batch) { - // All jobs completed successfully... - })->allowFailures()->dispatch(); +```php +$batch = Bus::batch([ + // ... +])->then(function (Batch $batch) { + // All jobs completed successfully... +})->allowFailures()->dispatch(); +``` #### Retrying Failed Batch Jobs @@ -1571,27 +1713,35 @@ php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5 Without pruning, the `job_batches` table can accumulate records very quickly. To mitigate this, you should [schedule](/docs/{{version}}/scheduling) the `queue:prune-batches` Artisan command to run daily: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('queue:prune-batches')->daily(); +Schedule::command('queue:prune-batches')->daily(); +``` By default, all finished batches that are more than 24 hours old will be pruned. You may use the `hours` option when calling the command to determine how long to retain batch data. For example, the following command will delete all batches that finished over 48 hours ago: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('queue:prune-batches --hours=48')->daily(); +Schedule::command('queue:prune-batches --hours=48')->daily(); +``` Sometimes, your `jobs_batches` table may accumulate batch records for batches that never completed successfully, such as batches where a job failed and that job was never retried successfully. You may instruct the `queue:prune-batches` command to prune these unfinished batch records using the `unfinished` option: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('queue:prune-batches --hours=48 --unfinished=72')->daily(); +Schedule::command('queue:prune-batches --hours=48 --unfinished=72')->daily(); +``` Likewise, your `jobs_batches` table may also accumulate batch records for cancelled batches. You may instruct the `queue:prune-batches` command to prune these cancelled batch records using the `cancelled` option: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('queue:prune-batches --hours=48 --cancelled=72')->daily(); +Schedule::command('queue:prune-batches --hours=48 --cancelled=72')->daily(); +``` ### Storing Batches in DynamoDB @@ -1652,21 +1802,25 @@ If you defined your DynamoDB table with a `ttl` attribute, you may define config 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 cannot be modified in transit: - $podcast = App\Podcast::find(1); +```php +$podcast = App\Podcast::find(1); - dispatch(function () use ($podcast) { - $podcast->publish(); - }); +dispatch(function () use ($podcast) { + $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](#max-job-attempts-and-timeout): - use Throwable; +```php +use Throwable; - dispatch(function () use ($podcast) { - $podcast->publish(); - })->catch(function (Throwable $e) { - // This job has failed... - }); +dispatch(function () use ($podcast) { + $podcast->publish(); +})->catch(function (Throwable $e) { + // This job has failed... +}); +``` > [!WARNING] > Since `catch` callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within `catch` callbacks. @@ -1784,7 +1938,9 @@ Daemon queue workers do not "reboot" the framework before processing each job. T 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')); +```php +dispatch((new Job)->onQueue('high')); +``` To start a worker that verifies that all of the `high` queue jobs are processed before continuing to any jobs on the `low` queue, pass a comma-delimited list of queue names to the `work` command: @@ -1917,77 +2073,85 @@ php artisan queue:work redis --tries=3 --backoff=3 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. - * - * @var int - */ - public $backoff = 3; +```php +/** + * The number of seconds to wait before retrying the job. + * + * @var int + */ +public $backoff = 3; +``` 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. - */ - public function backoff(): int - { - return 3; - } +```php +/** + * Calculate the number of seconds to wait before retrying the job. + */ +public function backoff(): int +{ + 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 second for the first retry, 5 seconds for the second retry, 10 seconds for the third retry, and 10 seconds for every subsequent retry if there are more attempts remaining: - /** - * Calculate the number of seconds to wait before retrying the job. - * - * @return array - */ - public function backoff(): array - { - return [1, 5, 10]; - } +```php +/** + * Calculate the number of seconds to wait before retrying the job. + * + * @return array + */ +public function backoff(): array +{ + return [1, 5, 10]; +} +``` ### Cleaning Up After Failed Jobs 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: - [!WARNING] > A new instance of the job is instantiated before invoking the `failed` method; therefore, any class property modifications that may have occurred within the `handle` method will be lost. @@ -2047,12 +2211,14 @@ When injecting an Eloquent model into a job, the model is automatically serializ For convenience, you may choose to automatically delete jobs with missing models by setting your job's `deleteWhenMissingModels` property to `true`. When this property is set to `true`, Laravel will quietly discard the job without raising an exception: - /** - * Delete the job if its models no longer exist. - * - * @var bool - */ - public $deleteWhenMissingModels = true; +```php +/** + * Delete the job if its models no longer exist. + * + * @var bool + */ +public $deleteWhenMissingModels = true; +``` ### Pruning Failed Jobs @@ -2108,36 +2274,38 @@ QUEUE_FAILED_DRIVER=null 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 - }); - } + /** + * Bootstrap any application services. + */ + public function boot(): void + { + Queue::failing(function (JobFailed $event) { + // $event->connectionName + // $event->job + // $event->exception + }); } +} +``` ## Clearing Jobs From Queues @@ -2277,9 +2445,11 @@ class ExampleTest extends TestCase 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: - Queue::assertPushed(function (ShipOrder $job) use ($order) { - return $job->order->id === $order->id; - }); +```php +Queue::assertPushed(function (ShipOrder $job) use ($order) { + return $job->order->id === $order->id; +}); +``` ### Faking a Subset of Jobs @@ -2315,41 +2485,49 @@ public function test_orders_can_be_shipped(): void You may fake all jobs except for a set of specified jobs using the `except` method: - Queue::fake()->except([ - ShipOrder::class, - ]); +```php +Queue::fake()->except([ + ShipOrder::class, +]); +``` ### Testing Job Chains To test job chains, you will need to utilize the `Bus` facade's faking capabilities. 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; +```php +use App\Jobs\RecordShipment; +use App\Jobs\ShipOrder; +use App\Jobs\UpdateInventory; +use Illuminate\Support\Facades\Bus; - Bus::fake(); +Bus::fake(); - // ... +// ... - Bus::assertChained([ - ShipOrder::class, - RecordShipment::class, - UpdateInventory::class - ]); +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, - ]); +```php +Bus::assertChained([ + new ShipOrder, + new RecordShipment, + new UpdateInventory, +]); +``` You may use the `assertDispatchedWithoutChain` method to assert that a job was pushed without a chain of jobs: - Bus::assertDispatchedWithoutChain(ShipOrder::class); +```php +Bus::assertDispatchedWithoutChain(ShipOrder::class); +``` #### Testing Chain Modifications @@ -2379,55 +2557,65 @@ $job->assertDoesntHaveChain(); If your job chain [contains a batch of jobs](#chains-and-batches), you may assert that the chained batch matches your expectations by inserting a `Bus::chainedBatch` definition within your chain assertion: - use App\Jobs\ShipOrder; - use App\Jobs\UpdateInventory; - use Illuminate\Bus\PendingBatch; - use Illuminate\Support\Facades\Bus; - - Bus::assertChained([ - new ShipOrder, - Bus::chainedBatch(function (PendingBatch $batch) { - return $batch->jobs->count() === 3; - }), - new UpdateInventory, - ]); +```php +use App\Jobs\ShipOrder; +use App\Jobs\UpdateInventory; +use Illuminate\Bus\PendingBatch; +use Illuminate\Support\Facades\Bus; + +Bus::assertChained([ + new ShipOrder, + Bus::chainedBatch(function (PendingBatch $batch) { + return $batch->jobs->count() === 3; + }), + new UpdateInventory, +]); +``` ### Testing Job Batches The `Bus` facade's `assertBatched` method may be used to assert that a [batch of jobs](/docs/{{version}}/queues#job-batching) 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; +```php +use Illuminate\Bus\PendingBatch; +use Illuminate\Support\Facades\Bus; - Bus::fake(); +Bus::fake(); - // ... +// ... - Bus::assertBatched(function (PendingBatch $batch) { - return $batch->name == 'import-csv' && - $batch->jobs->count() === 10; - }); +Bus::assertBatched(function (PendingBatch $batch) { + return $batch->name == 'import-csv' && + $batch->jobs->count() === 10; +}); +``` You may use the `assertBatchCount` method to assert that a given number of batches were dispatched: - Bus::assertBatchCount(3); +```php +Bus::assertBatchCount(3); +``` You may use `assertNothingBatched` to assert that no batches were dispatched: - Bus::assertNothingBatched(); +```php +Bus::assertNothingBatched(); +``` #### Testing Job / Batch Interaction In addition, you may occasionally need to test an individual job's interaction with its underlying batch. For example, you may need to test if a job cancelled further processing for its batch. To accomplish this, you need to assign a fake batch to the job via the `withFakeBatch` method. The `withFakeBatch` method returns a tuple containing the job instance and the fake batch: - [$job, $batch] = (new ShipOrder)->withFakeBatch(); +```php +[$job, $batch] = (new ShipOrder)->withFakeBatch(); - $job->handle(); +$job->handle(); - $this->assertTrue($batch->cancelled()); - $this->assertEmpty($batch->added); +$this->assertTrue($batch->cancelled()); +$this->assertEmpty($batch->added); +``` ### Testing Job / Queue Interactions @@ -2457,51 +2645,55 @@ $job->assertNotFailed(); 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: - connectionName - // $event->job - // $event->job->payload() - }); + /** + * Bootstrap any application services. + */ + public function boot(): void + { + Queue::before(function (JobProcessing $event) { + // $event->connectionName + // $event->job + // $event->job->payload() + }); - Queue::after(function (JobProcessed $event) { - // $event->connectionName - // $event->job - // $event->job->payload() - }); - } + Queue::after(function (JobProcessed $event) { + // $event->connectionName + // $event->job + // $event->job->payload() + }); } +} +``` Using the `looping` method on the `Queue` [facade](/docs/{{version}}/facades), you may specify callbacks that execute before the worker attempts to fetch a job from a queue. For example, you might register a closure to rollback any transactions that were left open by a previously failed job: - use Illuminate\Support\Facades\DB; - use Illuminate\Support\Facades\Queue; +```php +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Queue; - Queue::looping(function () { - while (DB::transactionLevel() > 0) { - DB::rollBack(); - } - }); +Queue::looping(function () { + while (DB::transactionLevel() > 0) { + DB::rollBack(); + } +}); +``` diff --git a/rate-limiting.md b/rate-limiting.md index 8492dda2231..414fd162441 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -19,9 +19,11 @@ Laravel includes a simple to use rate limiting abstraction which, in conjunction Typically, the rate limiter utilizes your default application cache as defined by the `default` key within your application's `cache` configuration file. However, you may specify which cache driver the rate limiter should use by defining a `limiter` key within your application's `cache` configuration file: - 'default' => env('CACHE_STORE', 'database'), +```php +'default' => env('CACHE_STORE', 'database'), - 'limiter' => 'redis', +'limiter' => 'redis', +``` ## Basic Usage @@ -30,93 +32,107 @@ The `Illuminate\Support\Facades\RateLimiter` facade may be used to interact with The `attempt` method returns `false` when the callback has no remaining attempts available; otherwise, the `attempt` method will return the callback's result or `true`. The first argument accepted by the `attempt` method is a rate limiter "key", which may be any string of your choosing that represents the action being rate limited: - use Illuminate\Support\Facades\RateLimiter; +```php +use Illuminate\Support\Facades\RateLimiter; - $executed = RateLimiter::attempt( - 'send-message:'.$user->id, - $perMinute = 5, - function() { - // Send message... - } - ); - - if (! $executed) { - return 'Too many messages sent!'; +$executed = RateLimiter::attempt( + 'send-message:'.$user->id, + $perMinute = 5, + function() { + // Send message... } +); + +if (! $executed) { + return 'Too many messages sent!'; +} +``` If necessary, you may provide a fourth argument to the `attempt` method, which is the "decay rate", or the number of seconds until the available attempts are reset. For example, we can modify the example above to allow five attempts every two minutes: - $executed = RateLimiter::attempt( - 'send-message:'.$user->id, - $perTwoMinutes = 5, - function() { - // Send message... - }, - $decayRate = 120, - ); +```php +$executed = RateLimiter::attempt( + 'send-message:'.$user->id, + $perTwoMinutes = 5, + function() { + // Send message... + }, + $decayRate = 120, +); +``` ### Manually Incrementing Attempts If you would like to manually interact with the rate limiter, a variety of other methods are available. For example, you may invoke the `tooManyAttempts` method to determine if a given rate limiter key has exceeded its maximum number of allowed attempts per minute: - use Illuminate\Support\Facades\RateLimiter; +```php +use Illuminate\Support\Facades\RateLimiter; - if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) { - return 'Too many attempts!'; - } +if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) { + return 'Too many attempts!'; +} - RateLimiter::increment('send-message:'.$user->id); +RateLimiter::increment('send-message:'.$user->id); - // Send message... +// Send message... +``` Alternatively, you may use the `remaining` method to retrieve the number of attempts remaining for a given key. If a given key has retries remaining, you may invoke the `increment` method to increment the number of total attempts: - use Illuminate\Support\Facades\RateLimiter; +```php +use Illuminate\Support\Facades\RateLimiter; - if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) { - RateLimiter::increment('send-message:'.$user->id); +if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) { + RateLimiter::increment('send-message:'.$user->id); - // Send message... - } + // Send message... +} +``` If you would like to increment the value for a given rate limiter key by more than one, you may provide the desired amount to the `increment` method: - RateLimiter::increment('send-message:'.$user->id, amount: 5); +```php +RateLimiter::increment('send-message:'.$user->id, amount: 5); +``` #### Determining Limiter Availability When a key has no more attempts left, the `availableIn` method returns the number of seconds remaining until more attempts will be available: - use Illuminate\Support\Facades\RateLimiter; +```php +use Illuminate\Support\Facades\RateLimiter; - if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) { - $seconds = RateLimiter::availableIn('send-message:'.$user->id); +if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) { + $seconds = RateLimiter::availableIn('send-message:'.$user->id); - return 'You may try again in '.$seconds.' seconds.'; - } + return 'You may try again in '.$seconds.' seconds.'; +} - RateLimiter::increment('send-message:'.$user->id); +RateLimiter::increment('send-message:'.$user->id); - // Send message... +// Send message... +``` ### Clearing Attempts You may reset the number of attempts for a given rate limiter key using the `clear` method. For example, you may reset the number of attempts when a given message is read by the receiver: - use App\Models\Message; - use Illuminate\Support\Facades\RateLimiter; +```php +use App\Models\Message; +use Illuminate\Support\Facades\RateLimiter; - /** - * Mark the message as read. - */ - public function read(Message $message): Message - { - $message->markAsRead(); +/** + * Mark the message as read. + */ +public function read(Message $message): Message +{ + $message->markAsRead(); - RateLimiter::clear('send-message:'.$message->user_id); + RateLimiter::clear('send-message:'.$message->user_id); - return $message; - } + return $message; +} +``` diff --git a/redirects.md b/redirects.md index 00f5e814230..a4fbf8772d2 100644 --- a/redirects.md +++ b/redirects.md @@ -10,88 +10,112 @@ 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 () { - return redirect('/home/dashboard'); - }); +```php +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: - Route::post('/user/profile', function () { - // Validate the request... +```php +Route::post('/user/profile', function () { + // Validate the request... - return back()->withInput(); - }); + return back()->withInput(); +}); +``` ## Redirecting To Named Routes When you call the `redirect` helper with no parameters, an instance of `Illuminate\Routing\Redirector` is returned, allowing you to call any method on the `Redirector` instance. For example, to generate a `RedirectResponse` to a named route, you may use the `route` method: - return redirect()->route('login'); +```php +return redirect()->route('login'); +``` 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} +```php +// For a route with the following URI: profile/{id} - return redirect()->route('profile', ['id' => 1]); +return redirect()->route('profile', ['id' => 1]); +``` For convenience, Laravel also offers the global `to_route` function: - return to_route('profile', ['id' => 1]); +```php +return to_route('profile', ['id' => 1]); +``` #### Populating Parameters Via Eloquent Models 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} +```php +// For a route with the following URI: profile/{id} - return redirect()->route('profile', [$user]); +return redirect()->route('profile', [$user]); +``` If you would like to customize the value that is placed in the route parameter, you should override the `getRouteKey` method on your Eloquent model: - /** - * Get the value of the model's route key. - */ - public function getRouteKey(): mixed - { - return $this->slug; - } +```php +/** + * Get the value of the model's route key. + */ +public function getRouteKey(): mixed +{ + return $this->slug; +} +``` ## Redirecting To Controller Actions 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; +```php +use App\Http\Controllers\HomeController; - return redirect()->action([HomeController::class, 'index']); +return redirect()->action([HomeController::class, 'index']); +``` If your controller route requires parameters, you may pass them as the second argument to the `action` method: - return redirect()->action( - [UserController::class, 'profile'], ['id' => 1] - ); +```php +return redirect()->action( + [UserController::class, 'profile'], ['id' => 1] +); +``` ## Redirecting With Flashed Session Data 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... +```php +Route::post('/user/profile', function () { + // Update the user's profile... - return redirect('/dashboard')->with('status', 'Profile updated!'); - }); + 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(); +```php +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')) -
    - {{ session('status') }} -
    - @endif +```blade +@if (session('status')) +
    + {{ session('status') }} +
    +@endif +``` diff --git a/redis.md b/redis.md index 7615f351090..ecd3fde4c57 100644 --- a/redis.md +++ b/redis.md @@ -28,189 +28,209 @@ composer require predis/predis:^2.0 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' => [ +```php +'redis' => [ - 'client' => env('REDIS_CLIENT', 'phpredis'), + 'client' => env('REDIS_CLIENT', 'phpredis'), - 'options' => [ - 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), - ], - - 'default' => [ - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_DB', '0'), - ], + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], - 'cache' => [ - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_CACHE_DB', '1'), - ], + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + ], + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), ], -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' => [ +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: - 'client' => env('REDIS_CLIENT', 'phpredis'), +```php +'redis' => [ - 'options' => [ - 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), - ], + 'client' => env('REDIS_CLIENT', 'phpredis'), - 'default' => [ - 'url' => 'tcp://127.0.0.1:6379?database=0', - ], + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], - 'cache' => [ - 'url' => 'tls://user:password@127.0.0.1:6380?database=1', - ], + 'default' => [ + 'url' => 'tcp://127.0.0.1:6379?database=0', + ], + 'cache' => [ + 'url' => 'tls://user:password@127.0.0.1:6380?database=1', ], +], +``` + #### 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's configuration array: - 'default' => [ - 'scheme' => 'tls', - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_DB', '0'), - ], +```php +'default' => [ + 'scheme' => 'tls', + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), +], +``` ### Clusters 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' => [ +```php +'redis' => [ - 'client' => env('REDIS_CLIENT', 'phpredis'), + 'client' => env('REDIS_CLIENT', 'phpredis'), - 'options' => [ - 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), - ], + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], - 'clusters' => [ - 'default' => [ - [ - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_DB', '0'), - ], + 'clusters' => [ + 'default' => [ + [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), ], ], - - // ... ], + // ... +], +``` + By default, Laravel will use native Redis clustering since the `options.cluster` configuration value is set to `redis`. Redis clustering is a great default option, as it gracefully handles failover. Laravel also supports client-side sharding when using Predis. 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 client-side sharding instead of native Redis clustering, you may remove the `options.cluster` configuration value within your application's `config/database.php` configuration file: - 'redis' => [ - - 'client' => env('REDIS_CLIENT', 'phpredis'), +```php +'redis' => [ - 'clusters' => [ - // ... - ], + 'client' => env('REDIS_CLIENT', 'phpredis'), + 'clusters' => [ // ... ], + // ... +], +``` + ### 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' => [ +```php +'redis' => [ - 'client' => env('REDIS_CLIENT', 'predis'), + 'client' => env('REDIS_CLIENT', 'predis'), - // ... - ], + // ... +], +``` In addition to the default 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' => [ - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_DB', '0'), - 'read_write_timeout' => 60, - ], +```php +'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + 'read_write_timeout' => 60, +], +``` ### PhpRedis 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' => [ +```php +'redis' => [ - 'client' => env('REDIS_CLIENT', 'phpredis'), + 'client' => env('REDIS_CLIENT', 'phpredis'), - // ... - ], + // ... +], +``` In addition to the default configuration options, PhpRedis supports the following additional connection parameters: `name`, `persistent`, `persistent_id`, `prefix`, `read_timeout`, `retry_interval`, `max_retries`, `backoff_algorithm`, `backoff_base`, `backoff_cap`, `timeout`, and `context`. You may add any of these options to your Redis server configuration in the `config/database.php` configuration file: - 'default' => [ - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_DB', '0'), - 'read_timeout' => 60, - 'context' => [ - // 'auth' => ['username', 'secret'], - // 'stream' => ['verify_peer' => false], - ], +```php +'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + 'read_timeout' => 60, + 'context' => [ + // 'auth' => ['username', 'secret'], + // 'stream' => ['verify_peer' => false], ], +], +``` #### PhpRedis Serialization and Compression The PhpRedis extension may also be configured to use a variety of serializers and compression algorithms. These algorithms can be configured via the `options` array of your Redis configuration: - 'redis' => [ +```php +'redis' => [ - 'client' => env('REDIS_CLIENT', 'phpredis'), + 'client' => env('REDIS_CLIENT', 'phpredis'), - 'options' => [ - 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), - 'serializer' => Redis::SERIALIZER_MSGPACK, - 'compression' => Redis::COMPRESSION_LZ4, - ], - - // ... + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + 'serializer' => Redis::SERIALIZER_MSGPACK, + 'compression' => Redis::COMPRESSION_LZ4, ], + // ... +], +``` + Currently supported serializers include: `Redis::SERIALIZER_NONE` (default), `Redis::SERIALIZER_PHP`, `Redis::SERIALIZER_JSON`, `Redis::SERIALIZER_IGBINARY`, and `Redis::SERIALIZER_MSGPACK`. Supported compression algorithms include: `Redis::COMPRESSION_NONE` (default), `Redis::COMPRESSION_LZF`, `Redis::COMPRESSION_ZSTD`, and `Redis::COMPRESSION_LZ4`. @@ -220,62 +240,74 @@ Supported compression algorithms include: `Redis::COMPRESSION_NONE` (default), ` You may interact with Redis by calling various methods on the `Redis` [facade](/docs/{{version}}/facades). The `Redis` facade supports dynamic methods, meaning you may call any [Redis command](https://redis.io/commands) on the facade and the command will be passed directly to Redis. In this example, we will call the Redis `GET` command by calling the `get` method on the `Redis` facade: - Redis::get('user:profile:'.$id) - ]); - } + return view('user.profile', [ + 'user' => Redis::get('user:profile:'.$id) + ]); } +} +``` 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; +```php +use Illuminate\Support\Facades\Redis; - Redis::set('name', 'Taylor'); +Redis::set('name', 'Taylor'); - $values = Redis::lrange('names', 5, 10); +$values = Redis::lrange('names', 5, 10); +``` 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]); +```php +$values = Redis::command('lrange', ['name', 5, 10]); +``` #### Using Multiple Redis Connections 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'); +```php +$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(); +```php +$redis = Redis::connection(); +``` ### Transactions 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 Redis; - use Illuminate\Support\Facades; +```php +use Redis; +use Illuminate\Support\Facades; - Facades\Redis::transaction(function (Redis $redis) { - $redis->incr('user_visits', 1); - $redis->incr('total_visits', 1); - }); +Facades\Redis::transaction(function (Redis $redis) { + $redis->incr('user_visits', 1); + $redis->incr('total_visits', 1); +}); +``` > [!WARNING] > 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. @@ -288,15 +320,17 @@ The `eval` method can be a bit scary at first, but we'll explore a basic example 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]) +```php +$value = Redis::eval(<<<'LUA' + local counter = redis.call("incr", KEYS[1]) - if counter > 5 then - redis.call("incr", KEYS[2]) - end + if counter > 5 then + redis.call("incr", KEYS[2]) + end - return counter - LUA, 2, 'first-counter', 'second-counter'); + return counter +LUA, 2, 'first-counter', 'second-counter'); +``` > [!WARNING] > Please consult the [Redis documentation](https://redis.io/commands/eval) for more information on Redis scripting. @@ -306,14 +340,16 @@ In this example, we will increment a counter, inspect its new value, and increme 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 Redis; - use Illuminate\Support\Facades; +```php +use Redis; +use Illuminate\Support\Facades; - Facades\Redis::pipeline(function (Redis $pipe) { - for ($i = 0; $i < 1000; $i++) { - $pipe->set("key:$i", $i); - } - }); +Facades\Redis::pipeline(function (Redis $pipe) { + for ($i = 0; $i < 1000; $i++) { + $pipe->set("key:$i", $i); + } +}); +``` ## Pub / Sub @@ -322,61 +358,67 @@ Laravel provides a convenient interface to the Redis `publish` and `subscribe` c First, let's setup a channel listener using the `subscribe` method. We'll place this method call within an [Artisan command](/docs/{{version}}/artisan) since calling the `subscribe` method begins a long-running process: - 'Adam Wathan' - ])); - }); + 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 closure: - Redis::psubscribe(['*'], function (string $message, string $channel) { - echo $message; - }); +```php +Redis::psubscribe(['*'], function (string $message, string $channel) { + echo $message; +}); - Redis::psubscribe(['users.*'], function (string $message, string $channel) { - echo $message; - }); +Redis::psubscribe(['users.*'], function (string $message, string $channel) { + echo $message; +}); +``` diff --git a/requests.md b/requests.md index 4b4cc9e412d..97c77b2e10a 100644 --- a/requests.md +++ b/requests.md @@ -34,66 +34,74 @@ Laravel's `Illuminate\Http\Request` class provides an object-oriented way to int 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): - input('name'); + $name = $request->input('name'); - // Store the user... + // Store the user... - return redirect('/users'); - } + return redirect('/users'); } +} +``` As mentioned, 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; +```php +use Illuminate\Http\Request; - Route::get('/', function (Request $request) { - // ... - }); +Route::get('/', function (Request $request) { + // ... +}); +``` #### Dependency Injection and Route Parameters If your controller method is also expecting input from a route parameter you should list your route parameters after your other dependencies. For example, if your route is defined like so: - use App\Http\Controllers\UserController; +```php +use App\Http\Controllers\UserController; - Route::put('/user/{id}', [UserController::class, 'update']); +Route::put('/user/{id}', [UserController::class, 'update']); +``` You may still type-hint the `Illuminate\Http\Request` and access your `id` route parameter by defining your controller method as follows: - ### Request Path, Host, and Method @@ -105,35 +113,45 @@ The `Illuminate\Http\Request` instance provides a variety of methods for examini 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(); +```php +$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/*')) { - // ... - } +```php +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.*')) { - // ... - } +```php +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: - $url = $request->url(); +```php +$url = $request->url(); - $urlWithQueryString = $request->fullUrl(); +$urlWithQueryString = $request->fullUrl(); +``` If you would like to append query string data to the current URL, you may call the `fullUrlWithQuery` method. This method merges the given array of query string variables with the current query string: - $request->fullUrlWithQuery(['type' => 'phone']); +```php +$request->fullUrlWithQuery(['type' => 'phone']); +``` If you would like to get the current URL without a given query string parameter, you may utilize the `fullUrlWithoutQuery` method: @@ -146,50 +164,64 @@ $request->fullUrlWithoutQuery(['type']); You may retrieve the "host" of the incoming request via the `host`, `httpHost`, and `schemeAndHttpHost` methods: - $request->host(); - $request->httpHost(); - $request->schemeAndHttpHost(); +```php +$request->host(); +$request->httpHost(); +$request->schemeAndHttpHost(); +``` #### Retrieving the Request Method The `method` method will return the HTTP verb for the request. You may use the `isMethod` method to verify that the HTTP verb matches a given string: - $method = $request->method(); +```php +$method = $request->method(); - if ($request->isMethod('post')) { - // ... - } +if ($request->isMethod('post')) { + // ... +} +``` ### 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'); +```php +$value = $request->header('X-Header-Name'); - $value = $request->header('X-Header-Name', 'default'); +$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')) { - // ... - } +```php +if ($request->hasHeader('X-Header-Name')) { + // ... +} +``` For convenience, the `bearerToken` method may be used to retrieve a bearer token from the `Authorization` header. If no such header is present, an empty string will be returned: - $token = $request->bearerToken(); +```php +$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(); +```php +$ipAddress = $request->ip(); +``` If you would like to retrieve an array of IP addresses, including all of the client IP addresses that were forwarded by proxies, you may use the `ips` method. The "original" client IP address will be at the end of the array: - $ipAddresses = $request->ips(); +```php +$ipAddresses = $request->ips(); +``` In general, IP addresses should be considered untrusted, user-controlled input and be used for informational purposes only. @@ -198,23 +230,31 @@ In general, IP addresses should be considered untrusted, user-controlled input a 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(); +```php +$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'])) { - // ... - } +```php +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']); +```php +$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()) { - // ... - } +```php +if ($request->expectsJson()) { + // ... +} +``` ### PSR-7 Requests @@ -228,11 +268,13 @@ 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 +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. @@ -248,92 +290,124 @@ Once you have installed these libraries, you may obtain a PSR-7 request by type- 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(); +```php +$input = $request->all(); +``` Using the `collect` method, you may retrieve all of the incoming request's input data as a [collection](/docs/{{version}}/collections): - $input = $request->collect(); +```php +$input = $request->collect(); +``` The `collect` method also allows you to retrieve a subset of the incoming request's input as a collection: - $request->collect('users')->each(function (string $user) { - // ... - }); +```php +$request->collect('users')->each(function (string $user) { + // ... +}); +``` #### Retrieving an Input Value Using a few simple methods, you may access all of the user input from your `Illuminate\Http\Request` instance without worrying about which HTTP verb was used for the request. Regardless of the HTTP verb, the `input` method may be used to retrieve user input: - $name = $request->input('name'); +```php +$name = $request->input('name'); +``` You may pass a default value as the second argument to the `input` method. This value will be returned if the requested input value is not present on the request: - $name = $request->input('name', 'Sally'); +```php +$name = $request->input('name', 'Sally'); +``` When working with forms that contain array inputs, use "dot" notation to access the arrays: - $name = $request->input('products.0.name'); +```php +$name = $request->input('products.0.name'); - $names = $request->input('products.*.name'); +$names = $request->input('products.*.name'); +``` You may call the `input` method without any arguments in order to retrieve all of the input values as an associative array: - $input = $request->input(); +```php +$input = $request->input(); +``` #### Retrieving Input From the Query String While the `input` method retrieves values from the entire request payload (including the query string), the `query` method will only retrieve values from the query string: - $name = $request->query('name'); +```php +$name = $request->query('name'); +``` If the requested query string value data is not present, the second argument to this method will be returned: - $name = $request->query('name', 'Helen'); +```php +$name = $request->query('name', 'Helen'); +``` You may call the `query` method without any arguments in order to retrieve all of the query string values as an associative array: - $query = $request->query(); +```php +$query = $request->query(); +``` #### 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 retrieve values that are nested within JSON arrays / objects: - $name = $request->input('user.name'); +```php +$name = $request->input('user.name'); +``` #### Retrieving Stringable Input Values Instead of retrieving the request's input data as a primitive `string`, you may use the `string` method to retrieve the request data as an instance of [`Illuminate\Support\Stringable`](/docs/{{version}}/strings): - $name = $request->string('name')->trim(); +```php +$name = $request->string('name')->trim(); +``` #### Retrieving Integer Input Values To retrieve input values as integers, you may use the `integer` method. This method will attempt to cast the input value to an integer. If the input is not present or the cast fails, it will return the default value you specify. This is particularly useful for pagination or other numeric inputs: - $perPage = $request->integer('per_page'); +```php +$perPage = $request->integer('per_page'); +``` #### Retrieving Boolean Input Values When dealing with HTML elements like checkboxes, your application may receive "truthy" values that are actually strings. For example, "true" or "on". For convenience, you may use the `boolean` method to retrieve these values as booleans. The `boolean` method returns `true` for 1, "1", true, "true", "on", and "yes". All other values will return `false`: - $archived = $request->boolean('archived'); +```php +$archived = $request->boolean('archived'); +``` #### Retrieving Date Input Values For convenience, input values containing dates / times may be retrieved as Carbon instances using the `date` method. If the request does not contain an input value with the given name, `null` will be returned: - $birthday = $request->date('birthday'); +```php +$birthday = $request->date('birthday'); +``` The second and third arguments accepted by the `date` method may be used to specify the date's format and timezone, respectively: - $elapsed = $request->date('elapsed', '!H:i', 'Europe/Madrid'); +```php +$elapsed = $request->date('elapsed', '!H:i', 'Europe/Madrid'); +``` If the input value is present but has an invalid format, an `InvalidArgumentException` will be thrown; therefore, it is recommended that you validate the input before invoking the `date` method. @@ -342,22 +416,28 @@ If the input value is present but has an invalid format, an `InvalidArgumentExce Input values that correspond to [PHP enums](https://www.php.net/manual/en/language.types.enumerations.php) may also be retrieved from the request. If the request does not contain an input value with the given name or the enum does not have a backing value that matches the input value, `null` will be returned. The `enum` method accepts the name of the input value and the enum class as its first and second arguments: - use App\Enums\Status; +```php +use App\Enums\Status; - $status = $request->enum('status', Status::class); +$status = $request->enum('status', Status::class); +``` If the input value is an array of values that correspond to a PHP enum, you may use the `enums` method to retrieve the array of values as enum instances: - use App\Enums\Product; +```php +use App\Enums\Product; - $products = $request->enums('products', Product::class); +$products = $request->enums('products', Product::class); +``` #### 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; +```php +$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. @@ -366,13 +446,15 @@ When using dynamic properties, Laravel will first look for the parameter's value If you need to retrieve a subset of the input data, you may use the `only` and `except` methods. Both of these methods accept a single `array` or a dynamic list of arguments: - $input = $request->only(['username', 'password']); +```php +$input = $request->only(['username', 'password']); - $input = $request->only('username', 'password'); +$input = $request->only('username', 'password'); - $input = $request->except(['credit_card']); +$input = $request->except(['credit_card']); - $input = $request->except('credit_card'); +$input = $request->except('credit_card'); +``` > [!WARNING] > 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. @@ -382,96 +464,124 @@ If you need to retrieve a subset of the input data, you may use the `only` and ` 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')) { - // ... - } +```php +if ($request->has('name')) { + // ... +} +``` When given an array, the `has` method will determine if all of the specified values are present: - if ($request->has(['name', 'email'])) { - // ... - } +```php +if ($request->has(['name', 'email'])) { + // ... +} +``` The `hasAny` method returns `true` if any of the specified values are present: - if ($request->hasAny(['name', 'email'])) { - // ... - } +```php +if ($request->hasAny(['name', 'email'])) { + // ... +} +``` The `whenHas` method will execute the given closure if a value is present on the request: - $request->whenHas('name', function (string $input) { - // ... - }); +```php +$request->whenHas('name', function (string $input) { + // ... +}); +``` A second closure may be passed to the `whenHas` method that will be executed if the specified value is not present on the request: - $request->whenHas('name', function (string $input) { - // The "name" value is present... - }, function () { - // The "name" value is not present... - }); +```php +$request->whenHas('name', function (string $input) { + // The "name" value is present... +}, function () { + // The "name" value is not present... +}); +``` If you would like to determine if a value is present on the request and is not an empty string, you may use the `filled` method: - if ($request->filled('name')) { - // ... - } +```php +if ($request->filled('name')) { + // ... +} +``` If you would like to determine if a value is missing from the request or is an empty string, you may use the `isNotFilled` method: - if ($request->isNotFilled('name')) { - // ... - } +```php +if ($request->isNotFilled('name')) { + // ... +} +``` When given an array, the `isNotFilled` method will determine if all of the specified values are missing or empty: - if ($request->isNotFilled(['name', 'email'])) { - // ... - } +```php +if ($request->isNotFilled(['name', 'email'])) { + // ... +} +``` The `anyFilled` method returns `true` if any of the specified values is not an empty string: - if ($request->anyFilled(['name', 'email'])) { - // ... - } +```php +if ($request->anyFilled(['name', 'email'])) { + // ... +} +``` The `whenFilled` method will execute the given closure if a value is present on the request and is not an empty string: - $request->whenFilled('name', function (string $input) { - // ... - }); +```php +$request->whenFilled('name', function (string $input) { + // ... +}); +``` A second closure may be passed to the `whenFilled` method that will be executed if the specified value is not "filled": - $request->whenFilled('name', function (string $input) { - // The "name" value is filled... - }, function () { - // The "name" value is not filled... - }); +```php +$request->whenFilled('name', function (string $input) { + // The "name" value is filled... +}, function () { + // The "name" value is not filled... +}); +``` To determine if a given key is absent from the request, you may use the `missing` and `whenMissing` methods: - if ($request->missing('name')) { - // ... - } - - $request->whenMissing('name', function () { - // The "name" value is missing... - }, function () { - // The "name" value is present... - }); +```php +if ($request->missing('name')) { + // ... +} + +$request->whenMissing('name', function () { + // The "name" value is missing... +}, function () { + // The "name" value is present... +}); +``` ### Merging Additional Input Sometimes you may need to manually merge additional input into the request's existing input data. To accomplish this, you may use the `merge` method. If a given input key already exists on the request, it will be overwritten by the data provided to the `merge` method: - $request->merge(['votes' => 0]); +```php +$request->merge(['votes' => 0]); +``` The `mergeIfMissing` method may be used to merge input into the request if the corresponding keys do not already exist within the request's input data: - $request->mergeIfMissing(['votes' => 0]); +```php +$request->mergeIfMissing(['votes' => 0]); +``` ### Old Input @@ -483,37 +593,47 @@ Laravel allows you to keep input from one request during the next request. This The `flash` method on the `Illuminate\Http\Request` class will flash the current input to the [session](/docs/{{version}}/session) so that it is available during the user's next request to the application: - $request->flash(); +```php +$request->flash(); +``` You may also use the `flashOnly` and `flashExcept` methods to flash a subset of the request data to the session. These methods are useful for keeping sensitive information such as passwords out of the session: - $request->flashOnly(['username', 'email']); +```php +$request->flashOnly(['username', 'email']); - $request->flashExcept('password'); +$request->flashExcept('password'); +``` #### Flashing Input Then Redirecting Since you often will want to flash input to the session and then redirect to the previous page, you may easily chain input flashing onto a redirect using the `withInput` method: - return redirect('/form')->withInput(); +```php +return redirect('/form')->withInput(); - return redirect()->route('user.create')->withInput(); +return redirect()->route('user.create')->withInput(); - return redirect('/form')->withInput( - $request->except('password') - ); +return redirect('/form')->withInput( + $request->except('password') +); +``` #### Retrieving Old Input 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'); +```php +$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 to repopulate the form. If no old input exists for the given field, `null` will be returned: - +```blade + +``` ### Cookies @@ -523,7 +643,9 @@ Laravel also provides a global `old` helper. If you are displaying old input wit All cookies created by the Laravel framework are encrypted and signed with an authentication code, meaning they will be considered invalid if they have been changed by the client. To retrieve a cookie value from the request, use the `cookie` method on an `Illuminate\Http\Request` instance: - $value = $request->cookie('name'); +```php +$value = $request->cookie('name'); +``` ## Input Trimming and Normalization @@ -534,27 +656,31 @@ By default, Laravel includes the `Illuminate\Foundation\Http\Middleware\TrimStri If you would like to disable this behavior for all requests, you may remove the two middleware from your application's middleware stack by invoking the `$middleware->remove` method in your application's `bootstrap/app.php` file: - use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; - use Illuminate\Foundation\Http\Middleware\TrimStrings; - - ->withMiddleware(function (Middleware $middleware) { - $middleware->remove([ - ConvertEmptyStringsToNull::class, - TrimStrings::class, - ]); - }) +```php +use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; +use Illuminate\Foundation\Http\Middleware\TrimStrings; + +->withMiddleware(function (Middleware $middleware) { + $middleware->remove([ + ConvertEmptyStringsToNull::class, + TrimStrings::class, + ]); +}) +``` If you would like to disable string trimming and empty string conversion for a subset of requests to your application, you may use the `trimStrings` and `convertEmptyStringsToNull` middleware methods within your application's `bootstrap/app.php` file. Both methods accept an array of closures, which should return `true` or `false` to indicate whether input normalization should be skipped: - ->withMiddleware(function (Middleware $middleware) { - $middleware->convertEmptyStringsToNull(except: [ - fn (Request $request) => $request->is('admin/*'), - ]); - - $middleware->trimStrings(except: [ - fn (Request $request) => $request->is('admin/*'), - ]); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->convertEmptyStringsToNull(except: [ + fn (Request $request) => $request->is('admin/*'), + ]); + + $middleware->trimStrings(except: [ + fn (Request $request) => $request->is('admin/*'), + ]); +}) +``` ## Files @@ -564,33 +690,41 @@ If you would like to disable string trimming and empty string conversion for a s 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'); +```php +$file = $request->file('photo'); - $file = $request->photo; +$file = $request->photo; +``` You may determine if a file is present on the request using the `hasFile` method: - if ($request->hasFile('photo')) { - // ... - } +```php +if ($request->hasFile('photo')) { + // ... +} +``` #### Validating Successful Uploads In addition to checking if the file is present, you may verify that there were no problems uploading the file via the `isValid` method: - if ($request->file('photo')->isValid()) { - // ... - } +```php +if ($request->file('photo')->isValid()) { + // ... +} +``` #### File Paths and Extensions The `UploadedFile` class also contains methods for accessing the file's fully-qualified path and its extension. The `extension` method will attempt to guess the file's extension based on its contents. This extension may be different from the extension that was supplied by the client: - $path = $request->photo->path(); +```php +$path = $request->photo->path(); - $extension = $request->photo->extension(); +$extension = $request->photo->extension(); +``` #### Other File Methods @@ -606,15 +740,19 @@ The `store` method accepts the path where the file should be stored relative to 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: - $path = $request->photo->store('images'); +```php +$path = $request->photo->store('images'); - $path = $request->photo->store('images', 's3'); +$path = $request->photo->store('images', 's3'); +``` 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'); +```php +$path = $request->photo->storeAs('images', 'filename.jpg'); - $path = $request->photo->storeAs('images', 'filename.jpg', 's3'); +$path = $request->photo->storeAs('images', 'filename.jpg', 's3'); +``` > [!NOTE] > For more information about file storage in Laravel, check out the complete [file storage documentation](/docs/{{version}}/filesystem). @@ -626,23 +764,27 @@ When running your applications behind a load balancer that terminates TLS / SSL To solve this, you may enable the `Illuminate\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 specified using the `trustProxies` middleware method in your application's `bootstrap/app.php` file: - ->withMiddleware(function (Middleware $middleware) { - $middleware->trustProxies(at: [ - '192.168.1.1', - '10.0.0.0/8', - ]); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(at: [ + '192.168.1.1', + '10.0.0.0/8', + ]); +}) +``` In addition to configuring the trusted proxies, you may also configure the proxy headers that should be trusted: - ->withMiddleware(function (Middleware $middleware) { - $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR | - Request::HEADER_X_FORWARDED_HOST | - Request::HEADER_X_FORWARDED_PORT | - Request::HEADER_X_FORWARDED_PROTO | - Request::HEADER_X_FORWARDED_AWS_ELB - ); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB + ); +}) +``` > [!NOTE] > If you are using AWS Elastic Load Balancing, the `headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. If your load balancer uses the standard `Forwarded` header from [RFC 7239](https://www.rfc-editor.org/rfc/rfc7239#section-4), the `headers` value should be `Request::HEADER_FORWARDED`. For more information on the constants that may be used in the `headers` value, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/7.0/deployment/proxies.html). @@ -652,9 +794,11 @@ In addition to configuring the trusted proxies, you may also configure the proxy If you are using Amazon AWS or another "cloud" load balancer provider, you may not know the IP addresses of your actual balancers. In this case, you may use `*` to trust all proxies: - ->withMiddleware(function (Middleware $middleware) { - $middleware->trustProxies(at: '*'); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(at: '*'); +}) +``` ## Configuring Trusted Hosts @@ -665,18 +809,24 @@ Typically, you should configure your web server, such as Nginx or Apache, to onl To enable the `TrustHosts` middleware, you should invoke the `trustHosts` middleware method in your application's `bootstrap/app.php` file. Using the `at` argument of this method, you may specify the hostnames that your application should respond to. Incoming requests with other `Host` headers will be rejected: - ->withMiddleware(function (Middleware $middleware) { - $middleware->trustHosts(at: ['laravel.test']); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->trustHosts(at: ['laravel.test']); +}) +``` By default, requests coming from subdomains of the application's URL are also automatically trusted. If you would like to disable this behavior, you may use the `subdomains` argument: - ->withMiddleware(function (Middleware $middleware) { - $middleware->trustHosts(at: ['laravel.test'], subdomains: false); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->trustHosts(at: ['laravel.test'], subdomains: false); +}) +``` If you need to access your application's configuration files or database to determine your trusted hosts, you may provide a closure to the `at` argument: - ->withMiddleware(function (Middleware $middleware) { - $middleware->trustHosts(at: fn () => config('app.trusted_hosts')); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->trustHosts(at: fn () => config('app.trusted_hosts')); +}) +``` diff --git a/responses.md b/responses.md index 68ef7b3bcdc..98e0948a185 100644 --- a/responses.md +++ b/responses.md @@ -25,15 +25,19 @@ All routes and controllers should return a response to be sent back to the user's browser. Laravel provides several different ways to return responses. The most basic response is returning a string from a route or controller. The framework will automatically convert the string into a full HTTP response: - Route::get('/', function () { - return 'Hello World'; - }); +```php +Route::get('/', function () { + return 'Hello World'; +}); +``` In addition to returning strings from your routes and controllers, you may also return arrays. The framework will automatically convert the array into a JSON response: - Route::get('/', function () { - return [1, 2, 3]; - }); +```php +Route::get('/', function () { + return [1, 2, 3]; +}); +``` > [!NOTE] > Did you know you can also return [Eloquent collections](/docs/{{version}}/eloquent-collections) from your routes or controllers? They will automatically be converted to JSON. Give it a shot! @@ -45,204 +49,252 @@ 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 () { - return response('Hello World', 200) - ->header('Content-Type', 'text/plain'); - }); +```php +Route::get('/home', function () { + return response('Hello World', 200) + ->header('Content-Type', 'text/plain'); +}); +``` #### Eloquent Models and 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; +```php +use App\Models\User; - Route::get('/user/{user}', function (User $user) { - return $user; - }); +Route::get('/user/{user}', function (User $user) { + return $user; +}); +``` ### 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: - return response($content) - ->header('Content-Type', $type) - ->header('X-Header-One', 'Header Value') - ->header('X-Header-Two', 'Header Value'); +```php +return response($content) + ->header('Content-Type', $type) + ->header('X-Header-One', 'Header Value') + ->header('X-Header-Two', 'Header Value'); +``` Or, you may use the `withHeaders` method to specify an array of headers to be added to the response: - return response($content) - ->withHeaders([ - 'Content-Type' => $type, - 'X-Header-One' => 'Header Value', - 'X-Header-Two' => 'Header Value', - ]); +```php +return response($content) + ->withHeaders([ + 'Content-Type' => $type, + 'X-Header-One' => 'Header Value', + 'X-Header-Two' => 'Header Value', + ]); +``` #### Cache Control Middleware Laravel includes a `cache.headers` middleware, which may be used to quickly set the `Cache-Control` header for a group of routes. Directives should be provided using the "snake case" equivalent of the corresponding cache-control directive and should be separated by a semicolon. 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 () { - // ... - }); +```php +Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () { + Route::get('/privacy', function () { + // ... + }); - Route::get('/terms', function () { - // ... - }); + Route::get('/terms', function () { + // ... }); +}); +``` ### 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 the number of minutes the cookie should be considered valid to this method: - return response('Hello World')->cookie( - 'name', 'value', $minutes - ); +```php +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 - ); +```php +return response('Hello World')->cookie( + 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly +); +``` 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: - use Illuminate\Support\Facades\Cookie; +```php +use Illuminate\Support\Facades\Cookie; - Cookie::queue('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 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); +```php +$cookie = cookie('name', 'value', $minutes); - return response('Hello World')->cookie($cookie); +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'); +```php +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 `expire` method to expire a cookie: - Cookie::expire('name'); +```php +Cookie::expire('name'); +``` ### Cookies and Encryption By default, thanks to the `Illuminate\Cookie\Middleware\EncryptCookies` middleware, 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 `encryptCookies` method in your application's `bootstrap/app.php` file: - ->withMiddleware(function (Middleware $middleware) { - $middleware->encryptCookies(except: [ - 'cookie_name', - ]); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->encryptCookies(except: [ + 'cookie_name', + ]); +}) +``` ## Redirects 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 () { - return redirect('/home/dashboard'); - }); +```php +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: - Route::post('/user/profile', function () { - // Validate the request... +```php +Route::post('/user/profile', function () { + // Validate the request... - return back()->withInput(); - }); + return back()->withInput(); +}); +``` ### Redirecting to Named Routes When you call the `redirect` helper with no parameters, an instance of `Illuminate\Routing\Redirector` is returned, allowing you to call any method on the `Redirector` instance. For example, to generate a `RedirectResponse` to a named route, you may use the `route` method: - return redirect()->route('login'); +```php +return redirect()->route('login'); +``` 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} +```php +// For a route with the following URI: /profile/{id} - return redirect()->route('profile', ['id' => 1]); +return redirect()->route('profile', ['id' => 1]); +``` #### Populating Parameters via Eloquent Models 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} +```php +// For a route with the following URI: /profile/{id} - return redirect()->route('profile', [$user]); +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: - /** - * Get the value of the model's route key. - */ - public function getRouteKey(): mixed - { - return $this->slug; - } +```php +/** + * Get the value of the model's route key. + */ +public function getRouteKey(): mixed +{ + return $this->slug; +} +``` ### Redirecting to Controller Actions 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\UserController; +```php +use App\Http\Controllers\UserController; - return redirect()->action([UserController::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: - return redirect()->action( - [UserController::class, 'profile'], ['id' => 1] - ); +```php +return redirect()->action( + [UserController::class, 'profile'], ['id' => 1] +); +``` ### Redirecting to External Domains Sometimes you may need to redirect to a domain outside of your application. You may do so by calling the `away` method, which creates a `RedirectResponse` without any additional URL encoding, validation, or verification: - return redirect()->away('https://www.google.com/'); +```php +return redirect()->away('https://www.google.com/'); +``` ### Redirecting With Flashed Session Data 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 () { - // ... +```php +Route::post('/user/profile', function () { + // ... - return redirect('/dashboard')->with('status', 'Profile updated!'); - }); + return redirect('/dashboard')->with('status', 'Profile updated!'); +}); +``` 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')) -
    - {{ session('status') }} -
    - @endif +```blade +@if (session('status')) +
    + {{ session('status') }} +
    +@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(); +```php +return back()->withInput(); +``` ## Other Response Types @@ -254,9 +306,11 @@ The `response` helper may be used to generate other types of response instances. If you need control over the response's status and headers but also need to return a [view](/docs/{{version}}/views) as the response's content, you should use the `view` method: - return response() - ->view('hello', $data, 200) - ->header('Content-Type', $type); +```php +return response() + ->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 may use the global `view` helper function. @@ -265,25 +319,31 @@ Of course, if you do not need to pass a custom HTTP status code or custom header The `json` method will automatically set the `Content-Type` header to `application/json`, as well as convert the given array to JSON using the `json_encode` PHP function: - return response()->json([ - 'name' => 'Abigail', - 'state' => 'CA', - ]); +```php +return response()->json([ + 'name' => 'Abigail', + 'state' => 'CA', +]); +``` If you would like to create a JSONP response, you may use the `json` method in combination with the `withCallback` method: - return response() - ->json(['name' => 'Abigail', 'state' => 'CA']) - ->withCallback($request->input('callback')); +```php +return response() + ->json(['name' => 'Abigail', 'state' => 'CA']) + ->withCallback($request->input('callback')); +``` ### File Downloads The `download` method may be used to generate a response that forces the user's browser to download the file at the given path. The `download` method accepts a 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); +```php +return response()->download($pathToFile); - return response()->download($pathToFile, $name, $headers); +return response()->download($pathToFile, $name, $headers); +``` > [!WARNING] > Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. @@ -293,30 +353,34 @@ The `download` method may be used to generate a response that forces the user's The `file` method may be used to display a file, such as an image or PDF, directly in the user's browser instead of initiating a download. This method accepts the absolute path to the file as its first argument and an array of headers as its second argument: - return response()->file($pathToFile); +```php +return response()->file($pathToFile); - return response()->file($pathToFile, $headers); +return response()->file($pathToFile, $headers); +``` ### Streamed Responses By streaming data to the client as it is generated, you can significantly reduce memory usage and improve performance, especially for very large responses. Streamed responses allow the client to begin processing data before the server has finished sending it: - function streamedContent(): Generator { - yield 'Hello, '; - yield 'World!'; - } - - Route::get('/stream', function () { - return response()->stream(function (): void { - foreach (streamedContent() as $chunk) { - echo $chunk; - ob_flush(); - flush(); - sleep(2); // Simulate delay between chunks... - } - }, 200, ['X-Accel-Buffering' => 'no']); - }); +```php +function streamedContent(): Generator { + yield 'Hello, '; + yield 'World!'; +} + +Route::get('/stream', function () { + return response()->stream(function (): void { + foreach (streamedContent() as $chunk) { + echo $chunk; + ob_flush(); + flush(); + sleep(2); // Simulate delay between chunks... + } + }, 200, ['X-Accel-Buffering' => 'no']); +}); +``` > [!NOTE] > Internally, Laravel utilizes PHP's output buffering functionality. As you can see in the example above, you should use the `ob_flush` and `flush` functions to push buffered content to the client. @@ -326,52 +390,60 @@ By streaming data to the client as it is generated, you can significantly reduce If you need to stream JSON data incrementally, you may utilize the `streamJson` method. This method is especially useful for large datasets that need to be sent progressively to the browser in a format that can be easily parsed by JavaScript: - use App\Models\User; +```php +use App\Models\User; - Route::get('/users.json', function () { - return response()->streamJson([ - 'users' => User::cursor(), - ]); - }); +Route::get('/users.json', function () { + return response()->streamJson([ + 'users' => User::cursor(), + ]); +}); +``` #### 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, filename, and an optional array of headers as its arguments: - use App\Services\GitHub; +```php +use App\Services\GitHub; - return response()->streamDownload(function () { - echo GitHub::api('repo') - ->contents() - ->readme('laravel', 'laravel')['contents']; - }, 'laravel-readme.md'); +return response()->streamDownload(function () { + echo GitHub::api('repo') + ->contents() + ->readme('laravel', 'laravel')['contents']; +}, 'laravel-readme.md'); +``` ## 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. 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\AppServiceProvider` service provider: - caps('foo'); +```php +return response()->caps('foo'); +``` diff --git a/routing.md b/routing.md index debcd9bd6f2..8b8935f3d1e 100644 --- a/routing.md +++ b/routing.md @@ -35,11 +35,13 @@ 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: - use Illuminate\Support\Facades\Route; +```php +use Illuminate\Support\Facades\Route; - Route::get('/greeting', function () { - return 'Hello World'; - }); +Route::get('/greeting', function () { + return 'Hello World'; +}); +``` ### The Default Route Files @@ -48,9 +50,11 @@ All Laravel routes are defined in your route files, which are located in the `ro 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; +```php +use App\Http\Controllers\UserController; - Route::get('/user', [UserController::class, 'index']); +Route::get('/user', [UserController::class, 'index']); +``` #### API Routes @@ -63,39 +67,47 @@ php artisan install:api The `install:api` command installs [Laravel Sanctum](/docs/{{version}}/sanctum), which provides a robust, yet simple API token authentication guard which can be used to authenticate third-party API consumers, SPAs, or mobile applications. In addition, the `install:api` command creates the `routes/api.php` file: - Route::get('/user', function (Request $request) { - return $request->user(); - })->middleware('auth:sanctum'); +```php +Route::get('/user', function (Request $request) { + return $request->user(); +})->middleware('auth:sanctum'); +``` The routes in `routes/api.php` are stateless and are assigned to the `api` [middleware group](/docs/{{version}}/middleware#laravels-default-middleware-groups). Additionally, the `/api` URI prefix is automatically applied to these routes, so you do not need to manually apply it to every route in the file. You may change the prefix by modifying your application's `bootstrap/app.php` file: - ->withRouting( - api: __DIR__.'/../routes/api.php', - apiPrefix: 'api/admin', - // ... - ) +```php +->withRouting( + api: __DIR__.'/../routes/api.php', + apiPrefix: 'api/admin', + // ... +) +``` #### Available Router Methods The router allows you to register routes that respond to any HTTP verb: - Route::get($uri, $callback); - Route::post($uri, $callback); - Route::put($uri, $callback); - Route::patch($uri, $callback); - Route::delete($uri, $callback); - Route::options($uri, $callback); +```php +Route::get($uri, $callback); +Route::post($uri, $callback); +Route::put($uri, $callback); +Route::patch($uri, $callback); +Route::delete($uri, $callback); +Route::options($uri, $callback); +``` Sometimes you may need to register a route that responds to multiple HTTP verbs. You may do so using the `match` method. Or, you may even register a route that responds to all HTTP verbs using the `any` method: - Route::match(['get', 'post'], '/', function () { - // ... - }); +```php +Route::match(['get', 'post'], '/', function () { + // ... +}); - Route::any('/', function () { - // ... - }); +Route::any('/', function () { + // ... +}); +``` > [!NOTE] > When defining multiple routes that share the same URI, routes using the `get`, `post`, `put`, `patch`, `delete`, and `options` methods should be defined before routes using the `any`, `match`, and `redirect` methods. This ensures the incoming request is matched with the correct route. @@ -105,36 +117,46 @@ Sometimes you may need to register a route that responds to multiple HTTP verbs. 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; +```php +use Illuminate\Http\Request; - Route::get('/users', function (Request $request) { - // ... - }); +Route::get('/users', function (Request $request) { + // ... +}); +``` #### CSRF Protection 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): -
    - @csrf - ... -
    +```blade +
    + @csrf + ... +
    +``` ### Redirect Routes If you are defining a route that redirects to another URI, you may use the `Route::redirect` method. This method provides a convenient shortcut so that you do not have to define a full route or controller for performing a simple redirect: - Route::redirect('/here', '/there'); +```php +Route::redirect('/here', '/there'); +``` By default, `Route::redirect` returns a `302` status code. You may customize the status code using the optional third parameter: - Route::redirect('/here', '/there', 301); +```php +Route::redirect('/here', '/there', 301); +``` Or, you may use the `Route::permanentRedirect` method to return a `301` status code: - Route::permanentRedirect('/here', '/there'); +```php +Route::permanentRedirect('/here', '/there'); +``` > [!WARNING] > When using route parameters in redirect routes, the following parameters are reserved by Laravel and cannot be used: `destination` and `status`. @@ -144,9 +166,11 @@ Or, you may use the `Route::permanentRedirect` method to return a `301` status c If your route only needs to return a [view](/docs/{{version}}/views), you may use the `Route::view` method. Like the `redirect` method, this method provides a simple shortcut so that you do not have to define a full route or controller. The `view` method accepts a URI as its first argument and a view name as its second argument. In addition, you may provide an array of data to pass to the view as an optional third argument: - Route::view('/welcome', 'welcome'); +```php +Route::view('/welcome', 'welcome'); - Route::view('/welcome', 'welcome', ['name' => 'Taylor']); +Route::view('/welcome', 'welcome', ['name' => 'Taylor']); +``` > [!WARNING] > When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. @@ -249,15 +273,19 @@ use Illuminate\Support\Facades\Route; Sometimes you will need to capture segments of the URI within your route. For example, you may need to capture a user's ID from the URL. You may do so by defining route parameters: - Route::get('/user/{id}', function (string $id) { - return 'User '.$id; - }); +```php +Route::get('/user/{id}', function (string $id) { + return 'User '.$id; +}); +``` You may define as many route parameters as required by your route: - Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) { - // ... - }); +```php +Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) { + // ... +}); +``` Route parameters are always encased within `{}` braces and should consist of alphabetic characters. Underscores (`_`) are also acceptable within route parameter names. Route parameters are injected into route callbacks / controllers based on their order - the names of the route callback / controller arguments do not matter. @@ -266,67 +294,75 @@ Route parameters are always encased within `{}` braces and should consist of alp If your route has dependencies that you would like the Laravel service container to automatically inject into your route's callback, you should list your route parameters after your dependencies: - use Illuminate\Http\Request; +```php +use Illuminate\Http\Request; - Route::get('/user/{id}', function (Request $request, string $id) { - return 'User '.$id; - }); +Route::get('/user/{id}', function (Request $request, string $id) { + return 'User '.$id; +}); +``` ### Optional Parameters Occasionally you may need to specify a route parameter that may not always be present in the URI. You may do so by placing a `?` mark after the parameter name. Make sure to give the route's corresponding variable a default value: - Route::get('/user/{name?}', function (?string $name = null) { - return $name; - }); +```php +Route::get('/user/{name?}', function (?string $name = null) { + return $name; +}); - Route::get('/user/{name?}', function (?string $name = 'John') { - return $name; - }); +Route::get('/user/{name?}', function (?string $name = 'John') { + return $name; +}); +``` ### Regular Expression Constraints You may constrain the format of your route parameters using the `where` method on a route instance. The `where` method accepts the name of the parameter and a regular expression defining how the parameter should be constrained: - Route::get('/user/{name}', function (string $name) { - // ... - })->where('name', '[A-Za-z]+'); +```php +Route::get('/user/{name}', function (string $name) { + // ... +})->where('name', '[A-Za-z]+'); - Route::get('/user/{id}', function (string $id) { - // ... - })->where('id', '[0-9]+'); +Route::get('/user/{id}', function (string $id) { + // ... +})->where('id', '[0-9]+'); - Route::get('/user/{id}/{name}', function (string $id, string $name) { - // ... - })->where(['id' => '[0-9]+', 'name' => '[a-z]+']); +Route::get('/user/{id}/{name}', function (string $id, string $name) { + // ... +})->where(['id' => '[0-9]+', 'name' => '[a-z]+']); +``` For convenience, some commonly used regular expression patterns have helper methods that allow you to quickly add pattern constraints to your routes: - Route::get('/user/{id}/{name}', function (string $id, string $name) { - // ... - })->whereNumber('id')->whereAlpha('name'); +```php +Route::get('/user/{id}/{name}', function (string $id, string $name) { + // ... +})->whereNumber('id')->whereAlpha('name'); - Route::get('/user/{name}', function (string $name) { - // ... - })->whereAlphaNumeric('name'); +Route::get('/user/{name}', function (string $name) { + // ... +})->whereAlphaNumeric('name'); - Route::get('/user/{id}', function (string $id) { - // ... - })->whereUuid('id'); +Route::get('/user/{id}', function (string $id) { + // ... +})->whereUuid('id'); - Route::get('/user/{id}', function (string $id) { - // ... - })->whereUlid('id'); +Route::get('/user/{id}', function (string $id) { + // ... +})->whereUlid('id'); - Route::get('/category/{category}', function (string $category) { - // ... - })->whereIn('category', ['movie', 'song', 'painting']); +Route::get('/category/{category}', function (string $category) { + // ... +})->whereIn('category', ['movie', 'song', 'painting']); - Route::get('/category/{category}', function (string $category) { - // ... - })->whereIn('category', CategoryEnum::cases()); +Route::get('/category/{category}', function (string $category) { + // ... +})->whereIn('category', CategoryEnum::cases()); +``` If the incoming request does not match the route pattern constraints, a 404 HTTP response will be returned. @@ -335,30 +371,36 @@ If the incoming request does not match the route pattern constraints, a 404 HTTP If you would like a route parameter to always be constrained by a given regular expression, you may use the `pattern` method. You should define these patterns in the `boot` method of your application's `App\Providers\AppServiceProvider` class: - use Illuminate\Support\Facades\Route; +```php +use Illuminate\Support\Facades\Route; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Route::pattern('id', '[0-9]+'); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Route::pattern('id', '[0-9]+'); +} +``` Once the pattern has been defined, it is automatically applied to all routes using that parameter name: - Route::get('/user/{id}', function (string $id) { - // Only executed if {id} is numeric... - }); +```php +Route::get('/user/{id}', function (string $id) { + // Only executed if {id} is numeric... +}); +``` #### Encoded Forward Slashes The Laravel routing component allows all characters except `/` to be present within route parameter values. You must explicitly allow `/` to be part of your placeholder using a `where` condition regular expression: - Route::get('/search/{search}', function (string $search) { - return $search; - })->where('search', '.*'); +```php +Route::get('/search/{search}', function (string $search) { + return $search; +})->where('search', '.*'); +``` > [!WARNING] > Encoded forward slashes are only supported within the last route segment. @@ -368,16 +410,20 @@ The Laravel routing component allows all characters except `/` to be present wit Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the `name` method onto the route definition: - Route::get('/user/profile', function () { - // ... - })->name('profile'); +```php +Route::get('/user/profile', function () { + // ... +})->name('profile'); +``` You may also specify route names for controller actions: - Route::get( - '/user/profile', - [UserProfileController::class, 'show'] - )->name('profile'); +```php +Route::get( + '/user/profile', + [UserProfileController::class, 'show'] +)->name('profile'); +``` > [!WARNING] > Route names should always be unique. @@ -387,31 +433,37 @@ You may also specify route names for controller actions: Once you have assigned a name to a given route, you may use the route's name when generating URLs or redirects via Laravel's `route` and `redirect` helper functions: - // Generating URLs... - $url = route('profile'); +```php +// Generating URLs... +$url = route('profile'); - // Generating Redirects... - return redirect()->route('profile'); +// Generating Redirects... +return redirect()->route('profile'); - return to_route('profile'); +return to_route('profile'); +``` If the named route defines parameters, you may pass the parameters as the second argument to the `route` function. The given parameters will automatically be inserted into the generated URL in their correct positions: - Route::get('/user/{id}/profile', function (string $id) { - // ... - })->name('profile'); +```php +Route::get('/user/{id}/profile', function (string $id) { + // ... +})->name('profile'); - $url = route('profile', ['id' => 1]); +$url = route('profile', ['id' => 1]); +``` If you pass additional parameters in the array, those key / value pairs will automatically be added to the generated URL's query string: - Route::get('/user/{id}/profile', function (string $id) { - // ... - })->name('profile'); +```php +Route::get('/user/{id}/profile', function (string $id) { + // ... +})->name('profile'); - $url = route('profile', ['id' => 1, 'photos' => 'yes']); +$url = route('profile', ['id' => 1, 'photos' => 'yes']); - // /user/1/profile?photos=yes +// /user/1/profile?photos=yes +``` > [!NOTE] > Sometimes, you may wish to specify request-wide default values for URL parameters, such as the current locale. To accomplish this, you may use the [`URL::defaults` method](/docs/{{version}}/urls#default-values). @@ -421,24 +473,26 @@ If you pass additional parameters in the array, those key / value pairs will aut If you would like to determine if the current request was routed to a given named route, you may use the `named` method on a Route instance. For example, you may check the current route name from a route middleware: - use Closure; - use Illuminate\Http\Request; - use Symfony\Component\HttpFoundation\Response; - - /** - * Handle an incoming request. - * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next - */ - public function handle(Request $request, Closure $next): Response - { - if ($request->route()->named('profile')) { - // ... - } - - return $next($request); +```php +use Closure; +use Illuminate\Http\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Handle an incoming request. + * + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + */ +public function handle(Request $request, Closure $next): Response +{ + if ($request->route()->named('profile')) { + // ... } + return $next($request); +} +``` + ## Route Groups @@ -451,38 +505,44 @@ Nested groups attempt to intelligently "merge" attributes with their parent grou To assign [middleware](/docs/{{version}}/middleware) to all routes within a group, you may use the `middleware` method before defining the group. Middleware are executed in the order they are listed in the array: - Route::middleware(['first', 'second'])->group(function () { - Route::get('/', function () { - // Uses first & second middleware... - }); +```php +Route::middleware(['first', 'second'])->group(function () { + Route::get('/', function () { + // Uses first & second middleware... + }); - Route::get('/user/profile', function () { - // Uses first & second middleware... - }); + Route::get('/user/profile', function () { + // Uses first & second middleware... }); +}); +``` ### Controllers If a group of routes all utilize the same [controller](/docs/{{version}}/controllers), you may use the `controller` method to define the common controller for all of the routes within the group. Then, when defining the routes, you only need to provide the controller method that they invoke: - use App\Http\Controllers\OrderController; +```php +use App\Http\Controllers\OrderController; - Route::controller(OrderController::class)->group(function () { - Route::get('/orders/{id}', 'show'); - Route::post('/orders', 'store'); - }); +Route::controller(OrderController::class)->group(function () { + Route::get('/orders/{id}', 'show'); + Route::post('/orders', 'store'); +}); +``` ### Subdomain Routing Route groups may also be used to handle subdomain routing. Subdomains may be assigned route parameters just like route URIs, allowing you to capture a portion of the subdomain for usage in your route or controller. The subdomain may be specified by calling the `domain` method before defining the group: - Route::domain('{account}.example.com')->group(function () { - Route::get('/user/{id}', function (string $account, string $id) { - // ... - }); +```php +Route::domain('{account}.example.com')->group(function () { + Route::get('/user/{id}', function (string $account, string $id) { + // ... }); +}); +``` > [!WARNING] > In order to ensure your subdomain routes are reachable, you should register subdomain routes before registering root domain routes. This will prevent root domain routes from overwriting subdomain routes which have the same URI path. @@ -492,22 +552,26 @@ Route groups may also be used to handle subdomain routing. Subdomains may be ass The `prefix` method may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with `admin`: - Route::prefix('admin')->group(function () { - Route::get('/users', function () { - // Matches The "/admin/users" URL - }); +```php +Route::prefix('admin')->group(function () { + Route::get('/users', function () { + // Matches The "/admin/users" URL }); +}); +``` ### Route Name Prefixes The `name` method may be used to prefix each route name in the group with a given string. For example, you may want to prefix the names of all of the routes in the group with `admin`. The given string is prefixed to the route name exactly as it is specified, so we will be sure to provide the trailing `.` character in the prefix: - Route::name('admin.')->group(function () { - Route::get('/users', function () { - // Route assigned name "admin.users"... - })->name('users'); - }); +```php +Route::name('admin.')->group(function () { + Route::get('/users', function () { + // Route assigned name "admin.users"... + })->name('users'); +}); +``` ## Route Model Binding @@ -519,111 +583,131 @@ When injecting a model ID to a route or controller action, you will often query Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name. For example: - use App\Models\User; +```php +use App\Models\User; - Route::get('/users/{user}', function (User $user) { - return $user->email; - }); +Route::get('/users/{user}', function (User $user) { + return $user->email; +}); +``` Since the `$user` variable is type-hinted as the `App\Models\User` Eloquent model and the variable name matches the `{user}` URI segment, Laravel will automatically inject the model instance that has an ID matching the corresponding value from the request URI. If a matching model instance is not found in the database, a 404 HTTP response will automatically be generated. Of course, implicit binding is also possible when using controller methods. Again, note the `{user}` URI segment matches the `$user` variable in the controller which contains an `App\Models\User` type-hint: - use App\Http\Controllers\UserController; - use App\Models\User; +```php +use App\Http\Controllers\UserController; +use App\Models\User; - // Route definition... - Route::get('/users/{user}', [UserController::class, 'show']); +// Route definition... +Route::get('/users/{user}', [UserController::class, 'show']); - // Controller method definition... - public function show(User $user) - { - return view('user.profile', ['user' => $user]); - } +// Controller method definition... +public function show(User $user) +{ + return view('user.profile', ['user' => $user]); +} +``` #### Soft Deleted Models Typically, implicit model binding will not retrieve models that have been [soft deleted](/docs/{{version}}/eloquent#soft-deleting). However, you may instruct the implicit binding to retrieve these models by chaining the `withTrashed` method onto your route's definition: - use App\Models\User; +```php +use App\Models\User; - Route::get('/users/{user}', function (User $user) { - return $user->email; - })->withTrashed(); +Route::get('/users/{user}', function (User $user) { + return $user->email; +})->withTrashed(); +``` #### Customizing the Key Sometimes you may wish to resolve Eloquent models using a column other than `id`. To do so, you may specify the column in the route parameter definition: - use App\Models\Post; +```php +use App\Models\Post; - Route::get('/posts/{post:slug}', function (Post $post) { - return $post; - }); +Route::get('/posts/{post:slug}', function (Post $post) { + return $post; +}); +``` If you would like model binding to always use a database column other than `id` when retrieving a given model class, you may override the `getRouteKeyName` method on the Eloquent model: - /** - * Get the route key for the model. - */ - public function getRouteKeyName(): string - { - return 'slug'; - } +```php +/** + * Get the route key for the model. + */ +public function getRouteKeyName(): string +{ + return 'slug'; +} +``` #### Custom Keys and Scoping When implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the previous Eloquent model. For example, consider this route definition that retrieves a blog post by slug for a specific user: - use App\Models\Post; - use App\Models\User; +```php +use App\Models\Post; +use App\Models\User; - Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { - return $post; - }); +Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { + return $post; +}); +``` When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. In this case, it will be assumed that the `User` model has a relationship named `posts` (the plural form of the route parameter name) which can be used to retrieve the `Post` model. If you wish, you may instruct Laravel to scope "child" bindings even when a custom key is not provided. To do so, you may invoke the `scopeBindings` method when defining your route: - use App\Models\Post; - use App\Models\User; +```php +use App\Models\Post; +use App\Models\User; - Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { - return $post; - })->scopeBindings(); +Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { + return $post; +})->scopeBindings(); +``` Or, you may instruct an entire group of route definitions to use scoped bindings: - Route::scopeBindings()->group(function () { - Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { - return $post; - }); +```php +Route::scopeBindings()->group(function () { + Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) { + return $post; }); +}); +``` Similarly, you may explicitly instruct Laravel to not scope bindings by invoking the `withoutScopedBindings` method: - Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { - return $post; - })->withoutScopedBindings(); +```php +Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { + return $post; +})->withoutScopedBindings(); +``` #### Customizing Missing Model Behavior Typically, a 404 HTTP response will be generated if an implicitly bound model is not found. However, you may customize this behavior by calling the `missing` method when defining your route. The `missing` method accepts a closure that will be invoked if an implicitly bound model cannot be found: - use App\Http\Controllers\LocationsController; - use Illuminate\Http\Request; - use Illuminate\Support\Facades\Redirect; +```php +use App\Http\Controllers\LocationsController; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Redirect; - Route::get('/locations/{location:slug}', [LocationsController::class, 'show']) - ->name('locations.view') - ->missing(function (Request $request) { - return Redirect::route('locations.index'); - }); +Route::get('/locations/{location:slug}', [LocationsController::class, 'show']) + ->name('locations.view') + ->missing(function (Request $request) { + return Redirect::route('locations.index'); + }); +``` ### Implicit Enum Binding @@ -658,24 +742,28 @@ Route::get('/categories/{category}', function (Category $category) { You are not required to use Laravel's implicit, convention based model resolution in order to use model binding. You can also explicitly define how route parameters correspond to models. To register an explicit binding, use the router's `model` method to specify the class for a given parameter. You should define your explicit model bindings at the beginning of the `boot` method of your `AppServiceProvider` class: - use App\Models\User; - use Illuminate\Support\Facades\Route; +```php +use App\Models\User; +use Illuminate\Support\Facades\Route; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Route::model('user', User::class); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Route::model('user', User::class); +} +``` Next, define a route that contains a `{user}` parameter: - use App\Models\User; +```php +use App\Models\User; - Route::get('/users/{user}', function (User $user) { - // ... - }); +Route::get('/users/{user}', function (User $user) { + // ... +}); +``` Since we have bound all `{user}` parameters to the `App\Models\User` model, an instance of that class will be injected into the route. So, for example, a request to `users/1` will inject the `User` instance from the database which has an ID of `1`. @@ -686,56 +774,64 @@ If a matching model instance is not found in the database, a 404 HTTP response w If you wish to define your own model binding resolution logic, you may use the `Route::bind` method. The closure you pass to the `bind` method will receive the value of the URI segment and should return the instance of the class that should be injected into the route. Again, this customization should take place in the `boot` method of your application's `AppServiceProvider`: - use App\Models\User; - use Illuminate\Support\Facades\Route; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Route::bind('user', function (string $value) { - return User::where('name', $value)->firstOrFail(); - }); - } +```php +use App\Models\User; +use Illuminate\Support\Facades\Route; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Route::bind('user', function (string $value) { + return User::where('name', $value)->firstOrFail(); + }); +} +``` Alternatively, you may override the `resolveRouteBinding` method on your Eloquent model. This method will receive the value of the URI segment and should return the instance of the class that should be injected into the route: - /** - * Retrieve the model for a bound value. - * - * @param mixed $value - * @param string|null $field - * @return \Illuminate\Database\Eloquent\Model|null - */ - public function resolveRouteBinding($value, $field = null) - { - return $this->where('name', $value)->firstOrFail(); - } +```php +/** + * Retrieve the model for a bound value. + * + * @param mixed $value + * @param string|null $field + * @return \Illuminate\Database\Eloquent\Model|null + */ +public function resolveRouteBinding($value, $field = null) +{ + return $this->where('name', $value)->firstOrFail(); +} +``` If a route is utilizing [implicit binding scoping](#implicit-model-binding-scoping), the `resolveChildRouteBinding` method will be used to resolve the child binding of the parent model: - /** - * Retrieve the child model for a bound value. - * - * @param string $childType - * @param mixed $value - * @param string|null $field - * @return \Illuminate\Database\Eloquent\Model|null - */ - public function resolveChildRouteBinding($childType, $value, $field) - { - return parent::resolveChildRouteBinding($childType, $value, $field); - } +```php +/** + * Retrieve the child model for a bound value. + * + * @param string $childType + * @param mixed $value + * @param string|null $field + * @return \Illuminate\Database\Eloquent\Model|null + */ +public function resolveChildRouteBinding($childType, $value, $field) +{ + return parent::resolveChildRouteBinding($childType, $value, $field); +} +``` ## Fallback Routes Using the `Route::fallback` method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you would typically define the `fallback` route within your `routes/web.php` file, all middleware in the `web` middleware group will apply to the route. You are free to add additional middleware to this route as needed: - Route::fallback(function () { - // ... - }); +```php +Route::fallback(function () { + // ... +}); +``` ## Rate Limiting @@ -765,128 +861,152 @@ protected function boot(): void 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 to the rate limiter. Limit configuration are instances of the `Illuminate\Cache\RateLimiting\Limit` class. This class contains helpful "builder" methods so that you can quickly define your limit. The rate limiter name may be any string you wish: - use Illuminate\Cache\RateLimiting\Limit; - use Illuminate\Http\Request; - use Illuminate\Support\Facades\RateLimiter; - - /** - * Bootstrap any application services. - */ - protected function boot(): void - { - RateLimiter::for('global', function (Request $request) { - return Limit::perMinute(1000); - }); - } +```php +use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\RateLimiter; + +/** + * Bootstrap any application services. + */ +protected function boot(): void +{ + RateLimiter::for('global', function (Request $request) { + return Limit::perMinute(1000); + }); +} +``` If the incoming request exceeds the specified rate limit, a response with a 429 HTTP status code will automatically be returned by Laravel. If you would like to define your own response that should be returned by a rate limit, you may use the `response` method: - RateLimiter::for('global', function (Request $request) { - return Limit::perMinute(1000)->response(function (Request $request, array $headers) { - return response('Custom response...', 429, $headers); - }); +```php +RateLimiter::for('global', function (Request $request) { + return Limit::perMinute(1000)->response(function (Request $request, array $headers) { + return response('Custom response...', 429, $headers); }); +}); +``` Since rate limiter callbacks receive the incoming HTTP request instance, you may build the appropriate rate limit dynamically based on the incoming request or authenticated user: - RateLimiter::for('uploads', function (Request $request) { - return $request->user()->vipCustomer() - ? Limit::none() - : Limit::perMinute(100); - }); +```php +RateLimiter::for('uploads', function (Request $request) { + return $request->user()->vipCustomer() + ? Limit::none() + : Limit::perMinute(100); +}); +``` #### Segmenting Rate Limits Sometimes you may wish to segment rate limits by some arbitrary value. For example, you may wish to allow users to access a given route 100 times per minute per IP address. To accomplish this, you may use the `by` method when building your rate limit: - RateLimiter::for('uploads', function (Request $request) { - return $request->user()->vipCustomer() - ? Limit::none() - : Limit::perMinute(100)->by($request->ip()); - }); +```php +RateLimiter::for('uploads', function (Request $request) { + return $request->user()->vipCustomer() + ? Limit::none() + : Limit::perMinute(100)->by($request->ip()); +}); +``` To illustrate this feature using another example, we can limit access to the route to 100 times per minute per authenticated user ID or 10 times per minute per IP address for guests: - RateLimiter::for('uploads', function (Request $request) { - return $request->user() - ? Limit::perMinute(100)->by($request->user()->id) - : Limit::perMinute(10)->by($request->ip()); - }); +```php +RateLimiter::for('uploads', function (Request $request) { + return $request->user() + ? Limit::perMinute(100)->by($request->user()->id) + : Limit::perMinute(10)->by($request->ip()); +}); +``` #### Multiple Rate Limits If needed, you may return an array of rate limits for a given rate limiter configuration. Each rate limit will be evaluated for the route based on the order they are placed within the array: - RateLimiter::for('login', function (Request $request) { - return [ - Limit::perMinute(500), - Limit::perMinute(3)->by($request->input('email')), - ]; - }); +```php +RateLimiter::for('login', function (Request $request) { + return [ + Limit::perMinute(500), + Limit::perMinute(3)->by($request->input('email')), + ]; +}); +``` If you're assigning multiple rate limits segmented by identical `by` values, you should ensure that each `by` value is unique. The easiest way to achieve this is to prefix the values given to the `by` method: - RateLimiter::for('uploads', function (Request $request) { - return [ - Limit::perMinute(10)->by('minute:'.$request->user()->id), - Limit::perDay(1000)->by('day:'.$request->user()->id), - ]; - }); +```php +RateLimiter::for('uploads', function (Request $request) { + return [ + Limit::perMinute(10)->by('minute:'.$request->user()->id), + Limit::perDay(1000)->by('day:'.$request->user()->id), + ]; +}); +``` ### Attaching Rate Limiters to Routes Rate limiters may be attached to routes or route groups using the `throttle` [middleware](/docs/{{version}}/middleware). The throttle middleware accepts the name of the rate limiter you wish to assign to the route: - Route::middleware(['throttle:uploads'])->group(function () { - Route::post('/audio', function () { - // ... - }); +```php +Route::middleware(['throttle:uploads'])->group(function () { + Route::post('/audio', function () { + // ... + }); - Route::post('/video', function () { - // ... - }); + Route::post('/video', function () { + // ... }); +}); +``` #### Throttling With Redis By default, the `throttle` middleware is mapped to the `Illuminate\Routing\Middleware\ThrottleRequests` class. However, if you are using Redis as your application's cache driver, you may wish to instruct Laravel to use Redis to manage rate limiting. To do so, you should use the `throttleWithRedis` method in your application's `bootstrap/app.php` file. This method maps the `throttle` middleware to the `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` middleware class: - ->withMiddleware(function (Middleware $middleware) { - $middleware->throttleWithRedis(); - // ... - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->throttleWithRedis(); + // ... +}) +``` ## Form Method Spoofing HTML forms do not support `PUT`, `PATCH`, or `DELETE` actions. So, when defining `PUT`, `PATCH`, or `DELETE` routes that are called from an HTML form, you will need to add a hidden `_method` field to the form. The value sent with the `_method` field will be used as the HTTP request method: -
    - - -
    +```blade +
    + + +
    +``` For convenience, you may use the `@method` [Blade directive](/docs/{{version}}/blade) to generate the `_method` input field: -
    - @method('PUT') - @csrf -
    +```blade +
    + @method('PUT') + @csrf +
    +``` ## Accessing the Current Route You may use the `current`, `currentRouteName`, and `currentRouteAction` methods on the `Route` facade to access information about the route handling the incoming request: - use Illuminate\Support\Facades\Route; +```php +use Illuminate\Support\Facades\Route; - $route = Route::current(); // Illuminate\Routing\Route - $name = Route::currentRouteName(); // string - $action = Route::currentRouteAction(); // string +$route = Route::current(); // Illuminate\Routing\Route +$name = Route::currentRouteName(); // string +$action = Route::currentRouteAction(); // string +``` 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. diff --git a/sanctum.md b/sanctum.md index 97cc7a6a079..3a3fe9f4a99 100644 --- a/sanctum.md +++ b/sanctum.md @@ -70,25 +70,29 @@ Next, if you plan to utilize Sanctum to authenticate an SPA, please refer to the Although not typically required, you are free to extend the `PersonalAccessToken` model used internally by Sanctum: - use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken; +```php +use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken; - class PersonalAccessToken extends SanctumPersonalAccessToken - { - // ... - } +class PersonalAccessToken extends SanctumPersonalAccessToken +{ + // ... +} +``` 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 your application's `AppServiceProvider` file: - use App\Models\Sanctum\PersonalAccessToken; - use Laravel\Sanctum\Sanctum; +```php +use App\Models\Sanctum\PersonalAccessToken; +use Laravel\Sanctum\Sanctum; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); +} +``` ## API Token Authentication @@ -103,72 +107,88 @@ Sanctum allows you to issue API tokens / personal access tokens that may be used To begin issuing tokens for users, your User model should use the `Laravel\Sanctum\HasApiTokens` trait: - use Laravel\Sanctum\HasApiTokens; +```php +use Laravel\Sanctum\HasApiTokens; - class User extends Authenticatable - { - use HasApiTokens, HasFactory, Notifiable; - } +class User extends Authenticatable +{ + use HasApiTokens, HasFactory, Notifiable; +} +``` 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: - use Illuminate\Http\Request; +```php +use Illuminate\Http\Request; - Route::post('/tokens/create', function (Request $request) { - $token = $request->user()->createToken($request->token_name); +Route::post('/tokens/create', function (Request $request) { + $token = $request->user()->createToken($request->token_name); - return ['token' => $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: - foreach ($user->tokens as $token) { - // ... - } +```php +foreach ($user->tokens as $token) { + // ... +} +``` ### Token Abilities 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; +```php +return $user->createToken('token-name', ['server:update'])->plainTextToken; +``` When handling an incoming request authenticated by Sanctum, you may determine if the token has a given ability using the `tokenCan` or `tokenCant` methods: - if ($user->tokenCan('server:update')) { - // ... - } +```php +if ($user->tokenCan('server:update')) { + // ... +} - if ($user->tokenCant('server:update')) { - // ... - } +if ($user->tokenCant('server:update')) { + // ... +} +``` #### Token Ability Middleware Sanctum also includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given ability. To get started, define the following middleware aliases in your application's `bootstrap/app.php` file: - use Laravel\Sanctum\Http\Middleware\CheckAbilities; - use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility; - - ->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'abilities' => CheckAbilities::class, - 'ability' => CheckForAnyAbility::class, - ]); - }) +```php +use Laravel\Sanctum\Http\Middleware\CheckAbilities; +use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility; + +->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'abilities' => CheckAbilities::class, + 'ability' => CheckForAnyAbility::class, + ]); +}) +``` The `abilities` middleware may be assigned to a route to verify that the incoming request's token has all of the listed abilities: - Route::get('/orders', function () { - // Token has both "check-status" and "place-orders" abilities... - })->middleware(['auth:sanctum', 'abilities:check-status,place-orders']); +```php +Route::get('/orders', function () { + // Token has both "check-status" and "place-orders" abilities... +})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']); +``` The `ability` middleware may be assigned to a route to verify that the incoming request's token has *at least one* of the listed abilities: - Route::get('/orders', function () { - // Token has the "check-status" or "place-orders" ability... - })->middleware(['auth:sanctum', 'ability:check-status,place-orders']); +```php +Route::get('/orders', function () { + // Token has the "check-status" or "place-orders" ability... +})->middleware(['auth:sanctum', 'ability:check-status,place-orders']); +``` #### First-Party UI Initiated Requests @@ -193,25 +213,29 @@ To protect routes so that all incoming requests must be authenticated, you shoul 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; +```php +use Illuminate\Http\Request; - Route::get('/user', function (Request $request) { - return $request->user(); - })->middleware('auth:sanctum'); +Route::get('/user', function (Request $request) { + return $request->user(); +})->middleware('auth:sanctum'); +``` ### Revoking Tokens 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(); +```php +// Revoke all tokens... +$user->tokens()->delete(); - // Revoke the token that was used to authenticate the current request... - $request->user()->currentAccessToken()->delete(); +// Revoke the token that was used to authenticate the current request... +$request->user()->currentAccessToken()->delete(); - // Revoke a specific token... - $user->tokens()->where('id', $tokenId)->delete(); +// Revoke a specific token... +$user->tokens()->where('id', $tokenId)->delete(); +``` ### Token Expiration @@ -264,9 +288,11 @@ First, you should configure which domains your SPA will be making requests from. Next, you should instruct Laravel that incoming requests from your SPA can authenticate using Laravel's session cookies, while still allowing requests from third parties or mobile applications to authenticate using API tokens. This can be easily accomplished by invoking the `statefulApi` middleware method in your application's `bootstrap/app.php` file: - ->withMiddleware(function (Middleware $middleware) { - $middleware->statefulApi(); - }) +```php +->withMiddleware(function (Middleware $middleware) { + $middleware->statefulApi(); +}) +``` #### CORS and Cookies @@ -290,7 +316,9 @@ axios.defaults.withXSRFToken = true; 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', +```php +'domain' => '.domain.com', +``` ### Authenticating @@ -325,26 +353,30 @@ Of course, if your user's session expires due to lack of activity, subsequent re 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 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; +```php +use Illuminate\Http\Request; - Route::get('/user', function (Request $request) { - return $request->user(); - })->middleware('auth:sanctum'); +Route::get('/user', function (Request $request) { + return $request->user(); +})->middleware('auth:sanctum'); +``` ### Authorizing Private Broadcast Channels If your SPA needs to authenticate with [private / presence broadcast channels](/docs/{{version}}/broadcasting#authorizing-channels), you should remove the `channels` entry from the `withRouting` method contained in your application's `bootstrap/app.php` file. Instead, you should invoke the `withBroadcasting` method so that you may specify the correct middleware for your application's broadcasting routes: - return Application::configure(basePath: dirname(__DIR__)) - ->withRouting( - web: __DIR__.'/../routes/web.php', - // ... - ) - ->withBroadcasting( - __DIR__.'/../routes/channels.php', - ['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']], - ) +```php +return Application::configure(basePath: dirname(__DIR__)) + ->withRouting( + web: __DIR__.'/../routes/web.php', + // ... + ) + ->withBroadcasting( + __DIR__.'/../routes/channels.php', + ['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']], + ) +``` Next, in order for Pusher's authorization requests to succeed, you will need to provide a custom Pusher `authorizer` when initializing [Laravel Echo](/docs/{{version}}/broadcasting#client-side-installation). This allows your application to configure Pusher to use the `axios` instance that is [properly configured for cross-domain requests](#cors-and-cookies): @@ -385,28 +417,30 @@ To get started, create a route that accepts the user's email / username, passwor 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; - use Illuminate\Support\Facades\Hash; - use Illuminate\Validation\ValidationException; - - Route::post('/sanctum/token', function (Request $request) { - $request->validate([ - 'email' => 'required|email', - 'password' => 'required', - 'device_name' => 'required', +```php +use App\Models\User; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Hash; +use Illuminate\Validation\ValidationException; + +Route::post('/sanctum/token', function (Request $request) { + $request->validate([ + 'email' => 'required|email', + 'password' => 'required', + 'device_name' => 'required', + ]); + + $user = User::where('email', $request->email)->first(); + + if (! $user || ! Hash::check($request->password, $user->password)) { + throw ValidationException::withMessages([ + 'email' => ['The provided credentials are incorrect.'], ]); + } - $user = User::where('email', $request->email)->first(); - - if (! $user || ! Hash::check($request->password, $user->password)) { - throw ValidationException::withMessages([ - 'email' => ['The provided credentials are incorrect.'], - ]); - } - - return $user->createToken($request->device_name)->plainTextToken; - }); + return $user->createToken($request->device_name)->plainTextToken; +}); +``` 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. @@ -418,20 +452,24 @@ When the mobile application uses the token to make an API request to your applic 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::get('/user', function (Request $request) { - return $request->user(); - })->middleware('auth:sanctum'); +```php +Route::get('/user', function (Request $request) { + return $request->user(); +})->middleware('auth:sanctum'); +``` ### 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 `Laravel\Sanctum\HasApiTokens` trait: - // Revoke all tokens... - $user->tokens()->delete(); +```php +// Revoke all tokens... +$user->tokens()->delete(); - // Revoke a specific token... - $user->tokens()->where('id', $tokenId)->delete(); +// Revoke a specific token... +$user->tokens()->where('id', $tokenId)->delete(); +``` ## Testing @@ -473,7 +511,9 @@ public function test_task_list_can_be_retrieved(): void If you would like to grant all abilities to the token, you should include `*` in the ability list provided to the `actingAs` method: - Sanctum::actingAs( - User::factory()->create(), - ['*'] - ); +```php +Sanctum::actingAs( + User::factory()->create(), + ['*'] +); +``` diff --git a/scheduling.md b/scheduling.md index 912f8e2070a..3b63243b16a 100644 --- a/scheduling.md +++ b/scheduling.md @@ -31,26 +31,32 @@ Laravel's command scheduler offers a fresh approach to managing scheduled tasks You may define all of your scheduled tasks in your application's `routes/console.php` file. 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: - delete(); - })->daily(); +Schedule::call(function () { + DB::table('recent_users')->delete(); +})->daily(); +``` In addition to scheduling using closures, you may also schedule [invokable objects](https://secure.php.net/manual/en/language.oop5.magic.php#object.invoke). Invokable objects are simple PHP classes that contain an `__invoke` method: - Schedule::call(new DeleteRecentUsers)->daily(); +```php +Schedule::call(new DeleteRecentUsers)->daily(); +``` If you prefer to reserve your `routes/console.php` file for command definitions only, you may use the `withSchedule` method in your application's `bootstrap/app.php` file to define your scheduled tasks. This method accepts a closure that receives an instance of the scheduler: - use Illuminate\Console\Scheduling\Schedule; +```php +use Illuminate\Console\Scheduling\Schedule; - ->withSchedule(function (Schedule $schedule) { - $schedule->call(new DeleteRecentUsers)->daily(); - }) +->withSchedule(function (Schedule $schedule) { + $schedule->call(new DeleteRecentUsers)->daily(); +}) +``` If you would like to view an overview of your scheduled tasks and the next time they are scheduled to run, you may use the `schedule:list` Artisan command: @@ -65,54 +71,66 @@ In addition to scheduling closures, you may also schedule [Artisan commands](/do 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\SendEmailsCommand; - use Illuminate\Support\Facades\Schedule; +```php +use App\Console\Commands\SendEmailsCommand; +use Illuminate\Support\Facades\Schedule; - Schedule::command('emails:send Taylor --force')->daily(); +Schedule::command('emails:send Taylor --force')->daily(); - Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily(); +Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily(); +``` #### Scheduling Artisan Closure Commands If you want to schedule an Artisan command defined by a closure, you may chain the scheduling related methods after the command's definition: - Artisan::command('delete:recent-users', function () { - DB::table('recent_users')->delete(); - })->purpose('Delete recent users')->daily(); +```php +Artisan::command('delete:recent-users', function () { + DB::table('recent_users')->delete(); +})->purpose('Delete recent users')->daily(); +``` If you need to pass arguments to the closure command, you may provide them to the `schedule` method: - Artisan::command('emails:send {user} {--force}', function ($user) { - // ... - })->purpose('Send emails to the specified user')->schedule(['Taylor', '--force'])->daily(); +```php +Artisan::command('emails:send {user} {--force}', function ($user) { + // ... +})->purpose('Send emails to the specified user')->schedule(['Taylor', '--force'])->daily(); +``` ### 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 queued jobs without using the `call` method to define closures to queue the job: - use App\Jobs\Heartbeat; - use Illuminate\Support\Facades\Schedule; +```php +use App\Jobs\Heartbeat; +use Illuminate\Support\Facades\Schedule; - Schedule::job(new Heartbeat)->everyFiveMinutes(); +Schedule::job(new Heartbeat)->everyFiveMinutes(); +``` Optional second and third arguments may be provided to the `job` method which specifies the queue name and queue connection that should be used to queue the job: - use App\Jobs\Heartbeat; - use Illuminate\Support\Facades\Schedule; +```php +use App\Jobs\Heartbeat; +use Illuminate\Support\Facades\Schedule; - // Dispatch the job to the "heartbeats" queue on the "sqs" connection... - Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes(); +// Dispatch the job to the "heartbeats" queue on the "sqs" connection... +Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes(); +``` ### Scheduling Shell Commands The `exec` method may be used to issue a command to the operating system: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::exec('node /home/forge/script.js')->daily(); +Schedule::exec('node /home/forge/script.js')->daily(); +``` ### Schedule Frequency Options @@ -166,19 +184,21 @@ We've already seen a few examples of how you may configure a task to run at spec 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: - use Illuminate\Support\Facades\Schedule; - - // Run once per week on Monday at 1 PM... - Schedule::call(function () { - // ... - })->weekly()->mondays()->at('13:00'); +```php +use Illuminate\Support\Facades\Schedule; - // Run hourly from 8 AM to 5 PM on weekdays... - Schedule::command('foo') - ->weekdays() - ->hourly() - ->timezone('America/Chicago') - ->between('8:00', '17:00'); +// Run once per week on Monday at 1 PM... +Schedule::call(function () { + // ... +})->weekly()->mondays()->at('13:00'); + +// Run hourly from 8 AM to 5 PM on weekdays... +Schedule::command('foo') + ->weekdays() + ->hourly() + ->timezone('America/Chicago') + ->between('8:00', '17:00'); +``` A list of additional schedule constraints may be found below: @@ -208,50 +228,62 @@ A list of additional schedule constraints may be found below: 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: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('emails:send') - ->hourly() - ->days([0, 3]); +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\Support\Facades; - use Illuminate\Console\Scheduling\Schedule; +```php +use Illuminate\Support\Facades; +use Illuminate\Console\Scheduling\Schedule; - Facades\Schedule::command('emails:send') - ->hourly() - ->days([Schedule::SUNDAY, Schedule::WEDNESDAY]); +Facades\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('emails:send') - ->hourly() - ->between('7:00', '22:00'); +```php +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('emails:send') - ->hourly() - ->unlessBetween('23:00', '4:00'); +```php +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: - Schedule::command('emails:send')->daily()->when(function () { - return true; - }); +```php +Schedule::command('emails:send')->daily()->when(function () { + return true; +}); +``` The `skip` method may be seen as the inverse of `when`. If the `skip` method returns `true`, the scheduled task will not be executed: - Schedule::command('emails:send')->daily()->skip(function () { - return true; - }); +```php +Schedule::command('emails:send')->daily()->skip(function () { + return true; +}); +``` When using chained `when` methods, the scheduled command will only execute if all `when` conditions return `true`. @@ -260,26 +292,32 @@ When using chained `when` methods, the scheduled command will only execute if al 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() - ->environments(['staging', 'production']); +```php +Schedule::command('emails:send') + ->daily() + ->environments(['staging', 'production']); +``` ### Timezones Using the `timezone` method, you may specify that a scheduled task's time should be interpreted within a given timezone: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('report:generate') - ->timezone('America/New_York') - ->at('2:00') +Schedule::command('report:generate') + ->timezone('America/New_York') + ->at('2:00') +``` If you are repeatedly assigning the same timezone to all of your scheduled tasks, you can specify which timezone should be assigned to all schedules by defining a `schedule_timezone` option within your application's `app` configuration file: - 'timezone' => 'UTC', +```php +'timezone' => 'UTC', - 'schedule_timezone' => 'America/Chicago', +'schedule_timezone' => 'America/Chicago', +``` > [!WARNING] > Remember that some timezones utilize daylight savings time. When daylight saving time changes occur, your scheduled task may run twice or even not run at all. For this reason, we recommend avoiding timezone scheduling when possible. @@ -289,15 +327,19 @@ If you are repeatedly assigning the same timezone to all of your scheduled tasks By default, scheduled tasks will be run even if the previous instance of the task is still running. To prevent this, you may use the `withoutOverlapping` method: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('emails:send')->withoutOverlapping(); +Schedule::command('emails:send')->withoutOverlapping(); +``` In this example, the `emails:send` [Artisan command](/docs/{{version}}/artisan) will be run every minute if it is not already running. The `withoutOverlapping` method is especially useful if you have tasks that vary drastically in their execution time, preventing you from predicting exactly how long a given task will take. If needed, you may specify how many minutes must pass before the "without overlapping" lock expires. By default, the lock will expire after 24 hours: - Schedule::command('emails:send')->withoutOverlapping(10); +```php +Schedule::command('emails:send')->withoutOverlapping(10); +``` Behind the scenes, the `withoutOverlapping` method utilizes your application's [cache](/docs/{{version}}/cache) to obtain locks. If necessary, you can clear these cache locks using the `schedule:clear-cache` Artisan command. This is typically only necessary if a task becomes stuck due to an unexpected server problem. @@ -311,12 +353,14 @@ If your application's scheduler is running on multiple servers, you may limit a 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: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('report:generate') - ->fridays() - ->at('17:00') - ->onOneServer(); +Schedule::command('report:generate') + ->fridays() + ->at('17:00') + ->onOneServer(); +``` #### Naming Single Server Jobs @@ -349,11 +393,13 @@ Schedule::call(fn () => User::resetApiRequestCount()) 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: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('analytics:report') - ->daily() - ->runInBackground(); +Schedule::command('analytics:report') + ->daily() + ->runInBackground(); +``` > [!WARNING] > The `runInBackground` method may only be used when scheduling tasks via the `command` and `exec` methods. @@ -363,7 +409,9 @@ By default, multiple tasks scheduled at the same time will execute sequentially 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(); +```php +Schedule::command('emails:send')->evenInMaintenanceMode(); +``` ### Schedule Groups @@ -400,21 +448,25 @@ So, when using Laravel's scheduler, we only need to add a single cron configurat On most operating systems, cron jobs are limited to running a maximum of once per minute. However, Laravel's scheduler allows you to schedule tasks to run at more frequent intervals, even as often as once per second: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::call(function () { - DB::table('recent_users')->delete(); - })->everySecond(); +Schedule::call(function () { + DB::table('recent_users')->delete(); +})->everySecond(); +``` When sub-minute tasks are defined within your application, the `schedule:run` command will continue running until the end of the current minute instead of exiting immediately. This allows the command to invoke all required sub-minute tasks throughout the minute. Since sub-minute tasks that take longer than expected to run could delay the execution of later sub-minute tasks, it is recommended that all sub-minute tasks dispatch queued jobs or background commands to handle the actual task processing: - use App\Jobs\DeleteRecentUsers; +```php +use App\Jobs\DeleteRecentUsers; - Schedule::job(new DeleteRecentUsers)->everyTenSeconds(); +Schedule::job(new DeleteRecentUsers)->everyTenSeconds(); - Schedule::command('users:delete')->everyTenSeconds()->runInBackground(); +Schedule::command('users:delete')->everyTenSeconds()->runInBackground(); +``` #### Interrupting Sub-Minute Tasks @@ -441,30 +493,38 @@ php artisan schedule:work The Laravel scheduler provides several convenient methods for working with the output generated by scheduled tasks. First, using the `sendOutputTo` method, you may send the output to a file for later inspection: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('emails:send') - ->daily() - ->sendOutputTo($filePath); +Schedule::command('emails:send') + ->daily() + ->sendOutputTo($filePath); +``` If you would like to append the output to a given file, you may use the `appendOutputTo` method: - Schedule::command('emails:send') - ->daily() - ->appendOutputTo($filePath); +```php +Schedule::command('emails:send') + ->daily() + ->appendOutputTo($filePath); +``` 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('report:generate') - ->daily() - ->sendOutputTo($filePath) - ->emailOutputTo('taylor@example.com'); +```php +Schedule::command('report:generate') + ->daily() + ->sendOutputTo($filePath) + ->emailOutputTo('taylor@example.com'); +``` 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('report:generate') - ->daily() - ->emailOutputOnFailure('taylor@example.com'); +```php +Schedule::command('report:generate') + ->daily() + ->emailOutputOnFailure('taylor@example.com'); +``` > [!WARNING] > The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods. @@ -474,69 +534,81 @@ If you only want to email the output if the scheduled Artisan or system command Using the `before` and `after` methods, you may specify code to be executed before and after the scheduled task is executed: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('emails:send') - ->daily() - ->before(function () { - // The task is about to execute... - }) - ->after(function () { - // The task has executed... - }); +Schedule::command('emails:send') + ->daily() + ->before(function () { + // The task is about to execute... + }) + ->after(function () { + // The task has executed... + }); +``` 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() - ->onSuccess(function () { - // The task succeeded... - }) - ->onFailure(function () { - // The task failed... - }); +```php +Schedule::command('emails:send') + ->daily() + ->onSuccess(function () { + // The task succeeded... + }) + ->onFailure(function () { + // 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 the `$output` argument of your hook's closure definition: - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Stringable; - Schedule::command('emails:send') - ->daily() - ->onSuccess(function (Stringable $output) { - // The task succeeded... - }) - ->onFailure(function (Stringable $output) { - // The task failed... - }); +Schedule::command('emails:send') + ->daily() + ->onSuccess(function (Stringable $output) { + // The task succeeded... + }) + ->onFailure(function (Stringable $output) { + // The task failed... + }); +``` #### Pinging URLs 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); +```php +Schedule::command('emails:send') + ->daily() + ->pingBefore($url) + ->thenPing($url); +``` 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); +```php +Schedule::command('emails:send') + ->daily() + ->pingOnSuccess($successUrl) + ->pingOnFailure($failureUrl); +``` The `pingBeforeIf`,`thenPingIf`,`pingOnSuccessIf`, and `pingOnFailureIf` 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); +```php +Schedule::command('emails:send') + ->daily() + ->pingBeforeIf($condition, $url) + ->thenPingIf($condition, $url); - Schedule::command('emails:send') - ->daily() - ->pingOnSuccessIf($condition, $successUrl) - ->pingOnFailureIf($condition, $failureUrl); +Schedule::command('emails:send') + ->daily() + ->pingOnSuccessIf($condition, $successUrl) + ->pingOnFailureIf($condition, $failureUrl); +``` ## Events diff --git a/scout.md b/scout.md index 6146ea36e4c..346be125376 100644 --- a/scout.md +++ b/scout.md @@ -54,17 +54,19 @@ 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 that will automatically keep the model in sync with your search driver: - ### Queueing @@ -73,20 +75,26 @@ While not strictly required to use Scout, you should strongly consider configuri 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, +```php +'queue' => true, +``` Even when the `queue` option is set to `false`, it's important to remember that some Scout drivers like Algolia and Meilisearch always index records asynchronously. Meaning, even though the index operation has completed within your Laravel application, the search engine itself may not reflect the new and updated records immediately. To specify the connection and queue that your Scout jobs utilize, you may define the `queue` configuration option as an array: - 'queue' => [ - 'connection' => 'redis', - 'queue' => 'scout' - ], +```php +'queue' => [ + 'connection' => 'redis', + 'queue' => 'scout' +], +``` Of course, if you customize the connection and queue that Scout jobs utilize, you should run a queue worker to process jobs on that connection and queue: - php artisan queue:work redis --queue=scout +```shell +php artisan queue:work redis --queue=scout +``` ## Driver Prerequisites @@ -219,67 +227,73 @@ Todo::search('Groceries')->options([ Each Eloquent model is synced with a given search "index", which contains all of the searchable records for that model. In other words, you can think of each index like a MySQL table. By default, each model will be persisted to an index matching the model's typical "table" name. Typically, this is the plural form of the model name; however, you are free to customize the model's index by overriding the `searchableAs` method on the model: - ### Configuring Searchable Data By default, the entire `toArray` form of a given model will be persisted to its search index. If you would like to customize the data that is synchronized to the search index, you may override the `toSearchableArray` method on the model: - - */ - public function toSearchableArray(): array - { - $array = $this->toArray(); + /** + * Get the indexable data array for the model. + * + * @return array + */ + public function toSearchableArray(): array + { + $array = $this->toArray(); - // Customize the data array... + // Customize the data array... - return $array; - } + return $array; } +} +``` Some search engines such as Meilisearch will only perform filter operations (`>`, `<`, etc.) on data of the correct type. So, when using these search engines and customizing your searchable data, you should ensure that numeric values are cast to their correct type: - public function toSearchableArray() - { - return [ - 'id' => (int) $this->id, - 'name' => $this->name, - 'price' => (float) $this->price, - ]; - } +```php +public function toSearchableArray() +{ + return [ + 'id' => (int) $this->id, + 'name' => $this->name, + 'price' => (float) $this->price, + ]; +} +``` #### Configuring Index Settings (Algolia) @@ -371,60 +385,64 @@ php artisan scout:sync-index-settings By default, Scout will use the primary key of the model as the 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: - email; + } - class User extends Model + /** + * Get the key name used to index the model. + */ + public function getScoutKeyName(): mixed { - use Searchable; - - /** - * Get the value used to index the model. - */ - public function getScoutKey(): mixed - { - return $this->email; - } - - /** - * Get the key name used to index the model. - */ - public function getScoutKeyName(): mixed - { - return 'email'; - } + return 'email'; } +} +``` ### Configuring Search Engines per Model When searching, Scout will typically use the default search engine specified in your application's `scout` configuration file. However, the search engine for a particular model can be changed by overriding the `searchableUsing` method on the model: - engine('meilisearch'); - } + return app(EngineManager::class)->engine('meilisearch'); } +} +``` ### Identifying Users @@ -529,15 +547,17 @@ php artisan scout:flush "App\Models\Post" If you would like to modify the query that is used to retrieve all of your models for batch importing, you may define a `makeAllSearchableUsing` method on your model. This is a great place to add any eager relationship loading that may be necessary before importing your models: - use Illuminate\Database\Eloquent\Builder; +```php +use Illuminate\Database\Eloquent\Builder; - /** - * Modify the query used to retrieve models when making all of the models searchable. - */ - protected function makeAllSearchableUsing(Builder $query): Builder - { - return $query->with('author'); - } +/** + * Modify the query used to retrieve models when making all of the models searchable. + */ +protected function makeAllSearchableUsing(Builder $query): Builder +{ + return $query->with('author'); +} +``` > [!WARNING] > The `makeAllSearchableUsing` method may not be applicable when using a queue to batch import models. Relationships are [not restored](/docs/{{version}}/queues#handling-relationships) when model collections are processed by jobs. @@ -547,30 +567,38 @@ If you would like to modify the query that is used to retrieve all of your model 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; +```php +use App\Models\Order; - $order = new Order; +$order = new Order; - // ... +// ... - $order->save(); +$order->save(); +``` #### 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; +```php +use App\Models\Order; - Order::where('price', '>', 100)->searchable(); +Order::where('price', '>', 100)->searchable(); +``` You may also call the `searchable` method on an Eloquent relationship instance: - $user->orders()->searchable(); +```php +$user->orders()->searchable(); +``` 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(); +```php +$orders->searchable(); +``` > [!NOTE] > 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. @@ -580,91 +608,115 @@ Or, if you already have a collection of Eloquent models in memory, you may call 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: - use App\Models\Order; +```php +use App\Models\Order; - $order = Order::find(1); +$order = Order::find(1); - // Update the order... +// Update the order... - $order->save(); +$order->save(); +``` 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: - Order::where('price', '>', 100)->searchable(); +```php +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: - $user->orders()->searchable(); +```php +$user->orders()->searchable(); +``` 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(); +```php +$orders->searchable(); +``` #### Modifying Records Before Importing Sometimes you may need to prepare the collection of models before they are made searchable. For instance, you may want to eager load a relationship so that the relationship data can be efficiently added to your search index. To accomplish this, define a `makeSearchableUsing` method on the corresponding model: - use Illuminate\Database\Eloquent\Collection; +```php +use Illuminate\Database\Eloquent\Collection; - /** - * Modify the collection of models being made searchable. - */ - public function makeSearchableUsing(Collection $models): Collection - { - return $models->load('author'); - } +/** + * Modify the collection of models being made searchable. + */ +public function makeSearchableUsing(Collection $models): Collection +{ + return $models->load('author'); +} +``` ### Removing Records 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: - use App\Models\Order; +```php +use App\Models\Order; - $order = Order::find(1); +$order = Order::find(1); - $order->delete(); +$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: - Order::where('price', '>', 100)->unsearchable(); +```php +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: - $user->orders()->unsearchable(); +```php +$user->orders()->unsearchable(); +``` 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(); +```php +$orders->unsearchable(); +``` To remove all of the model records from their corresponding index, you may invoke the `removeAllFromSearch` method: - Order::removeAllFromSearch(); +```php +Order::removeAllFromSearch(); +``` ### 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 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; +```php +use App\Models\Order; - Order::withoutSyncingToSearch(function () { - // Perform model actions... - }); +Order::withoutSyncingToSearch(function () { + // Perform model actions... +}); +``` ### Conditionally Searchable Model Instances 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. - */ - public function shouldBeSearchable(): bool - { - return $this->isPublished(); - } +```php +/** + * Determine if the model should be searchable. + */ +public function shouldBeSearchable(): bool +{ + return $this->isPublished(); +} +``` 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. @@ -676,52 +728,66 @@ The `shouldBeSearchable` method is only applied when manipulating models through 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: - use App\Models\Order; +```php +use App\Models\Order; - $orders = Order::search('Star Trek')->get(); +$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; +```php +use App\Models\Order; +use Illuminate\Http\Request; - Route::get('/search', function (Request $request) { - return Order::search($request->search)->get(); - }); +Route::get('/search', function (Request $request) { + return Order::search($request->search)->get(); +}); +``` 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(); +```php +$orders = 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 = Order::search('Star Trek') - ->within('tv_shows_popularity_desc') - ->get(); +```php +$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 an owner ID: - use App\Models\Order; +```php +use App\Models\Order; - $orders = Order::search('Star Trek')->where('user_id', 1)->get(); +$orders = Order::search('Star Trek')->where('user_id', 1)->get(); +``` In addition, the `whereIn` method may be used to verify that a given column's value is contained within the given array: - $orders = Order::search('Star Trek')->whereIn( - 'status', ['open', 'paid'] - )->get(); +```php +$orders = Order::search('Star Trek')->whereIn( + 'status', ['open', 'paid'] +)->get(); +``` The `whereNotIn` method verifies that the given column's value is not contained in the given array: - $orders = Order::search('Star Trek')->whereNotIn( - 'status', ['closed'] - )->get(); +```php +$orders = Order::search('Star Trek')->whereNotIn( + 'status', ['closed'] +)->get(); +``` Since a search index is not a relational database, more advanced "where" clauses are not currently supported. @@ -733,13 +799,17 @@ Since a search index is not a relational database, more advanced "where" clauses 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; +```php +use App\Models\Order; - $orders = 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 = Order::search('Star Trek')->paginate(15); +```php +$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: @@ -755,12 +825,14 @@ Once you have retrieved the results, you may display the results and render the 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: - use App\Models\Order; - use Illuminate\Http\Request; +```php +use App\Models\Order; +use Illuminate\Http\Request; - Route::get('/orders', function (Request $request) { - return Order::search($request->input('query'))->paginate(15); - }); +Route::get('/orders', function (Request $request) { + return Order::search($request->input('query'))->paginate(15); +}); +``` > [!WARNING] > Since search engines are not aware of your Eloquent model's global scope definitions, you should not utilize global scopes in applications that utilize Scout pagination. Or, you should recreate the global scope's constraints when searching via Scout. @@ -770,17 +842,21 @@ Of course, if you would like to retrieve the pagination results as JSON, you may If your indexed models are [soft deleting](/docs/{{version}}/eloquent#soft-deleting) and you need to search your soft deleted models, set the `soft_delete` option of the `config/scout.php` configuration file to `true`: - 'soft_delete' => true, +```php +'soft_delete' => true, +``` 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; +```php +use App\Models\Order; - // Include trashed records when retrieving results... - $orders = Order::search('Star Trek')->withTrashed()->get(); +// Include trashed records when retrieving results... +$orders = Order::search('Star Trek')->withTrashed()->get(); - // Only include trashed records when retrieving results... - $orders = Order::search('Star Trek')->onlyTrashed()->get(); +// Only include trashed records when retrieving results... +$orders = Order::search('Star Trek')->onlyTrashed()->get(); +``` > [!NOTE] > When a soft deleted model is permanently deleted using `forceDelete`, Scout will remove it from the search index automatically. @@ -790,20 +866,22 @@ When this configuration option is `true`, Scout will not remove soft deleted mod 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; +```php +use Algolia\AlgoliaSearch\SearchIndex; +use App\Models\Order; - 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(); +``` #### Customizing the Eloquent Results Query @@ -829,16 +907,18 @@ Since this callback is invoked after the relevant models have already been retri If one of the built-in Scout search engines doesn't fit your needs, you may write your own custom engine and register it with Scout. Your engine should extend the `Laravel\Scout\Engines\Engine` abstract class. This abstract class contains eight methods your custom engine must implement: - use Laravel\Scout\Builder; - - abstract public function update($models); - abstract public function delete($models); - abstract public function search(Builder $builder); - abstract public function paginate(Builder $builder, $perPage, $page); - abstract public function mapIds($results); - abstract public function map(Builder $builder, $results, $model); - abstract public function getTotalCount($results); - abstract public function flush($model); +```php +use Laravel\Scout\Builder; + +abstract public function update($models); +abstract public function delete($models); +abstract public function search(Builder $builder); +abstract public function paginate(Builder $builder, $perPage, $page); +abstract public function mapIds($results); +abstract public function map(Builder $builder, $results, $model); +abstract public function getTotalCount($results); +abstract public function flush($model); +``` You may find it helpful to review the implementations of these methods on the `Laravel\Scout\Engines\AlgoliaEngine` class. This class will provide you with a good starting point for learning how to implement each of these methods in your own engine. @@ -847,19 +927,23 @@ You may find it helpful to review the implementations of these methods on the `L 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; +```php +use App\ScoutExtensions\MySqlSearchEngine; +use Laravel\Scout\EngineManager; - /** - * Bootstrap any application services. - */ - public function boot(): void - { - resolve(EngineManager::class)->extend('mysql', function () { - return new MySqlSearchEngine; - }); - } +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + resolve(EngineManager::class)->extend('mysql', function () { + return new MySqlSearchEngine; + }); +} +``` 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', +```php +'driver' => 'mysql', +``` diff --git a/seeding.md b/seeding.md index 94433c619be..28f6970af67 100644 --- a/seeding.md +++ b/seeding.md @@ -28,29 +28,31 @@ A seeder class only contains one method by default: `run`. This method is called 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). @@ -62,62 +64,68 @@ Of course, manually specifying the attributes for each model seed is cumbersome. For example, let's create 50 users that each has one related post: - use App\Models\User; - - /** - * Run the database seeders. - */ - public function run(): void - { - User::factory() - ->count(50) - ->hasPosts(1) - ->create(); - } +```php +use App\Models\User; + +/** + * Run the database seeders. + */ +public function run(): void +{ + User::factory() + ->count(50) + ->hasPosts(1) + ->create(); +} +``` ### 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 too large. The `call` method accepts an array of seeder classes that should be executed: - /** - * Run the database seeders. - */ - public function run(): void - { - $this->call([ - UserSeeder::class, - PostSeeder::class, - CommentSeeder::class, - ]); - } +```php +/** + * Run the database seeders. + */ +public function run(): void +{ + $this->call([ + UserSeeder::class, + PostSeeder::class, + CommentSeeder::class, + ]); +} +``` ### Muting Model Events While running seeds, you may want to prevent models from dispatching events. You may achieve this using the `WithoutModelEvents` trait. When used, the `WithoutModelEvents` trait ensures no model events are dispatched, even if additional seed classes are executed via the `call` method: - call([ - UserSeeder::class, - ]); - } + $this->call([ + UserSeeder::class, + ]); } +} +``` ## Running Seeders diff --git a/session.md b/session.md index 55bb3797557..543cac5a0a6 100644 --- a/session.md +++ b/session.md @@ -72,53 +72,59 @@ Before using Redis sessions with Laravel, you will need to either install the Ph 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'); + $value = $request->session()->get('key'); - // ... + // ... - $user = $this->users->find($id); + $user = $this->users->find($id); - return view('user.profile', ['user' => $user]); - } + return view('user.profile', ['user' => $user]); } +} +``` When you retrieve an item from the session, you may also pass a default value as the second argument to the `get` method. This default value will be returned if the specified key does not exist in the session. If you pass a closure as the default value to the `get` method and the requested key does not exist, the closure will be executed and its result returned: - $value = $request->session()->get('key', 'default'); +```php +$value = $request->session()->get('key', 'default'); - $value = $request->session()->get('key', function () { - return 'default'; - }); +$value = $request->session()->get('key', function () { + return 'default'; +}); +``` #### The Global Session Helper 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 () { - // Retrieve a piece of data from the session... - $value = session('key'); +```php +Route::get('/home', function () { + // Retrieve a piece of data from the session... + $value = session('key'); - // Specifying a default value... - $value = session('key', 'default'); + // Specifying a default value... + $value = session('key', 'default'); - // Store a piece of data in the session... - session(['key' => 'value']); - }); + // Store a piece of data in the session... + session(['key' => 'value']); +}); +``` > [!NOTE] > There is little practical difference between using the session via an HTTP request instance versus using the global `session` helper. Both methods are [testable](/docs/{{version}}/testing) via the `assertSessionHas` method which is available in all of your test cases. @@ -128,105 +134,131 @@ You may also use the global `session` PHP function to retrieve and store data in If you would like to retrieve all the data in the session, you may use the `all` method: - $data = $request->session()->all(); +```php +$data = $request->session()->all(); +``` #### Retrieving a Portion of the Session Data The `only` and `except` methods may be used to retrieve a subset of the session data: - $data = $request->session()->only(['username', 'email']); +```php +$data = $request->session()->only(['username', 'email']); - $data = $request->session()->except(['username', 'email']); +$data = $request->session()->except(['username', 'email']); +``` #### Determining if an Item Exists in the Session To determine if an item is present in the session, you may use the `has` method. The `has` method returns `true` if the item is present and is not `null`: - if ($request->session()->has('users')) { - // ... - } +```php +if ($request->session()->has('users')) { + // ... +} +``` 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')) { - // ... - } +```php +if ($request->session()->exists('users')) { + // ... +} +``` To determine if an item is not present in the session, you may use the `missing` method. The `missing` method returns `true` if the item is not present: - if ($request->session()->missing('users')) { - // ... - } +```php +if ($request->session()->missing('users')) { + // ... +} +``` ### Storing Data To store data in the session, you will typically use the request instance's `put` method or the global `session` helper: - // Via a request instance... - $request->session()->put('key', 'value'); +```php +// Via a request instance... +$request->session()->put('key', 'value'); - // Via the global "session" helper... - session(['key' => 'value']); +// Via the global "session" helper... +session(['key' => 'value']); +``` #### Pushing to Array Session Values The `push` method may be used to push a new value onto a session value that is an array. For example, if the `user.teams` key contains an array of team names, you may push a new value onto the array like so: - $request->session()->push('user.teams', 'developers'); +```php +$request->session()->push('user.teams', 'developers'); +``` #### Retrieving and Deleting an Item The `pull` method will retrieve and delete an item from the session in a single statement: - $value = $request->session()->pull('key', 'default'); +```php +$value = $request->session()->pull('key', 'default'); +``` #### Incrementing and Decrementing Session Values If your session data contains an integer you wish to increment or decrement, you may use the `increment` and `decrement` methods: - $request->session()->increment('count'); +```php +$request->session()->increment('count'); - $request->session()->increment('count', $incrementBy = 2); +$request->session()->increment('count', $incrementBy = 2); - $request->session()->decrement('count'); +$request->session()->decrement('count'); - $request->session()->decrement('count', $decrementBy = 2); +$request->session()->decrement('count', $decrementBy = 2); +``` ### Flash Data 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!'); +```php +$request->session()->flash('status', 'Task was successful!'); +``` 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(); +```php +$request->session()->reflash(); - $request->session()->keep(['username', 'email']); +$request->session()->keep(['username', 'email']); +``` To persist your flash data only for the current request, you may use the `now` method: - $request->session()->now('status', 'Task was successful!'); +```php +$request->session()->now('status', 'Task was successful!'); +``` ### Deleting Data 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('name'); +```php +// Forget a single key... +$request->session()->forget('name'); - // Forget multiple keys... - $request->session()->forget(['name', 'status']); +// Forget multiple keys... +$request->session()->forget(['name', 'status']); - $request->session()->flush(); +$request->session()->flush(); +``` ### Regenerating the Session ID @@ -235,11 +267,15 @@ Regenerating the session ID is often done in order to prevent malicious users fr 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](/docs/{{version}}/fortify); however, if you need to manually regenerate the session ID, you may use the `regenerate` method: - $request->session()->regenerate(); +```php +$request->session()->regenerate(); +``` If you need to regenerate the session ID and remove all data from the session in a single statement, you may use the `invalidate` method: - $request->session()->invalidate(); +```php +$request->session()->invalidate(); +``` ## Session Blocking @@ -251,13 +287,15 @@ By default, Laravel allows requests using the same session to execute concurrent To mitigate this, Laravel provides functionality that allows you to limit concurrent requests for a given session. To get started, you may simply chain the `block` method onto your route definition. In this example, an incoming request to the `/profile` endpoint would acquire a session lock. While this lock is being held, any incoming requests to the `/profile` or `/order` endpoints which share the same session ID will wait for the first request to finish executing before continuing their execution: - Route::post('/profile', function () { - // ... - })->block($lockSeconds = 10, $waitSeconds = 10); +```php +Route::post('/profile', function () { + // ... +})->block($lockSeconds = 10, $waitSeconds = 10); - Route::post('/order', function () { - // ... - })->block($lockSeconds = 10, $waitSeconds = 10); +Route::post('/order', function () { + // ... +})->block($lockSeconds = 10, $waitSeconds = 10); +``` The `block` method accepts two optional arguments. The first argument accepted by the `block` method is the maximum number of seconds the session lock should be held for before it is released. Of course, if the request finishes executing before this time the lock will be released earlier. @@ -265,9 +303,11 @@ The second argument accepted by the `block` method is the number of seconds a re If neither of these arguments is passed, the lock will be obtained for a maximum of 10 seconds and requests will wait a maximum of 10 seconds while attempting to obtain a lock: - Route::post('/profile', function () { - // ... - })->block(); +```php +Route::post('/profile', function () { + // ... +})->block(); +``` ## Adding Custom Session Drivers @@ -277,19 +317,21 @@ If neither of these arguments is passed, the lock will be obtained for a maximum 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: - [ - 'client_id' => env('GITHUB_CLIENT_ID'), - 'client_secret' => env('GITHUB_CLIENT_SECRET'), - 'redirect' => 'http://example.com/callback-url', - ], +```php +'github' => [ + 'client_id' => env('GITHUB_CLIENT_ID'), + 'client_secret' => env('GITHUB_CLIENT_SECRET'), + 'redirect' => 'http://example.com/callback-url', +], +``` > [!NOTE] > If the `redirect` option contains a relative path, it will automatically be resolved to a fully qualified URL. @@ -58,17 +60,19 @@ These credentials should be placed in your application's `config/services.php` c 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 routes below demonstrate the implementation of both routes: - use Laravel\Socialite\Facades\Socialite; +```php +use Laravel\Socialite\Facades\Socialite; - Route::get('/auth/redirect', function () { - return Socialite::driver('github')->redirect(); - }); +Route::get('/auth/redirect', function () { + return Socialite::driver('github')->redirect(); +}); - Route::get('/auth/callback', function () { - $user = Socialite::driver('github')->user(); +Route::get('/auth/callback', function () { + $user = Socialite::driver('github')->user(); - // $user->token - }); + // $user->token +}); +``` The `redirect` method provided by the `Socialite` facade takes care of redirecting the user to the OAuth provider, while the `user` method will examine the incoming request and retrieve the user's information from the provider after they have approved the authentication request. @@ -77,26 +81,28 @@ The `redirect` method provided by the `Socialite` facade takes care of redirecti Once the user has been retrieved from the OAuth provider, you may determine if the user exists in your application's database and [authenticate the user](/docs/{{version}}/authentication#authenticate-a-user-instance). If the user does not exist in your application's database, you will typically create a new record in your database to represent the user: - use App\Models\User; - use Illuminate\Support\Facades\Auth; - use Laravel\Socialite\Facades\Socialite; +```php +use App\Models\User; +use Illuminate\Support\Facades\Auth; +use Laravel\Socialite\Facades\Socialite; - Route::get('/auth/callback', function () { - $githubUser = Socialite::driver('github')->user(); +Route::get('/auth/callback', function () { + $githubUser = Socialite::driver('github')->user(); - $user = User::updateOrCreate([ - 'github_id' => $githubUser->id, - ], [ - 'name' => $githubUser->name, - 'email' => $githubUser->email, - 'github_token' => $githubUser->token, - 'github_refresh_token' => $githubUser->refreshToken, - ]); + $user = User::updateOrCreate([ + 'github_id' => $githubUser->id, + ], [ + 'name' => $githubUser->name, + 'email' => $githubUser->email, + 'github_token' => $githubUser->token, + 'github_refresh_token' => $githubUser->refreshToken, + ]); - Auth::login($user); + Auth::login($user); - return redirect('/dashboard'); - }); + return redirect('/dashboard'); +}); +``` > [!NOTE] > For more information regarding what user information is available from specific OAuth providers, please consult the documentation on [retrieving user details](#retrieving-user-details). @@ -106,17 +112,21 @@ Once the user has been retrieved from the OAuth provider, you may determine if t Before redirecting the user, you may use the `scopes` method to specify the "scopes" that should be included in the authentication request. This method will merge all previously specified scopes with the scopes that you specify: - use Laravel\Socialite\Facades\Socialite; +```php +use Laravel\Socialite\Facades\Socialite; - return Socialite::driver('github') - ->scopes(['read:user', 'public_repo']) - ->redirect(); +return Socialite::driver('github') + ->scopes(['read:user', 'public_repo']) + ->redirect(); +``` You can overwrite all existing scopes on the authentication request using the `setScopes` method: - return Socialite::driver('github') - ->setScopes(['read:user', 'public_repo']) - ->redirect(); +```php +return Socialite::driver('github') + ->setScopes(['read:user', 'public_repo']) + ->redirect(); +``` ### Slack Bot Scopes @@ -134,14 +144,18 @@ By default, the `slack` driver will generate a `user` token and invoking the dri Bot tokens are primarily useful if your application will be sending notifications to external Slack workspaces that are owned by your application's users. To generate a bot token, invoke the `asBotUser` method before redirecting the user to Slack for authentication: - return Socialite::driver('slack') - ->asBotUser() - ->setScopes(['chat:write', 'chat:write.public', 'chat:write.customize']) - ->redirect(); +```php +return Socialite::driver('slack') + ->asBotUser() + ->setScopes(['chat:write', 'chat:write.public', 'chat:write.customize']) + ->redirect(); +``` In addition, you must invoke the `asBotUser` method before invoking the `user` method after Slack redirects the user back to your application after authentication: - $user = Socialite::driver('slack')->asBotUser()->user(); +```php +$user = Socialite::driver('slack')->asBotUser()->user(); +``` When generating a bot token, the `user` method will still return a `Laravel\Socialite\Two\User` instance; however, only the `token` property will be hydrated. This token may be stored in order to [send notifications to the authenticated user's Slack workspaces](/docs/{{version}}/notifications#notifying-external-slack-workspaces). @@ -150,11 +164,13 @@ When generating a bot token, the `user` method will still return a `Laravel\Soci A number of OAuth providers support other optional parameters on the redirect request. To include any optional parameters in the request, call the `with` method with an associative array: - use Laravel\Socialite\Facades\Socialite; +```php +use Laravel\Socialite\Facades\Socialite; - return Socialite::driver('google') - ->with(['hd' => 'example.com']) - ->redirect(); +return Socialite::driver('google') + ->with(['hd' => 'example.com']) + ->redirect(); +``` > [!WARNING] > When using the `with` method, be careful not to pass any reserved keywords such as `state` or `response_type`. @@ -166,36 +182,40 @@ After the user is redirected back to your application's authentication callback Differing properties and methods may be available on this object depending on whether the OAuth provider you are authenticating with supports OAuth 1.0 or OAuth 2.0: - use Laravel\Socialite\Facades\Socialite; +```php +use Laravel\Socialite\Facades\Socialite; - Route::get('/auth/callback', function () { - $user = Socialite::driver('github')->user(); +Route::get('/auth/callback', function () { + $user = Socialite::driver('github')->user(); - // OAuth 2.0 providers... - $token = $user->token; - $refreshToken = $user->refreshToken; - $expiresIn = $user->expiresIn; + // OAuth 2.0 providers... + $token = $user->token; + $refreshToken = $user->refreshToken; + $expiresIn = $user->expiresIn; - // OAuth 1.0 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 If you already have a valid access token for a user, you can retrieve their user details using Socialite's `userFromToken` method: - use Laravel\Socialite\Facades\Socialite; +```php +use Laravel\Socialite\Facades\Socialite; - $user = Socialite::driver('github')->userFromToken($token); +$user = Socialite::driver('github')->userFromToken($token); +``` If you are using Facebook Limited Login via an iOS application, Facebook will return an OIDC token instead of an access token. Like an access token, the OIDC token can be provided to the `userFromToken` method in order to retrieve user details. @@ -204,6 +224,8 @@ If you are using Facebook Limited Login via an iOS application, Facebook will re The `stateless` method may be used to disable session state verification. This is useful when adding social authentication to a stateless API that does not utilize cookie based sessions: - use Laravel\Socialite\Facades\Socialite; +```php +use Laravel\Socialite\Facades\Socialite; - return Socialite::driver('google')->stateless()->user(); +return Socialite::driver('google')->stateless()->user(); +``` diff --git a/strings.md b/strings.md index 15166f189fd..1a2986f95b8 100644 --- a/strings.md +++ b/strings.md @@ -241,9 +241,11 @@ Laravel includes a variety of functions for manipulating string values. Many of The `__` function translates the given translation string or translation key using your [language files](/docs/{{version}}/localization): - echo __('Welcome to our application'); +```php +echo __('Welcome to our application'); - echo __('messages.welcome'); +echo __('messages.welcome'); +``` If the specified translation string or key does not exist, the `__` function will return the given value. So, using the example above, the `__` function would return `messages.welcome` if that translation key does not exist. @@ -252,762 +254,898 @@ If the specified translation string or key does not exist, the `__` function wil The `class_basename` function returns the class name of the given class with the class's namespace removed: - $class = class_basename('Foo\Bar\Baz'); +```php +$class = class_basename('Foo\Bar\Baz'); - // Baz +// Baz +``` #### `e()` {.collection-method} The `e` function runs PHP's `htmlspecialchars` function with the `double_encode` option set to `true` by default: - echo e('foo'); +```php +echo e('foo'); - // <html>foo</html> +// <html>foo</html> +``` #### `preg_replace_array()` {.collection-method} The `preg_replace_array` function replaces a given pattern in the string sequentially using an array: - $string = 'The event will take place between :start and :end'; +```php +$string = 'The event will take place between :start and :end'; - $replaced = preg_replace_array('/:[a-z_]+/', ['8:30', '9:00'], $string); +$replaced = preg_replace_array('/:[a-z_]+/', ['8:30', '9:00'], $string); - // The event will take place between 8:30 and 9:00 +// The event will take place between 8:30 and 9:00 +``` #### `Str::after()` {.collection-method} The `Str::after` method returns everything after the given value in a string. The entire string will be returned if the value does not exist within the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::after('This is my name', 'This is'); +$slice = Str::after('This is my name', 'This is'); - // ' my name' +// ' my name' +``` #### `Str::afterLast()` {.collection-method} The `Str::afterLast` method returns everything after the last occurrence of the given value in a string. The entire string will be returned if the value does not exist within the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::afterLast('App\Http\Controllers\Controller', '\\'); +$slice = Str::afterLast('App\Http\Controllers\Controller', '\\'); - // 'Controller' +// 'Controller' +``` #### `Str::apa()` {.collection-method} The `Str::apa` method converts the given string to title case following the [APA guidelines](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $title = Str::apa('Creating A Project'); +$title = Str::apa('Creating A Project'); - // 'Creating a Project' +// 'Creating a Project' +``` #### `Str::ascii()` {.collection-method} The `Str::ascii` method will attempt to transliterate the string into an ASCII value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::ascii('û'); +$slice = Str::ascii('û'); - // 'u' +// 'u' +``` #### `Str::before()` {.collection-method} The `Str::before` method returns everything before the given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::before('This is my name', 'my name'); +$slice = Str::before('This is my name', 'my name'); - // 'This is ' +// 'This is ' +``` #### `Str::beforeLast()` {.collection-method} The `Str::beforeLast` method returns everything before the last occurrence of the given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::beforeLast('This is my name', 'is'); +$slice = Str::beforeLast('This is my name', 'is'); - // 'This ' +// 'This ' +``` #### `Str::between()` {.collection-method} The `Str::between` method returns the portion of a string between two values: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::between('This is my name', 'This', 'name'); +$slice = Str::between('This is my name', 'This', 'name'); - // ' is my ' +// ' is my ' +``` #### `Str::betweenFirst()` {.collection-method} The `Str::betweenFirst` method returns the smallest possible portion of a string between two values: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::betweenFirst('[a] bc [d]', '[', ']'); +$slice = Str::betweenFirst('[a] bc [d]', '[', ']'); - // 'a' +// 'a' +``` #### `Str::camel()` {.collection-method} The `Str::camel` method converts the given string to `camelCase`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::camel('foo_bar'); +$converted = Str::camel('foo_bar'); - // 'fooBar' +// 'fooBar' +``` #### `Str::charAt()` {.collection-method} The `Str::charAt` method returns the character at the specified index. If the index is out of bounds, `false` is returned: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $character = Str::charAt('This is my name.', 6); +$character = Str::charAt('This is my name.', 6); - // 's' +// 's' +``` #### `Str::chopStart()` {.collection-method} The `Str::chopStart` method removes the first occurrence of the given value only if the value appears at the start of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::chopStart('https://laravel.com/', 'https://'); +$url = Str::chopStart('https://laravel.com/', 'https://'); - // 'laravel.com' +// 'laravel.com' +``` You may also pass an array as the second argument. If the string starts with any of the values in the array then that value will be removed from string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::chopStart('http://laravel.com/', ['https://', 'http://']); +$url = Str::chopStart('http://laravel.com/', ['https://', 'http://']); - // 'laravel.com' +// 'laravel.com' +``` #### `Str::chopEnd()` {.collection-method} The `Str::chopEnd` method removes the last occurrence of the given value only if the value appears at the end of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::chopEnd('app/Models/Photograph.php', '.php'); +$url = Str::chopEnd('app/Models/Photograph.php', '.php'); - // 'app/Models/Photograph' +// 'app/Models/Photograph' +``` You may also pass an array as the second argument. If the string ends with any of the values in the array then that value will be removed from string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::chopEnd('laravel.com/index.php', ['/index.html', '/index.php']); +$url = Str::chopEnd('laravel.com/index.php', ['/index.html', '/index.php']); - // 'laravel.com' +// 'laravel.com' +``` #### `Str::contains()` {.collection-method} The `Str::contains` method determines if the given string contains the given value. By default this method is case sensitive: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $contains = Str::contains('This is my name', 'my'); +$contains = Str::contains('This is my name', 'my'); - // true +// true +``` You may also pass an array of values to determine if the given string contains any of the values in the array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $contains = Str::contains('This is my name', ['my', 'foo']); +$contains = Str::contains('This is my name', ['my', 'foo']); - // true +// true +``` You may disable case sensitivity by setting the `ignoreCase` argument to `true`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $contains = Str::contains('This is my name', 'MY', ignoreCase: true); +$contains = Str::contains('This is my name', 'MY', ignoreCase: true); - // true +// true +``` #### `Str::containsAll()` {.collection-method} The `Str::containsAll` method determines if the given string contains all of the values in a given array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $containsAll = Str::containsAll('This is my name', ['my', 'name']); +$containsAll = Str::containsAll('This is my name', ['my', 'name']); - // true +// true +``` You may disable case sensitivity by setting the `ignoreCase` argument to `true`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $containsAll = Str::containsAll('This is my name', ['MY', 'NAME'], ignoreCase: true); +$containsAll = Str::containsAll('This is my name', ['MY', 'NAME'], ignoreCase: true); - // true +// true +``` #### `Str::doesntContain()` {.collection-method} The `Str::doesntContain` method determines if the given string doesn't contain the given value. By default this method is case sensitive: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $doesntContain = Str::doesntContain('This is name', 'my'); +$doesntContain = Str::doesntContain('This is name', 'my'); - // true +// true +``` You may also pass an array of values to determine if the given string doesn't contain any of the values in the array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $doesntContain = Str::doesntContain('This is name', ['my', 'foo']); +$doesntContain = Str::doesntContain('This is name', ['my', 'foo']); - // true +// true +``` You may disable case sensitivity by setting the `ignoreCase` argument to `true`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $doesntContain = Str::doesntContain('This is name', 'MY', ignoreCase: true); +$doesntContain = Str::doesntContain('This is name', 'MY', ignoreCase: true); - // true +// true +``` #### `Str::deduplicate()` {.collection-method} The `Str::deduplicate` method replaces consecutive instances of a character with a single instance of that character in the given string. By default, the method deduplicates spaces: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::deduplicate('The Laravel Framework'); +$result = Str::deduplicate('The Laravel Framework'); - // The Laravel Framework +// The Laravel Framework +``` You may specify a different character to deduplicate by passing it in as the second argument to the method: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::deduplicate('The---Laravel---Framework', '-'); +$result = Str::deduplicate('The---Laravel---Framework', '-'); - // The-Laravel-Framework +// The-Laravel-Framework +``` #### `Str::endsWith()` {.collection-method} The `Str::endsWith` method determines if the given string ends with the given value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::endsWith('This is my name', 'name'); +$result = Str::endsWith('This is my name', 'name'); - // true +// true +``` You may also pass an array of values to determine if the given string ends with any of the values in the array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::endsWith('This is my name', ['name', 'foo']); +$result = Str::endsWith('This is my name', ['name', 'foo']); - // true +// true - $result = Str::endsWith('This is my name', ['this', 'foo']); +$result = Str::endsWith('This is my name', ['this', 'foo']); - // false +// false +``` #### `Str::excerpt()` {.collection-method} The `Str::excerpt` method extracts an excerpt from a given string that matches the first instance of a phrase within that string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $excerpt = Str::excerpt('This is my name', 'my', [ - 'radius' => 3 - ]); +$excerpt = Str::excerpt('This is my name', 'my', [ + 'radius' => 3 +]); - // '...is my na...' +// '...is my na...' +``` The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. In addition, you may use the `omission` option to define the string that will be prepended and appended to the truncated string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $excerpt = Str::excerpt('This is my name', 'name', [ - 'radius' => 3, - 'omission' => '(...) ' - ]); +$excerpt = Str::excerpt('This is my name', 'name', [ + 'radius' => 3, + 'omission' => '(...) ' +]); - // '(...) my name' +// '(...) my name' +``` #### `Str::finish()` {.collection-method} The `Str::finish` method adds a single instance of the given value to a string if it does not already end with that value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $adjusted = Str::finish('this/string', '/'); +$adjusted = Str::finish('this/string', '/'); - // this/string/ +// this/string/ - $adjusted = Str::finish('this/string/', '/'); +$adjusted = Str::finish('this/string/', '/'); - // this/string/ +// this/string/ +``` #### `Str::headline()` {.collection-method} The `Str::headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $headline = Str::headline('steve_jobs'); +$headline = Str::headline('steve_jobs'); - // Steve Jobs +// Steve Jobs - $headline = Str::headline('EmailNotificationSent'); +$headline = Str::headline('EmailNotificationSent'); - // Email Notification Sent +// Email Notification Sent +``` #### `Str::inlineMarkdown()` {.collection-method} The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $html = Str::inlineMarkdown('**Laravel**'); +$html = Str::inlineMarkdown('**Laravel**'); - // Laravel +// Laravel +``` #### Markdown Security By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - Str::inlineMarkdown('Inject: ', [ - 'html_input' => 'strip', - 'allow_unsafe_links' => false, - ]); +Str::inlineMarkdown('Inject: ', [ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, +]); - // Inject: alert("Hello XSS!"); +// Inject: alert("Hello XSS!"); +``` #### `Str::is()` {.collection-method} The `Str::is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $matches = Str::is('foo*', 'foobar'); +$matches = Str::is('foo*', 'foobar'); - // true +// true - $matches = Str::is('baz*', 'foobar'); +$matches = Str::is('baz*', 'foobar'); - // false +// false +``` You may disable case sensitivity by setting the `ignoreCase` argument to `true`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $matches = Str::is('*.jpg', 'photo.JPG', ignoreCase: true); +$matches = Str::is('*.jpg', 'photo.JPG', ignoreCase: true); - // true +// true +``` #### `Str::isAscii()` {.collection-method} The `Str::isAscii` method determines if a given string is 7 bit ASCII: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $isAscii = Str::isAscii('Taylor'); +$isAscii = Str::isAscii('Taylor'); - // true +// true - $isAscii = Str::isAscii('ü'); +$isAscii = Str::isAscii('ü'); - // false +// false +``` #### `Str::isJson()` {.collection-method} The `Str::isJson` method determines if the given string is valid JSON: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::isJson('[1,2,3]'); +$result = Str::isJson('[1,2,3]'); - // true +// true - $result = Str::isJson('{"first": "John", "last": "Doe"}'); +$result = Str::isJson('{"first": "John", "last": "Doe"}'); - // true +// true - $result = Str::isJson('{first: "John", last: "Doe"}'); +$result = Str::isJson('{first: "John", last: "Doe"}'); - // false +// false +``` #### `Str::isUrl()` {.collection-method} The `Str::isUrl` method determines if the given string is a valid URL: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $isUrl = Str::isUrl('http://example.com/'); +$isUrl = Str::isUrl('http://example.com/'); - // true +// true - $isUrl = Str::isUrl('laravel'); +$isUrl = Str::isUrl('laravel'); - // false +// false +``` The `isUrl` method considers a wide range of protocols as valid. However, you may specify the protocols that should be considered valid by providing them to the `isUrl` method: - $isUrl = Str::isUrl('http://example.com/', ['http', 'https']); +```php +$isUrl = Str::isUrl('http://example.com/', ['http', 'https']); +``` #### `Str::isUlid()` {.collection-method} The `Str::isUlid` method determines if the given string is a valid ULID: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $isUlid = Str::isUlid('01gd6r360bp37zj17nxb55yv40'); +$isUlid = Str::isUlid('01gd6r360bp37zj17nxb55yv40'); - // true +// true - $isUlid = Str::isUlid('laravel'); +$isUlid = Str::isUlid('laravel'); - // false +// false +``` #### `Str::isUuid()` {.collection-method} The `Str::isUuid` method determines if the given string is a valid UUID: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $isUuid = Str::isUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de'); +$isUuid = Str::isUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de'); - // true +// true - $isUuid = Str::isUuid('laravel'); +$isUuid = Str::isUuid('laravel'); - // false +// false +``` #### `Str::kebab()` {.collection-method} The `Str::kebab` method converts the given string to `kebab-case`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::kebab('fooBar'); +$converted = Str::kebab('fooBar'); - // foo-bar +// foo-bar +``` #### `Str::lcfirst()` {.collection-method} The `Str::lcfirst` method returns the given string with the first character lowercased: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::lcfirst('Foo Bar'); +$string = Str::lcfirst('Foo Bar'); - // foo Bar +// foo Bar +``` #### `Str::length()` {.collection-method} The `Str::length` method returns the length of the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $length = Str::length('Laravel'); +$length = Str::length('Laravel'); - // 7 +// 7 +``` #### `Str::limit()` {.collection-method} The `Str::limit` method truncates the given string to the specified length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20); +$truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20); - // The quick brown fox... +// The quick brown fox... +``` You may pass a third argument to the method to change the string that will be appended to the end of the truncated string: - $truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20, ' (...)'); +```php +$truncated = Str::limit('The quick brown fox jumps over the lazy dog', 20, ' (...)'); - // The quick brown fox (...) +// The quick brown fox (...) +``` If you would like to preserve complete words when truncating the string, you may utilize the `preserveWords` argument. When this argument is `true`, the string will be truncated to the nearest complete word boundary: - $truncated = Str::limit('The quick brown fox', 12, preserveWords: true); +```php +$truncated = Str::limit('The quick brown fox', 12, preserveWords: true); - // The quick... +// The quick... +``` #### `Str::lower()` {.collection-method} The `Str::lower` method converts the given string to lowercase: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::lower('LARAVEL'); +$converted = Str::lower('LARAVEL'); - // laravel +// laravel +``` #### `Str::markdown()` {.collection-method} The `Str::markdown` method converts GitHub flavored Markdown into HTML using [CommonMark](https://commonmark.thephpleague.com/): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $html = Str::markdown('# Laravel'); +$html = Str::markdown('# Laravel'); - //

    Laravel

    +//

    Laravel

    - $html = Str::markdown('# Taylor Otwell', [ - 'html_input' => 'strip', - ]); +$html = Str::markdown('# Taylor Otwell', [ + 'html_input' => 'strip', +]); - //

    Taylor Otwell

    +//

    Taylor Otwell

    +``` #### Markdown Security By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - Str::markdown('Inject: ', [ - 'html_input' => 'strip', - 'allow_unsafe_links' => false, - ]); +Str::markdown('Inject: ', [ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, +]); - //

    Inject: alert("Hello XSS!");

    +//

    Inject: alert("Hello XSS!");

    +``` #### `Str::mask()` {.collection-method} The `Str::mask` method masks a portion of a string with a repeated character, and may be used to obfuscate segments of strings such as email addresses and phone numbers: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::mask('taylor@example.com', '*', 3); +$string = Str::mask('taylor@example.com', '*', 3); - // tay*************** +// tay*************** +``` If needed, you provide a negative number as the third argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: - $string = Str::mask('taylor@example.com', '*', -15, 3); +```php +$string = Str::mask('taylor@example.com', '*', -15, 3); - // tay***@example.com +// tay***@example.com +``` #### `Str::orderedUuid()` {.collection-method} The `Str::orderedUuid` method generates a "timestamp first" UUID that may be efficiently stored in an indexed database column. Each UUID that is generated using this method will be sorted after UUIDs previously generated using the method: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - return (string) Str::orderedUuid(); +return (string) Str::orderedUuid(); +``` #### `Str::padBoth()` {.collection-method} The `Str::padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches a desired length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $padded = Str::padBoth('James', 10, '_'); +$padded = Str::padBoth('James', 10, '_'); - // '__James___' +// '__James___' - $padded = Str::padBoth('James', 10); +$padded = Str::padBoth('James', 10); - // ' James ' +// ' James ' +``` #### `Str::padLeft()` {.collection-method} The `Str::padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches a desired length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $padded = Str::padLeft('James', 10, '-='); +$padded = Str::padLeft('James', 10, '-='); - // '-=-=-James' +// '-=-=-James' - $padded = Str::padLeft('James', 10); +$padded = Str::padLeft('James', 10); - // ' James' +// ' James' +``` #### `Str::padRight()` {.collection-method} The `Str::padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches a desired length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $padded = Str::padRight('James', 10, '-'); +$padded = Str::padRight('James', 10, '-'); - // 'James-----' +// 'James-----' - $padded = Str::padRight('James', 10); +$padded = Str::padRight('James', 10); - // 'James ' +// 'James ' +``` #### `Str::password()` {.collection-method} The `Str::password` method may be used to generate a secure, random password of a given length. The password will consist of a combination of letters, numbers, symbols, and spaces. By default, passwords are 32 characters long: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $password = Str::password(); +$password = Str::password(); - // 'EbJo2vE-AS:U,$%_gkrV4n,q~1xy/-_4' +// 'EbJo2vE-AS:U,$%_gkrV4n,q~1xy/-_4' - $password = Str::password(12); +$password = Str::password(12); - // 'qwuar>#V|i]N' +// 'qwuar>#V|i]N' +``` #### `Str::plural()` {.collection-method} The `Str::plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $plural = Str::plural('car'); +$plural = Str::plural('car'); - // cars +// cars - $plural = Str::plural('child'); +$plural = Str::plural('child'); - // children +// children +``` You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $plural = Str::plural('child', 2); +$plural = Str::plural('child', 2); - // children +// children - $singular = Str::plural('child', 1); +$singular = Str::plural('child', 1); - // child +// child +``` #### `Str::pluralStudly()` {.collection-method} The `Str::pluralStudly` method converts a singular word string formatted in studly caps case to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $plural = Str::pluralStudly('VerifiedHuman'); +$plural = Str::pluralStudly('VerifiedHuman'); - // VerifiedHumans +// VerifiedHumans - $plural = Str::pluralStudly('UserFeedback'); +$plural = Str::pluralStudly('UserFeedback'); - // UserFeedback +// UserFeedback +``` You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $plural = Str::pluralStudly('VerifiedHuman', 2); +$plural = Str::pluralStudly('VerifiedHuman', 2); - // VerifiedHumans +// VerifiedHumans - $singular = Str::pluralStudly('VerifiedHuman', 1); +$singular = Str::pluralStudly('VerifiedHuman', 1); - // VerifiedHuman +// VerifiedHuman +``` #### `Str::position()` {.collection-method} The `Str::position` method returns the position of the first occurrence of a substring in a string. If the substring does not exist in the given string, `false` is returned: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $position = Str::position('Hello, World!', 'Hello'); +$position = Str::position('Hello, World!', 'Hello'); - // 0 +// 0 - $position = Str::position('Hello, World!', 'W'); +$position = Str::position('Hello, World!', 'W'); - // 7 +// 7 +``` #### `Str::random()` {.collection-method} The `Str::random` method generates a random string of the specified length. This function uses PHP's `random_bytes` function: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $random = Str::random(40); +$random = Str::random(40); +``` During testing, it may be useful to "fake" the value that is returned by the `Str::random` method. To accomplish this, you may use the `createRandomStringsUsing` method: - Str::createRandomStringsUsing(function () { - return 'fake-random-string'; - }); +```php +Str::createRandomStringsUsing(function () { + return 'fake-random-string'; +}); +``` To instruct the `random` method to return to generating random strings normally, you may invoke the `createRandomStringsNormally` method: - Str::createRandomStringsNormally(); +```php +Str::createRandomStringsNormally(); +``` #### `Str::remove()` {.collection-method} The `Str::remove` method removes the given value or array of values from the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = 'Peter Piper picked a peck of pickled peppers.'; +$string = 'Peter Piper picked a peck of pickled peppers.'; - $removed = Str::remove('e', $string); +$removed = Str::remove('e', $string); - // Ptr Pipr pickd a pck of pickld ppprs. +// Ptr Pipr pickd a pck of pickld ppprs. +``` You may also pass `false` as a third argument to the `remove` method to ignore case when removing strings. @@ -1031,383 +1169,449 @@ $repeat = Str::repeat($string, 5); The `Str::replace` method replaces a given string within the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = 'Laravel 10.x'; +$string = 'Laravel 10.x'; - $replaced = Str::replace('10.x', '11.x', $string); +$replaced = Str::replace('10.x', '11.x', $string); - // Laravel 11.x +// Laravel 11.x +``` The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: - Str::replace('Framework', 'Laravel', caseSensitive: false); +```php +Str::replace('Framework', 'Laravel', caseSensitive: false); +``` #### `Str::replaceArray()` {.collection-method} The `Str::replaceArray` method replaces a given value in the string sequentially using an array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = 'The event will take place between ? and ?'; +$string = 'The event will take place between ? and ?'; - $replaced = Str::replaceArray('?', ['8:30', '9:00'], $string); +$replaced = Str::replaceArray('?', ['8:30', '9:00'], $string); - // The event will take place between 8:30 and 9:00 +// The event will take place between 8:30 and 9:00 +``` #### `Str::replaceFirst()` {.collection-method} The `Str::replaceFirst` method replaces the first occurrence of a given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::replaceFirst('the', 'a', 'the quick brown fox jumps over the lazy dog'); +$replaced = Str::replaceFirst('the', 'a', 'the quick brown fox jumps over the lazy dog'); - // a quick brown fox jumps over the lazy dog +// a quick brown fox jumps over the lazy dog +``` #### `Str::replaceLast()` {.collection-method} The `Str::replaceLast` method replaces the last occurrence of a given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::replaceLast('the', 'a', 'the quick brown fox jumps over the lazy dog'); +$replaced = Str::replaceLast('the', 'a', 'the quick brown fox jumps over the lazy dog'); - // the quick brown fox jumps over a lazy dog +// the quick brown fox jumps over a lazy dog +``` #### `Str::replaceMatches()` {.collection-method} The `Str::replaceMatches` method replaces all portions of a string matching a pattern with the given replacement string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::replaceMatches( - pattern: '/[^A-Za-z0-9]++/', - replace: '', - subject: '(+1) 501-555-1000' - ) +$replaced = Str::replaceMatches( + pattern: '/[^A-Za-z0-9]++/', + replace: '', + subject: '(+1) 501-555-1000' +) - // '15015551000' +// '15015551000' +``` The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given pattern, allowing you to perform the replacement logic within the closure and return the replaced value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::replaceMatches('/\d/', function (array $matches) { - return '['.$matches[0].']'; - }, '123'); +$replaced = Str::replaceMatches('/\d/', function (array $matches) { + return '['.$matches[0].']'; +}, '123'); - // '[1][2][3]' +// '[1][2][3]' +``` #### `Str::replaceStart()` {.collection-method} The `Str::replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::replaceStart('Hello', 'Laravel', 'Hello World'); +$replaced = Str::replaceStart('Hello', 'Laravel', 'Hello World'); - // Laravel World +// Laravel World - $replaced = Str::replaceStart('World', 'Laravel', 'Hello World'); +$replaced = Str::replaceStart('World', 'Laravel', 'Hello World'); - // Hello World +// Hello World +``` #### `Str::replaceEnd()` {.collection-method} The `Str::replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::replaceEnd('World', 'Laravel', 'Hello World'); +$replaced = Str::replaceEnd('World', 'Laravel', 'Hello World'); - // Hello Laravel +// Hello Laravel - $replaced = Str::replaceEnd('Hello', 'Laravel', 'Hello World'); +$replaced = Str::replaceEnd('Hello', 'Laravel', 'Hello World'); - // Hello World +// Hello World +``` #### `Str::reverse()` {.collection-method} The `Str::reverse` method reverses the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $reversed = Str::reverse('Hello World'); +$reversed = Str::reverse('Hello World'); - // dlroW olleH +// dlroW olleH +``` #### `Str::singular()` {.collection-method} The `Str::singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $singular = Str::singular('cars'); +$singular = Str::singular('cars'); - // car +// car - $singular = Str::singular('children'); +$singular = Str::singular('children'); - // child +// child +``` #### `Str::slug()` {.collection-method} The `Str::slug` method generates a URL friendly "slug" from the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slug = Str::slug('Laravel 5 Framework', '-'); +$slug = Str::slug('Laravel 5 Framework', '-'); - // laravel-5-framework +// laravel-5-framework +``` #### `Str::snake()` {.collection-method} The `Str::snake` method converts the given string to `snake_case`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::snake('fooBar'); +$converted = Str::snake('fooBar'); - // foo_bar +// foo_bar - $converted = Str::snake('fooBar', '-'); +$converted = Str::snake('fooBar', '-'); - // foo-bar +// foo-bar +``` #### `Str::squish()` {.collection-method} The `Str::squish` method removes all extraneous white space from a string, including extraneous white space between words: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::squish(' laravel framework '); +$string = Str::squish(' laravel framework '); - // laravel framework +// laravel framework +``` #### `Str::start()` {.collection-method} The `Str::start` method adds a single instance of the given value to a string if it does not already start with that value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $adjusted = Str::start('this/string', '/'); +$adjusted = Str::start('this/string', '/'); - // /this/string +// /this/string - $adjusted = Str::start('/this/string', '/'); +$adjusted = Str::start('/this/string', '/'); - // /this/string +// /this/string +``` #### `Str::startsWith()` {.collection-method} The `Str::startsWith` method determines if the given string begins with the given value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::startsWith('This is my name', 'This'); +$result = Str::startsWith('This is my name', 'This'); - // true +// true +``` If an array of possible values is passed, the `startsWith` method will return `true` if the string begins with any of the given values: - $result = Str::startsWith('This is my name', ['This', 'That', 'There']); +```php +$result = Str::startsWith('This is my name', ['This', 'That', 'There']); - // true +// true +``` #### `Str::studly()` {.collection-method} The `Str::studly` method converts the given string to `StudlyCase`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::studly('foo_bar'); +$converted = Str::studly('foo_bar'); - // FooBar +// FooBar +``` #### `Str::substr()` {.collection-method} The `Str::substr` method returns the portion of string specified by the start and length parameters: - use Illuminate\Support\Str; - - $converted = Str::substr('The Laravel Framework', 4, 7); +```php +use Illuminate\Support\Str; - // Laravel +$converted = Str::substr('The Laravel Framework', 4, 7); + +// Laravel +``` #### `Str::substrCount()` {.collection-method} The `Str::substrCount` method returns the number of occurrences of a given value in the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $count = Str::substrCount('If you like ice cream, you will like snow cones.', 'like'); +$count = Str::substrCount('If you like ice cream, you will like snow cones.', 'like'); - // 2 +// 2 +``` #### `Str::substrReplace()` {.collection-method} The `Str::substrReplace` method replaces text within a portion of a string, starting at the position specified by the third argument and replacing the number of characters specified by the fourth argument. Passing `0` to the method's fourth argument will insert the string at the specified position without replacing any of the existing characters in the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::substrReplace('1300', ':', 2); - // 13: +$result = Str::substrReplace('1300', ':', 2); +// 13: - $result = Str::substrReplace('1300', ':', 2, 0); - // 13:00 +$result = Str::substrReplace('1300', ':', 2, 0); +// 13:00 +``` #### `Str::swap()` {.collection-method} The `Str::swap` method replaces multiple values in the given string using PHP's `strtr` function: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::swap([ - 'Tacos' => 'Burritos', - 'great' => 'fantastic', - ], 'Tacos are great!'); +$string = Str::swap([ + 'Tacos' => 'Burritos', + 'great' => 'fantastic', +], 'Tacos are great!'); - // Burritos are fantastic! +// Burritos are fantastic! +``` #### `Str::take()` {.collection-method} The `Str::take` method returns a specified number of characters from the beginning of a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $taken = Str::take('Build something amazing!', 5); +$taken = Str::take('Build something amazing!', 5); - // Build +// Build +``` #### `Str::title()` {.collection-method} The `Str::title` method converts the given string to `Title Case`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::title('a nice title uses the correct case'); +$converted = Str::title('a nice title uses the correct case'); - // A Nice Title Uses The Correct Case +// A Nice Title Uses The Correct Case +``` #### `Str::toBase64()` {.collection-method} The `Str::toBase64` method converts the given string to Base64: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $base64 = Str::toBase64('Laravel'); +$base64 = Str::toBase64('Laravel'); - // TGFyYXZlbA== +// TGFyYXZlbA== +``` #### `Str::transliterate()` {.collection-method} The `Str::transliterate` method will attempt to convert a given string into its closest ASCII representation: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $email = Str::transliterate('ⓣⓔⓢⓣ@ⓛⓐⓡⓐⓥⓔⓛ.ⓒⓞⓜ'); +$email = Str::transliterate('ⓣⓔⓢⓣ@ⓛⓐⓡⓐⓥⓔⓛ.ⓒⓞⓜ'); - // 'test@laravel.com' +// 'test@laravel.com' +``` #### `Str::trim()` {.collection-method} The `Str::trim` method strips whitespace (or other characters) from the beginning and end of the given string. Unlike PHP's native `trim` function, the `Str::trim` method also removes unicode whitespace characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::trim(' foo bar '); +$string = Str::trim(' foo bar '); - // 'foo bar' +// 'foo bar' +``` #### `Str::ltrim()` {.collection-method} The `Str::ltrim` method strips whitespace (or other characters) from the beginning of the given string. Unlike PHP's native `ltrim` function, the `Str::ltrim` method also removes unicode whitespace characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::ltrim(' foo bar '); +$string = Str::ltrim(' foo bar '); - // 'foo bar ' +// 'foo bar ' +``` #### `Str::rtrim()` {.collection-method} The `Str::rtrim` method strips whitespace (or other characters) from the end of the given string. Unlike PHP's native `rtrim` function, the `Str::rtrim` method also removes unicode whitespace characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::rtrim(' foo bar '); +$string = Str::rtrim(' foo bar '); - // ' foo bar' +// ' foo bar' +``` #### `Str::ucfirst()` {.collection-method} The `Str::ucfirst` method returns the given string with the first character capitalized: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::ucfirst('foo bar'); +$string = Str::ucfirst('foo bar'); - // Foo bar +// Foo bar +``` #### `Str::ucsplit()` {.collection-method} The `Str::ucsplit` method splits the given string into an array by uppercase characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $segments = Str::ucsplit('FooBar'); +$segments = Str::ucsplit('FooBar'); - // [0 => 'Foo', 1 => 'Bar'] +// [0 => 'Foo', 1 => 'Bar'] +``` #### `Str::upper()` {.collection-method} The `Str::upper` method converts the given string to uppercase: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::upper('laravel'); +$string = Str::upper('laravel'); - // LARAVEL +// LARAVEL +``` #### `Str::ulid()` {.collection-method} The `Str::ulid` method generates a ULID, which is a compact, time-ordered unique identifier: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - return (string) Str::ulid(); +return (string) Str::ulid(); - // 01gd6r360bp37zj17nxb55yv40 +// 01gd6r360bp37zj17nxb55yv40 +``` If you would like to retrieve a `Illuminate\Support\Carbon` date instance representing the date and time that a given ULID was created, you may use the `createFromId` method provided by Laravel's Carbon integration: @@ -1420,51 +1624,63 @@ $date = Carbon::createFromId((string) Str::ulid()); During testing, it may be useful to "fake" the value that is returned by the `Str::ulid` method. To accomplish this, you may use the `createUlidsUsing` method: - use Symfony\Component\Uid\Ulid; +```php +use Symfony\Component\Uid\Ulid; - Str::createUlidsUsing(function () { - return new Ulid('01HRDBNHHCKNW2AK4Z29SN82T9'); - }); +Str::createUlidsUsing(function () { + return new Ulid('01HRDBNHHCKNW2AK4Z29SN82T9'); +}); +``` To instruct the `ulid` method to return to generating ULIDs normally, you may invoke the `createUlidsNormally` method: - Str::createUlidsNormally(); +```php +Str::createUlidsNormally(); +``` #### `Str::unwrap()` {.collection-method} The `Str::unwrap` method removes the specified strings from the beginning and end of a given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - Str::unwrap('-Laravel-', '-'); +Str::unwrap('-Laravel-', '-'); - // Laravel +// Laravel - Str::unwrap('{framework: "Laravel"}', '{', '}'); +Str::unwrap('{framework: "Laravel"}', '{', '}'); - // framework: "Laravel" +// framework: "Laravel" +``` #### `Str::uuid()` {.collection-method} The `Str::uuid` method generates a UUID (version 4): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - return (string) Str::uuid(); +return (string) Str::uuid(); +``` During testing, it may be useful to "fake" the value that is returned by the `Str::uuid` method. To accomplish this, you may use the `createUuidsUsing` method: - use Ramsey\Uuid\Uuid; +```php +use Ramsey\Uuid\Uuid; - Str::createUuidsUsing(function () { - return Uuid::fromString('eadbfeac-5258-45c2-bab7-ccb9b5ef74f9'); - }); +Str::createUuidsUsing(function () { + return Uuid::fromString('eadbfeac-5258-45c2-bab7-ccb9b5ef74f9'); +}); +``` To instruct the `uuid` method to return to generating UUIDs normally, you may invoke the `createUuidsNormally` method: - Str::createUuidsNormally(); +```php +Str::createUuidsNormally(); +``` #### `Str::wordCount()` {.collection-method} @@ -1482,65 +1698,89 @@ Str::wordCount('Hello, world!'); // 2 The `Str::wordWrap` method wraps a string to a given number of characters: - use Illuminate\Support\Str; +```php + +``````php +use Illuminate\Support\Str; - $text = "The quick brown fox jumped over the lazy dog." +$text = "The quick brown fox jumped over the lazy dog." - Str::wordWrap($text, characters: 20, break: "
    \n"); +Str::wordWrap($text, characters: 20, break: "
    \n"); - /* - The quick brown fox
    - jumped over the lazy
    - dog. - */ +/* +The quick brown fox
    +jumped over the lazy
    +dog. +*/ +``` #### `Str::words()` {.collection-method} The `Str::words` method limits the number of words in a string. An additional string may be passed to this method via its third argument to specify which string should be appended to the end of the truncated string: - use Illuminate\Support\Str; +```php + +``````php +use Illuminate\Support\Str; - return Str::words('Perfectly balanced, as all things should be.', 3, ' >>>'); +return Str::words('Perfectly balanced, as all things should be.', 3, ' >>>'); - // Perfectly balanced, as >>> +// Perfectly balanced, as >>> +``` #### `Str::wrap()` {.collection-method} The `Str::wrap` method wraps the given string with an additional string or pair of strings: - use Illuminate\Support\Str; +```php + +``````php +use Illuminate\Support\Str; - Str::wrap('Laravel', '"'); +Str::wrap('Laravel', '"'); - // "Laravel" +// "Laravel" - Str::wrap('is', before: 'This ', after: ' Laravel!'); +Str::wrap('is', before: 'This ', after: ' Laravel!'); - // This is Laravel! +// This is Laravel! +``` #### `str()` {.collection-method} The `str` function returns a new `Illuminate\Support\Stringable` instance of the given string. This function is equivalent to the `Str::of` method: - $string = str('Taylor')->append(' Otwell'); +```php - // 'Taylor Otwell' +``````php +$string = str('Taylor')->append(' Otwell'); + +// 'Taylor Otwell' +``` If no argument is provided to the `str` function, the function returns an instance of `Illuminate\Support\Str`: - $snake = str()->snake('FooBar'); +```php + +``````php +$snake = str()->snake('FooBar'); - // 'foo_bar' +// 'foo_bar' +``` #### `trans()` {.collection-method} The `trans` function translates the given translation key using your [language files](/docs/{{version}}/localization): - echo trans('messages.welcome'); +```php + +``````php +echo trans('messages.welcome'); +``` If the specified translation key does not exist, the `trans` function will return the given key. So, using the example above, the `trans` function would return `messages.welcome` if the translation key does not exist. @@ -1549,7 +1789,9 @@ If the specified translation key does not exist, the `trans` function will retur The `trans_choice` function translates the given translation key with inflection: - echo trans_choice('messages.notifications', $unreadCount); +```php +echo trans_choice('messages.notifications', $unreadCount); +``` If the specified translation key does not exist, the `trans_choice` function will return the given key. So, using the example above, the `trans_choice` function would return `messages.notifications` if the translation key does not exist. @@ -1563,678 +1805,798 @@ Fluent strings provide a more fluent, object-oriented interface for working with The `after` method returns everything after the given value in a string. The entire string will be returned if the value does not exist within the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::of('This is my name')->after('This is'); +$slice = Str::of('This is my name')->after('This is'); - // ' my name' +// ' my name' +``` #### `afterLast` {.collection-method} The `afterLast` method returns everything after the last occurrence of the given value in a string. The entire string will be returned if the value does not exist within the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::of('App\Http\Controllers\Controller')->afterLast('\\'); +$slice = Str::of('App\Http\Controllers\Controller')->afterLast('\\'); - // 'Controller' +// 'Controller' +``` #### `apa` {.collection-method} The `apa` method converts the given string to title case following the [APA guidelines](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('a nice title uses the correct case')->apa(); +$converted = Str::of('a nice title uses the correct case')->apa(); - // A Nice Title Uses the Correct Case +// A Nice Title Uses the Correct Case +``` #### `append` {.collection-method} The `append` method appends the given values to the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Taylor')->append(' Otwell'); +$string = Str::of('Taylor')->append(' Otwell'); - // 'Taylor Otwell' +// 'Taylor Otwell' +``` #### `ascii` {.collection-method} The `ascii` method will attempt to transliterate the string into an ASCII value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('ü')->ascii(); +$string = Str::of('ü')->ascii(); - // 'u' +// 'u' +``` #### `basename` {.collection-method} The `basename` method will return the trailing name component of the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('/foo/bar/baz')->basename(); +$string = Str::of('/foo/bar/baz')->basename(); - // 'baz' +// 'baz' +``` If needed, you may provide an "extension" that will be removed from the trailing component: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('/foo/bar/baz.jpg')->basename('.jpg'); +$string = Str::of('/foo/bar/baz.jpg')->basename('.jpg'); - // 'baz' +// 'baz' +``` #### `before` {.collection-method} The `before` method returns everything before the given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::of('This is my name')->before('my name'); +$slice = Str::of('This is my name')->before('my name'); - // 'This is ' +// 'This is ' +``` #### `beforeLast` {.collection-method} The `beforeLast` method returns everything before the last occurrence of the given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slice = Str::of('This is my name')->beforeLast('is'); +$slice = Str::of('This is my name')->beforeLast('is'); - // 'This ' +// 'This ' +``` #### `between` {.collection-method} The `between` method returns the portion of a string between two values: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('This is my name')->between('This', 'name'); +$converted = Str::of('This is my name')->between('This', 'name'); - // ' is my ' +// ' is my ' +``` #### `betweenFirst` {.collection-method} The `betweenFirst` method returns the smallest possible portion of a string between two values: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('[a] bc [d]')->betweenFirst('[', ']'); +$converted = Str::of('[a] bc [d]')->betweenFirst('[', ']'); - // 'a' +// 'a' +``` #### `camel` {.collection-method} The `camel` method converts the given string to `camelCase`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('foo_bar')->camel(); +$converted = Str::of('foo_bar')->camel(); - // 'fooBar' +// 'fooBar' +``` #### `charAt` {.collection-method} The `charAt` method returns the character at the specified index. If the index is out of bounds, `false` is returned: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $character = Str::of('This is my name.')->charAt(6); +$character = Str::of('This is my name.')->charAt(6); - // 's' +// 's' +``` #### `classBasename` {.collection-method} The `classBasename` method returns the class name of the given class with the class's namespace removed: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $class = Str::of('Foo\Bar\Baz')->classBasename(); +$class = Str::of('Foo\Bar\Baz')->classBasename(); - // 'Baz' +// 'Baz' +``` #### `chopStart` {.collection-method} The `chopStart` method removes the first occurrence of the given value only if the value appears at the start of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::of('https://laravel.com/')->chopStart('https://'); +$url = Str::of('https://laravel.com/')->chopStart('https://'); - // 'laravel.com' +// 'laravel.com' +``` You may also pass an array. If the string starts with any of the values in the array then that value will be removed from string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::of('http://laravel.com/')->chopStart(['https://', 'http://']); +$url = Str::of('http://laravel.com/')->chopStart(['https://', 'http://']); - // 'laravel.com' +// 'laravel.com' +``` #### `chopEnd` {.collection-method} The `chopEnd` method removes the last occurrence of the given value only if the value appears at the end of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::of('https://laravel.com/')->chopEnd('.com'); +$url = Str::of('https://laravel.com/')->chopEnd('.com'); - // 'https://laravel/' +// 'https://laravel/' +``` You may also pass an array. If the string ends with any of the values in the array then that value will be removed from string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $url = Str::of('http://laravel.com/')->chopEnd(['.com', '.io']); +$url = Str::of('http://laravel.com/')->chopEnd(['.com', '.io']); - // 'http://laravel/' +// 'http://laravel/' +``` #### `contains` {.collection-method} The `contains` method determines if the given string contains the given value. By default this method is case sensitive: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $contains = Str::of('This is my name')->contains('my'); +$contains = Str::of('This is my name')->contains('my'); - // true +// true +``` You may also pass an array of values to determine if the given string contains any of the values in the array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $contains = Str::of('This is my name')->contains(['my', 'foo']); +$contains = Str::of('This is my name')->contains(['my', 'foo']); - // true +// true +``` You can disable case sensitivity by setting the `ignoreCase` argument to `true`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $contains = Str::of('This is my name')->contains('MY', ignoreCase: true); +$contains = Str::of('This is my name')->contains('MY', ignoreCase: true); - // true +// true +``` #### `containsAll` {.collection-method} The `containsAll` method determines if the given string contains all of the values in the given array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $containsAll = Str::of('This is my name')->containsAll(['my', 'name']); +$containsAll = Str::of('This is my name')->containsAll(['my', 'name']); - // true +// true +``` You can disable case sensitivity by setting the `ignoreCase` argument to `true`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $containsAll = Str::of('This is my name')->containsAll(['MY', 'NAME'], ignoreCase: true); +$containsAll = Str::of('This is my name')->containsAll(['MY', 'NAME'], ignoreCase: true); - // true +// true +``` #### `deduplicate` {.collection-method} The `deduplicate` method replaces consecutive instances of a character with a single instance of that character in the given string. By default, the method deduplicates spaces: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('The Laravel Framework')->deduplicate(); +$result = Str::of('The Laravel Framework')->deduplicate(); - // The Laravel Framework +// The Laravel Framework +``` You may specify a different character to deduplicate by passing it in as the second argument to the method: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('The---Laravel---Framework')->deduplicate('-'); +$result = Str::of('The---Laravel---Framework')->deduplicate('-'); - // The-Laravel-Framework +// The-Laravel-Framework +``` #### `dirname` {.collection-method} The `dirname` method returns the parent directory portion of the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('/foo/bar/baz')->dirname(); +$string = Str::of('/foo/bar/baz')->dirname(); - // '/foo/bar' +// '/foo/bar' +``` If necessary, you may specify how many directory levels you wish to trim from the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('/foo/bar/baz')->dirname(2); +$string = Str::of('/foo/bar/baz')->dirname(2); - // '/foo' +// '/foo' +``` #### `endsWith` {.collection-method} The `endsWith` method determines if the given string ends with the given value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('This is my name')->endsWith('name'); +$result = Str::of('This is my name')->endsWith('name'); - // true +// true +``` You may also pass an array of values to determine if the given string ends with any of the values in the array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('This is my name')->endsWith(['name', 'foo']); +$result = Str::of('This is my name')->endsWith(['name', 'foo']); - // true +// true - $result = Str::of('This is my name')->endsWith(['this', 'foo']); +$result = Str::of('This is my name')->endsWith(['this', 'foo']); - // false +// false +``` #### `exactly` {.collection-method} The `exactly` method determines if the given string is an exact match with another string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('Laravel')->exactly('Laravel'); +$result = Str::of('Laravel')->exactly('Laravel'); - // true +// true +``` #### `excerpt` {.collection-method} The `excerpt` method extracts an excerpt from the string that matches the first instance of a phrase within that string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $excerpt = Str::of('This is my name')->excerpt('my', [ - 'radius' => 3 - ]); +$excerpt = Str::of('This is my name')->excerpt('my', [ + 'radius' => 3 +]); - // '...is my na...' +// '...is my na...' +``` The `radius` option, which defaults to `100`, allows you to define the number of characters that should appear on each side of the truncated string. In addition, you may use the `omission` option to change the string that will be prepended and appended to the truncated string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $excerpt = Str::of('This is my name')->excerpt('name', [ - 'radius' => 3, - 'omission' => '(...) ' - ]); +$excerpt = Str::of('This is my name')->excerpt('name', [ + 'radius' => 3, + 'omission' => '(...) ' +]); - // '(...) my name' +// '(...) my name' +``` #### `explode` {.collection-method} The `explode` method splits the string by the given delimiter and returns a collection containing each section of the split string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $collection = Str::of('foo bar baz')->explode(' '); +$collection = Str::of('foo bar baz')->explode(' '); - // collect(['foo', 'bar', 'baz']) +// collect(['foo', 'bar', 'baz']) +``` #### `finish` {.collection-method} The `finish` method adds a single instance of the given value to a string if it does not already end with that value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $adjusted = Str::of('this/string')->finish('/'); +$adjusted = Str::of('this/string')->finish('/'); - // this/string/ +// this/string/ - $adjusted = Str::of('this/string/')->finish('/'); +$adjusted = Str::of('this/string/')->finish('/'); - // this/string/ +// this/string/ +``` #### `headline` {.collection-method} The `headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $headline = Str::of('taylor_otwell')->headline(); +$headline = Str::of('taylor_otwell')->headline(); - // Taylor Otwell +// Taylor Otwell - $headline = Str::of('EmailNotificationSent')->headline(); +$headline = Str::of('EmailNotificationSent')->headline(); - // Email Notification Sent +// Email Notification Sent +``` #### `inlineMarkdown` {.collection-method} The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $html = Str::of('**Laravel**')->inlineMarkdown(); +$html = Str::of('**Laravel**')->inlineMarkdown(); - // Laravel +// Laravel +``` #### Markdown Security By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - Str::of('Inject: ')->inlineMarkdown([ - 'html_input' => 'strip', - 'allow_unsafe_links' => false, - ]); +Str::of('Inject: ')->inlineMarkdown([ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, +]); - // Inject: alert("Hello XSS!"); +// Inject: alert("Hello XSS!"); +``` #### `is` {.collection-method} The `is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $matches = Str::of('foobar')->is('foo*'); +$matches = Str::of('foobar')->is('foo*'); - // true +// true - $matches = Str::of('foobar')->is('baz*'); +$matches = Str::of('foobar')->is('baz*'); - // false +// false +``` #### `isAscii` {.collection-method} The `isAscii` method determines if a given string is an ASCII string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('Taylor')->isAscii(); +$result = Str::of('Taylor')->isAscii(); - // true +// true - $result = Str::of('ü')->isAscii(); +$result = Str::of('ü')->isAscii(); - // false +// false +``` #### `isEmpty` {.collection-method} The `isEmpty` method determines if the given string is empty: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of(' ')->trim()->isEmpty(); +$result = Str::of(' ')->trim()->isEmpty(); - // true +// true - $result = Str::of('Laravel')->trim()->isEmpty(); +$result = Str::of('Laravel')->trim()->isEmpty(); - // false +// false +``` #### `isNotEmpty` {.collection-method} The `isNotEmpty` method determines if the given string is not empty: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of(' ')->trim()->isNotEmpty(); +$result = Str::of(' ')->trim()->isNotEmpty(); - // false +// false - $result = Str::of('Laravel')->trim()->isNotEmpty(); +$result = Str::of('Laravel')->trim()->isNotEmpty(); - // true +// true +``` #### `isJson` {.collection-method} The `isJson` method determines if a given string is valid JSON: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('[1,2,3]')->isJson(); +$result = Str::of('[1,2,3]')->isJson(); - // true +// true - $result = Str::of('{"first": "John", "last": "Doe"}')->isJson(); +$result = Str::of('{"first": "John", "last": "Doe"}')->isJson(); - // true +// true - $result = Str::of('{first: "John", last: "Doe"}')->isJson(); +$result = Str::of('{first: "John", last: "Doe"}')->isJson(); - // false +// false +``` #### `isUlid` {.collection-method} The `isUlid` method determines if a given string is a ULID: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('01gd6r360bp37zj17nxb55yv40')->isUlid(); +$result = Str::of('01gd6r360bp37zj17nxb55yv40')->isUlid(); - // true +// true - $result = Str::of('Taylor')->isUlid(); +$result = Str::of('Taylor')->isUlid(); - // false +// false +``` #### `isUrl` {.collection-method} The `isUrl` method determines if a given string is a URL: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('http://example.com/')->isUrl(); +$result = Str::of('http://example.com/')->isUrl(); - // true +// true - $result = Str::of('Taylor')->isUrl(); +$result = Str::of('Taylor')->isUrl(); - // false +// false +``` The `isUrl` method considers a wide range of protocols as valid. However, you may specify the protocols that should be considered valid by providing them to the `isUrl` method: - $result = Str::of('http://example.com/')->isUrl(['http', 'https']); +```php +$result = Str::of('http://example.com/')->isUrl(['http', 'https']); +``` #### `isUuid` {.collection-method} The `isUuid` method determines if a given string is a UUID: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('5ace9ab9-e9cf-4ec6-a19d-5881212a452c')->isUuid(); +$result = Str::of('5ace9ab9-e9cf-4ec6-a19d-5881212a452c')->isUuid(); - // true +// true - $result = Str::of('Taylor')->isUuid(); +$result = Str::of('Taylor')->isUuid(); - // false +// false +``` #### `kebab` {.collection-method} The `kebab` method converts the given string to `kebab-case`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('fooBar')->kebab(); +$converted = Str::of('fooBar')->kebab(); - // foo-bar +// foo-bar +``` #### `lcfirst` {.collection-method} The `lcfirst` method returns the given string with the first character lowercased: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Foo Bar')->lcfirst(); +$string = Str::of('Foo Bar')->lcfirst(); - // foo Bar +// foo Bar +``` #### `length` {.collection-method} The `length` method returns the length of the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $length = Str::of('Laravel')->length(); +$length = Str::of('Laravel')->length(); - // 7 +// 7 +``` #### `limit` {.collection-method} The `limit` method truncates the given string to the specified length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20); +$truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20); - // The quick brown fox... +// The quick brown fox... +``` You may also pass a second argument to change the string that will be appended to the end of the truncated string: - $truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20, ' (...)'); +```php +$truncated = Str::of('The quick brown fox jumps over the lazy dog')->limit(20, ' (...)'); - // The quick brown fox (...) +// The quick brown fox (...) +``` If you would like to preserve complete words when truncating the string, you may utilize the `preserveWords` argument. When this argument is `true`, the string will be truncated to the nearest complete word boundary: - $truncated = Str::of('The quick brown fox')->limit(12, preserveWords: true); +```php +$truncated = Str::of('The quick brown fox')->limit(12, preserveWords: true); - // The quick... +// The quick... +``` #### `lower` {.collection-method} The `lower` method converts the given string to lowercase: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('LARAVEL')->lower(); +$result = Str::of('LARAVEL')->lower(); - // 'laravel' +// 'laravel' +``` #### `markdown` {.collection-method} The `markdown` method converts GitHub flavored Markdown into HTML: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $html = Str::of('# Laravel')->markdown(); +$html = Str::of('# Laravel')->markdown(); - //

    Laravel

    +//

    Laravel

    - $html = Str::of('# Taylor Otwell')->markdown([ - 'html_input' => 'strip', - ]); +$html = Str::of('# Taylor Otwell')->markdown([ + 'html_input' => 'strip', +]); - //

    Taylor Otwell

    +//

    Taylor Otwell

    +``` #### Markdown Security By default, Markdown supports raw HTML, which will expose Cross-Site Scripting (XSS) vulnerabilities when used with raw user input. As per the [CommonMark Security documentation](https://commonmark.thephpleague.com/security/), you may use the `html_input` option to either escape or strip raw HTML, and the `allow_unsafe_links` option to specify whether to allow unsafe links. If you need to allow some raw HTML, you should pass your compiled Markdown through an HTML Purifier: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - Str::of('Inject: ')->markdown([ - 'html_input' => 'strip', - 'allow_unsafe_links' => false, - ]); +Str::of('Inject: ')->markdown([ + 'html_input' => 'strip', + 'allow_unsafe_links' => false, +]); - //

    Inject: alert("Hello XSS!");

    +//

    Inject: alert("Hello XSS!");

    +``` #### `mask` {.collection-method} The `mask` method masks a portion of a string with a repeated character, and may be used to obfuscate segments of strings such as email addresses and phone numbers: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('taylor@example.com')->mask('*', 3); +$string = Str::of('taylor@example.com')->mask('*', 3); - // tay*************** +// tay*************** +``` If needed, you may provide negative numbers as the third or fourth argument to the `mask` method, which will instruct the method to begin masking at the given distance from the end of the string: - $string = Str::of('taylor@example.com')->mask('*', -15, 3); +```php +$string = Str::of('taylor@example.com')->mask('*', -15, 3); - // tay***@example.com +// tay***@example.com - $string = Str::of('taylor@example.com')->mask('*', 4, -4); +$string = Str::of('taylor@example.com')->mask('*', 4, -4); - // tayl**********.com +// tayl**********.com +``` #### `match` {.collection-method} The `match` method will return the portion of a string that matches a given regular expression pattern: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('foo bar')->match('/bar/'); +$result = Str::of('foo bar')->match('/bar/'); - // 'bar' +// 'bar' - $result = Str::of('foo bar')->match('/foo (.*)/'); +$result = Str::of('foo bar')->match('/foo (.*)/'); - // 'bar' +// 'bar' +``` #### `matchAll` {.collection-method} The `matchAll` method will return a collection containing the portions of a string that match a given regular expression pattern: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('bar foo bar')->matchAll('/bar/'); +$result = Str::of('bar foo bar')->matchAll('/bar/'); - // collect(['bar', 'bar']) +// collect(['bar', 'bar']) +``` If you specify a matching group within the expression, Laravel will return a collection of the first matching group's matches: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('bar fun bar fly')->matchAll('/f(\w*)/'); +$result = Str::of('bar fun bar fly')->matchAll('/f(\w*)/'); - // collect(['un', 'ly']); +// collect(['un', 'ly']); +``` If no matches are found, an empty collection will be returned. @@ -2243,154 +2605,176 @@ If no matches are found, an empty collection will be returned. The `isMatch` method will return `true` if the string matches a given regular expression: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('foo bar')->isMatch('/foo (.*)/'); +$result = Str::of('foo bar')->isMatch('/foo (.*)/'); - // true +// true - $result = Str::of('laravel')->isMatch('/foo (.*)/'); +$result = Str::of('laravel')->isMatch('/foo (.*)/'); - // false +// false +``` #### `newLine` {.collection-method} The `newLine` method appends an "end of line" character to a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $padded = Str::of('Laravel')->newLine()->append('Framework'); +$padded = Str::of('Laravel')->newLine()->append('Framework'); - // 'Laravel - // Framework' +// 'Laravel +// Framework' +``` #### `padBoth` {.collection-method} The `padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches the desired length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $padded = Str::of('James')->padBoth(10, '_'); +$padded = Str::of('James')->padBoth(10, '_'); - // '__James___' +// '__James___' - $padded = Str::of('James')->padBoth(10); +$padded = Str::of('James')->padBoth(10); - // ' James ' +// ' James ' +``` #### `padLeft` {.collection-method} The `padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches the desired length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $padded = Str::of('James')->padLeft(10, '-='); +$padded = Str::of('James')->padLeft(10, '-='); - // '-=-=-James' +// '-=-=-James' - $padded = Str::of('James')->padLeft(10); +$padded = Str::of('James')->padLeft(10); - // ' James' +// ' James' +``` #### `padRight` {.collection-method} The `padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches the desired length: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $padded = Str::of('James')->padRight(10, '-'); +$padded = Str::of('James')->padRight(10, '-'); - // 'James-----' +// 'James-----' - $padded = Str::of('James')->padRight(10); +$padded = Str::of('James')->padRight(10); - // 'James ' +// 'James ' +``` #### `pipe` {.collection-method} The `pipe` method allows you to transform the string by passing its current value to the given callable: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $hash = Str::of('Laravel')->pipe('md5')->prepend('Checksum: '); +$hash = Str::of('Laravel')->pipe('md5')->prepend('Checksum: '); - // 'Checksum: a5c95b86291ea299fcbe64458ed12702' +// 'Checksum: a5c95b86291ea299fcbe64458ed12702' - $closure = Str::of('foo')->pipe(function (Stringable $str) { - return 'bar'; - }); +$closure = Str::of('foo')->pipe(function (Stringable $str) { + return 'bar'; +}); - // 'bar' +// 'bar' +``` #### `plural` {.collection-method} The `plural` method converts a singular word string to its plural form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $plural = Str::of('car')->plural(); +$plural = Str::of('car')->plural(); - // cars +// cars - $plural = Str::of('child')->plural(); +$plural = Str::of('child')->plural(); - // children +// children +``` You may provide an integer as a second argument to the function to retrieve the singular or plural form of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $plural = Str::of('child')->plural(2); +$plural = Str::of('child')->plural(2); - // children +// children - $plural = Str::of('child')->plural(1); +$plural = Str::of('child')->plural(1); - // child +// child +``` #### `position` {.collection-method} The `position` method returns the position of the first occurrence of a substring in a string. If the substring does not exist within the string, `false` is returned: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $position = Str::of('Hello, World!')->position('Hello'); +$position = Str::of('Hello, World!')->position('Hello'); - // 0 +// 0 - $position = Str::of('Hello, World!')->position('W'); +$position = Str::of('Hello, World!')->position('W'); - // 7 +// 7 +``` #### `prepend` {.collection-method} The `prepend` method prepends the given values onto the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Framework')->prepend('Laravel '); +$string = Str::of('Framework')->prepend('Laravel '); - // Laravel Framework +// Laravel Framework +``` #### `remove` {.collection-method} The `remove` method removes the given value or array of values from the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Arkansas is quite beautiful!')->remove('quite'); +$string = Str::of('Arkansas is quite beautiful!')->remove('quite'); - // Arkansas is beautiful! +// Arkansas is beautiful! +``` You may also pass `false` as a second parameter to ignore case when removing strings. @@ -2412,459 +2796,533 @@ $repeated = Str::of('a')->repeat(5); The `replace` method replaces a given string within the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::of('Laravel 6.x')->replace('6.x', '7.x'); +$replaced = Str::of('Laravel 6.x')->replace('6.x', '7.x'); - // Laravel 7.x +// Laravel 7.x +``` The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: - $replaced = Str::of('macOS 13.x')->replace( - 'macOS', 'iOS', caseSensitive: false - ); +```php +$replaced = Str::of('macOS 13.x')->replace( + 'macOS', 'iOS', caseSensitive: false +); +``` #### `replaceArray` {.collection-method} The `replaceArray` method replaces a given value in the string sequentially using an array: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = 'The event will take place between ? and ?'; +$string = 'The event will take place between ? and ?'; - $replaced = Str::of($string)->replaceArray('?', ['8:30', '9:00']); +$replaced = Str::of($string)->replaceArray('?', ['8:30', '9:00']); - // The event will take place between 8:30 and 9:00 +// The event will take place between 8:30 and 9:00 +``` #### `replaceFirst` {.collection-method} The `replaceFirst` method replaces the first occurrence of a given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceFirst('the', 'a'); +$replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceFirst('the', 'a'); - // a quick brown fox jumps over the lazy dog +// a quick brown fox jumps over the lazy dog +``` #### `replaceLast` {.collection-method} The `replaceLast` method replaces the last occurrence of a given value in a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceLast('the', 'a'); +$replaced = Str::of('the quick brown fox jumps over the lazy dog')->replaceLast('the', 'a'); - // the quick brown fox jumps over a lazy dog +// the quick brown fox jumps over a lazy dog +``` #### `replaceMatches` {.collection-method} The `replaceMatches` method replaces all portions of a string matching a pattern with the given replacement string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::of('(+1) 501-555-1000')->replaceMatches('/[^A-Za-z0-9]++/', '') +$replaced = Str::of('(+1) 501-555-1000')->replaceMatches('/[^A-Za-z0-9]++/', '') - // '15015551000' +// '15015551000' +``` The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given pattern, allowing you to perform the replacement logic within the closure and return the replaced value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::of('123')->replaceMatches('/\d/', function (array $matches) { - return '['.$matches[0].']'; - }); +$replaced = Str::of('123')->replaceMatches('/\d/', function (array $matches) { + return '['.$matches[0].']'; +}); - // '[1][2][3]' +// '[1][2][3]' +``` #### `replaceStart` {.collection-method} The `replaceStart` method replaces the first occurrence of the given value only if the value appears at the start of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::of('Hello World')->replaceStart('Hello', 'Laravel'); +$replaced = Str::of('Hello World')->replaceStart('Hello', 'Laravel'); - // Laravel World +// Laravel World - $replaced = Str::of('Hello World')->replaceStart('World', 'Laravel'); +$replaced = Str::of('Hello World')->replaceStart('World', 'Laravel'); - // Hello World +// Hello World +``` #### `replaceEnd` {.collection-method} The `replaceEnd` method replaces the last occurrence of the given value only if the value appears at the end of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $replaced = Str::of('Hello World')->replaceEnd('World', 'Laravel'); +$replaced = Str::of('Hello World')->replaceEnd('World', 'Laravel'); - // Hello Laravel +// Hello Laravel - $replaced = Str::of('Hello World')->replaceEnd('Hello', 'Laravel'); +$replaced = Str::of('Hello World')->replaceEnd('Hello', 'Laravel'); - // Hello World +// Hello World +``` #### `scan` {.collection-method} The `scan` method parses input from a string into a collection according to a format supported by the [`sscanf` PHP function](https://www.php.net/manual/en/function.sscanf.php): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $collection = Str::of('filename.jpg')->scan('%[^.].%s'); +$collection = Str::of('filename.jpg')->scan('%[^.].%s'); - // collect(['filename', 'jpg']) +// collect(['filename', 'jpg']) +``` #### `singular` {.collection-method} The `singular` method converts a string to its singular form. This function supports [any of the languages support by Laravel's pluralizer](/docs/{{version}}/localization#pluralization-language): - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $singular = Str::of('cars')->singular(); +$singular = Str::of('cars')->singular(); - // car +// car - $singular = Str::of('children')->singular(); +$singular = Str::of('children')->singular(); - // child +// child +``` #### `slug` {.collection-method} The `slug` method generates a URL friendly "slug" from the given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $slug = Str::of('Laravel Framework')->slug('-'); +$slug = Str::of('Laravel Framework')->slug('-'); - // laravel-framework +// laravel-framework +``` #### `snake` {.collection-method} The `snake` method converts the given string to `snake_case`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('fooBar')->snake(); +$converted = Str::of('fooBar')->snake(); - // foo_bar +// foo_bar +``` #### `split` {.collection-method} The `split` method splits a string into a collection using a regular expression: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $segments = Str::of('one, two, three')->split('/[\s,]+/'); +$segments = Str::of('one, two, three')->split('/[\s,]+/'); - // collect(["one", "two", "three"]) +// collect(["one", "two", "three"]) +``` #### `squish` {.collection-method} The `squish` method removes all extraneous white space from a string, including extraneous white space between words: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of(' laravel framework ')->squish(); +$string = Str::of(' laravel framework ')->squish(); - // laravel framework +// laravel framework +``` #### `start` {.collection-method} The `start` method adds a single instance of the given value to a string if it does not already start with that value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $adjusted = Str::of('this/string')->start('/'); +$adjusted = Str::of('this/string')->start('/'); - // /this/string +// /this/string - $adjusted = Str::of('/this/string')->start('/'); +$adjusted = Str::of('/this/string')->start('/'); - // /this/string +// /this/string +``` #### `startsWith` {.collection-method} The `startsWith` method determines if the given string begins with the given value: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('This is my name')->startsWith('This'); +$result = Str::of('This is my name')->startsWith('This'); - // true +// true +``` #### `stripTags` {.collection-method} The `stripTags` method removes all HTML and PHP tags from a string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('Taylor Otwell')->stripTags(); +$result = Str::of('Taylor Otwell')->stripTags(); - // Taylor Otwell +// Taylor Otwell - $result = Str::of('Taylor Otwell')->stripTags(''); +$result = Str::of('Taylor Otwell')->stripTags(''); - // Taylor Otwell +// Taylor Otwell +``` #### `studly` {.collection-method} The `studly` method converts the given string to `StudlyCase`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('foo_bar')->studly(); +$converted = Str::of('foo_bar')->studly(); - // FooBar +// FooBar +``` #### `substr` {.collection-method} The `substr` method returns the portion of the string specified by the given start and length parameters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Laravel Framework')->substr(8); +$string = Str::of('Laravel Framework')->substr(8); - // Framework +// Framework - $string = Str::of('Laravel Framework')->substr(8, 5); +$string = Str::of('Laravel Framework')->substr(8, 5); - // Frame +// Frame +``` #### `substrReplace` {.collection-method} The `substrReplace` method replaces text within a portion of a string, starting at the position specified by the second argument and replacing the number of characters specified by the third argument. Passing `0` to the method's third argument will insert the string at the specified position without replacing any of the existing characters in the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('1300')->substrReplace(':', 2); +$string = Str::of('1300')->substrReplace(':', 2); - // 13: +// 13: - $string = Str::of('The Framework')->substrReplace(' Laravel', 3, 0); +$string = Str::of('The Framework')->substrReplace(' Laravel', 3, 0); - // The Laravel Framework +// The Laravel Framework +``` #### `swap` {.collection-method} The `swap` method replaces multiple values in the string using PHP's `strtr` function: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Tacos are great!') - ->swap([ - 'Tacos' => 'Burritos', - 'great' => 'fantastic', - ]); +$string = Str::of('Tacos are great!') + ->swap([ + 'Tacos' => 'Burritos', + 'great' => 'fantastic', + ]); - // Burritos are fantastic! +// Burritos are fantastic! +``` #### `take` {.collection-method} The `take` method returns a specified number of characters from the beginning of the string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $taken = Str::of('Build something amazing!')->take(5); +$taken = Str::of('Build something amazing!')->take(5); - // Build +// Build +``` #### `tap` {.collection-method} The `tap` method passes the string to the given closure, allowing you to examine and interact with the string while not affecting the string itself. The original string is returned by the `tap` method regardless of what is returned by the closure: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('Laravel') - ->append(' Framework') - ->tap(function (Stringable $string) { - dump('String after append: '.$string); - }) - ->upper(); +$string = Str::of('Laravel') + ->append(' Framework') + ->tap(function (Stringable $string) { + dump('String after append: '.$string); + }) + ->upper(); - // LARAVEL FRAMEWORK +// LARAVEL FRAMEWORK +``` #### `test` {.collection-method} The `test` method determines if a string matches the given regular expression pattern: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $result = Str::of('Laravel Framework')->test('/Laravel/'); +$result = Str::of('Laravel Framework')->test('/Laravel/'); - // true +// true +``` #### `title` {.collection-method} The `title` method converts the given string to `Title Case`: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $converted = Str::of('a nice title uses the correct case')->title(); +$converted = Str::of('a nice title uses the correct case')->title(); - // A Nice Title Uses The Correct Case +// A Nice Title Uses The Correct Case +``` #### `toBase64` {.collection-method} The `toBase64` method converts the given string to Base64: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $base64 = Str::of('Laravel')->toBase64(); +$base64 = Str::of('Laravel')->toBase64(); - // TGFyYXZlbA== +// TGFyYXZlbA== +``` #### `toHtmlString` {.collection-method} The `toHtmlString` method converts the given string to an instance of `Illuminate\Support\HtmlString`, which will not be escaped when rendered in Blade templates: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $htmlString = Str::of('Nuno Maduro')->toHtmlString(); +$htmlString = Str::of('Nuno Maduro')->toHtmlString(); +``` #### `transliterate` {.collection-method} The `transliterate` method will attempt to convert a given string into its closest ASCII representation: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $email = Str::of('ⓣⓔⓢⓣ@ⓛⓐⓡⓐⓥⓔⓛ.ⓒⓞⓜ')->transliterate() +$email = Str::of('ⓣⓔⓢⓣ@ⓛⓐⓡⓐⓥⓔⓛ.ⓒⓞⓜ')->transliterate() - // 'test@laravel.com' +// 'test@laravel.com' +``` #### `trim` {.collection-method} The `trim` method trims the given string. Unlike PHP's native `trim` function, Laravel's `trim` method also removes unicode whitespace characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of(' Laravel ')->trim(); +$string = Str::of(' Laravel ')->trim(); - // 'Laravel' +// 'Laravel' - $string = Str::of('/Laravel/')->trim('/'); +$string = Str::of('/Laravel/')->trim('/'); - // 'Laravel' +// 'Laravel' +``` #### `ltrim` {.collection-method} The `ltrim` method trims the left side of the string. Unlike PHP's native `ltrim` function, Laravel's `ltrim` method also removes unicode whitespace characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of(' Laravel ')->ltrim(); +$string = Str::of(' Laravel ')->ltrim(); - // 'Laravel ' +// 'Laravel ' - $string = Str::of('/Laravel/')->ltrim('/'); +$string = Str::of('/Laravel/')->ltrim('/'); - // 'Laravel/' +// 'Laravel/' +``` #### `rtrim` {.collection-method} The `rtrim` method trims the right side of the given string. Unlike PHP's native `rtrim` function, Laravel's `rtrim` method also removes unicode whitespace characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of(' Laravel ')->rtrim(); +$string = Str::of(' Laravel ')->rtrim(); - // ' Laravel' +// ' Laravel' - $string = Str::of('/Laravel/')->rtrim('/'); +$string = Str::of('/Laravel/')->rtrim('/'); - // '/Laravel' +// '/Laravel' +``` #### `ucfirst` {.collection-method} The `ucfirst` method returns the given string with the first character capitalized: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('foo bar')->ucfirst(); +$string = Str::of('foo bar')->ucfirst(); - // Foo bar +// Foo bar +``` #### `ucsplit` {.collection-method} The `ucsplit` method splits the given string into a collection by uppercase characters: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Foo Bar')->ucsplit(); +$string = Str::of('Foo Bar')->ucsplit(); - // collect(['Foo', 'Bar']) +// collect(['Foo', 'Bar']) +``` #### `unwrap` {.collection-method} The `unwrap` method removes the specified strings from the beginning and end of a given string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - Str::of('-Laravel-')->unwrap('-'); +Str::of('-Laravel-')->unwrap('-'); - // Laravel +// Laravel - Str::of('{framework: "Laravel"}')->unwrap('{', '}'); +Str::of('{framework: "Laravel"}')->unwrap('{', '}'); - // framework: "Laravel" +// framework: "Laravel" +``` #### `upper` {.collection-method} The `upper` method converts the given string to uppercase: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $adjusted = Str::of('laravel')->upper(); +$adjusted = Str::of('laravel')->upper(); - // LARAVEL +// LARAVEL +``` #### `when` {.collection-method} The `when` method invokes the given closure if a given condition is `true`. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('Taylor') - ->when(true, function (Stringable $string) { - return $string->append(' Otwell'); - }); +$string = Str::of('Taylor') + ->when(true, function (Stringable $string) { + return $string->append(' Otwell'); + }); - // 'Taylor Otwell' +// 'Taylor Otwell' +``` If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the condition parameter evaluates to `false`. @@ -2873,44 +3331,50 @@ If necessary, you may pass another closure as the third parameter to the `when` The `whenContains` method invokes the given closure if the string contains the given value. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('tony stark') - ->whenContains('tony', function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('tony stark') + ->whenContains('tony', function (Stringable $string) { + return $string->title(); + }); - // 'Tony Stark' +// 'Tony Stark' +``` If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the string does not contain the given value. You may also pass an array of values to determine if the given string contains any of the values in the array: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('tony stark') - ->whenContains(['tony', 'hulk'], function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('tony stark') + ->whenContains(['tony', 'hulk'], function (Stringable $string) { + return $string->title(); + }); - // Tony Stark +// Tony Stark +``` #### `whenContainsAll` {.collection-method} The `whenContainsAll` method invokes the given closure if the string contains all of the given sub-strings. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('tony stark') - ->whenContainsAll(['tony', 'stark'], function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('tony stark') + ->whenContainsAll(['tony', 'stark'], function (Stringable $string) { + return $string->title(); + }); - // 'Tony Stark' +// 'Tony Stark' +``` If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the condition parameter evaluates to `false`. @@ -2919,153 +3383,175 @@ If necessary, you may pass another closure as the third parameter to the `when` The `whenEmpty` method invokes the given closure if the string is empty. If the closure returns a value, that value will also be returned by the `whenEmpty` method. If the closure does not return a value, the fluent string instance will be returned: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of(' ')->whenEmpty(function (Stringable $string) { - return $string->trim()->prepend('Laravel'); - }); +$string = Str::of(' ')->whenEmpty(function (Stringable $string) { + return $string->trim()->prepend('Laravel'); +}); - // 'Laravel' +// 'Laravel' +``` #### `whenNotEmpty` {.collection-method} The `whenNotEmpty` method invokes the given closure if the string is not empty. If the closure returns a value, that value will also be returned by the `whenNotEmpty` method. If the closure does not return a value, the fluent string instance will be returned: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('Framework')->whenNotEmpty(function (Stringable $string) { - return $string->prepend('Laravel '); - }); +$string = Str::of('Framework')->whenNotEmpty(function (Stringable $string) { + return $string->prepend('Laravel '); +}); - // 'Laravel Framework' +// 'Laravel Framework' +``` #### `whenStartsWith` {.collection-method} The `whenStartsWith` method invokes the given closure if the string starts with the given sub-string. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('disney world')->whenStartsWith('disney', function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('disney world')->whenStartsWith('disney', function (Stringable $string) { + return $string->title(); +}); - // 'Disney World' +// 'Disney World' +``` #### `whenEndsWith` {.collection-method} The `whenEndsWith` method invokes the given closure if the string ends with the given sub-string. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('disney world')->whenEndsWith('world', function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('disney world')->whenEndsWith('world', function (Stringable $string) { + return $string->title(); +}); - // 'Disney World' +// 'Disney World' +``` #### `whenExactly` {.collection-method} The `whenExactly` method invokes the given closure if the string exactly matches the given string. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('laravel')->whenExactly('laravel', function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('laravel')->whenExactly('laravel', function (Stringable $string) { + return $string->title(); +}); - // 'Laravel' +// 'Laravel' +``` #### `whenNotExactly` {.collection-method} The `whenNotExactly` method invokes the given closure if the string does not exactly match the given string. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('framework')->whenNotExactly('laravel', function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('framework')->whenNotExactly('laravel', function (Stringable $string) { + return $string->title(); +}); - // 'Framework' +// 'Framework' +``` #### `whenIs` {.collection-method} The `whenIs` method invokes the given closure if the string matches a given pattern. Asterisks may be used as wildcard values. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('foo/bar')->whenIs('foo/*', function (Stringable $string) { - return $string->append('/baz'); - }); +$string = Str::of('foo/bar')->whenIs('foo/*', function (Stringable $string) { + return $string->append('/baz'); +}); - // 'foo/bar/baz' +// 'foo/bar/baz' +``` #### `whenIsAscii` {.collection-method} The `whenIsAscii` method invokes the given closure if the string is 7 bit ASCII. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('laravel')->whenIsAscii(function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('laravel')->whenIsAscii(function (Stringable $string) { + return $string->title(); +}); - // 'Laravel' +// 'Laravel' +``` #### `whenIsUlid` {.collection-method} The `whenIsUlid` method invokes the given closure if the string is a valid ULID. The closure will receive the fluent string instance: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function (Stringable $string) { - return $string->substr(0, 8); - }); +$string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function (Stringable $string) { + return $string->substr(0, 8); +}); - // '01gd6r36' +// '01gd6r36' +``` #### `whenIsUuid` {.collection-method} The `whenIsUuid` method invokes the given closure if the string is a valid UUID. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function (Stringable $string) { - return $string->substr(0, 8); - }); +$string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function (Stringable $string) { + return $string->substr(0, 8); +}); - // 'a0a2a2d2' +// 'a0a2a2d2' +``` #### `whenTest` {.collection-method} The `whenTest` method invokes the given closure if the string matches the given regular expression. The closure will receive the fluent string instance: - use Illuminate\Support\Str; - use Illuminate\Support\Stringable; +```php +use Illuminate\Support\Str; +use Illuminate\Support\Stringable; - $string = Str::of('laravel framework')->whenTest('/laravel/', function (Stringable $string) { - return $string->title(); - }); +$string = Str::of('laravel framework')->whenTest('/laravel/', function (Stringable $string) { + return $string->title(); +}); - // 'Laravel Framework' +// 'Laravel Framework' +``` #### `wordCount` {.collection-method} @@ -3083,23 +3569,27 @@ Str::of('Hello, world!')->wordCount(); // 2 The `words` method limits the number of words in a string. If necessary, you may specify an additional string that will be appended to the truncated string: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - $string = Str::of('Perfectly balanced, as all things should be.')->words(3, ' >>>'); +$string = Str::of('Perfectly balanced, as all things should be.')->words(3, ' >>>'); - // Perfectly balanced, as >>> +// Perfectly balanced, as >>> +``` #### `wrap` {.collection-method} The `wrap` method wraps the given string with an additional string or pair of strings: - use Illuminate\Support\Str; +```php +use Illuminate\Support\Str; - Str::of('Laravel')->wrap('"'); +Str::of('Laravel')->wrap('"'); - // "Laravel" +// "Laravel" - Str::is('is')->wrap(before: 'This ', after: ' Laravel!'); +Str::is('is')->wrap(before: 'This ', after: ' Laravel!'); - // This is Laravel! +// This is Laravel! +``` diff --git a/telescope.md b/telescope.md index b008e5a1d48..4365ffcba65 100644 --- a/telescope.md +++ b/telescope.md @@ -73,16 +73,18 @@ php artisan migrate After running `telescope:install`, you should remove the `TelescopeServiceProvider` service provider registration from your application's `bootstrap/providers.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. - */ - public function register(): void - { - if ($this->app->environment('local') && class_exists(\Laravel\Telescope\TelescopeServiceProvider::class)) { - $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); - $this->app->register(TelescopeServiceProvider::class); - } +```php +/** + * Register any application services. + */ +public function register(): void +{ + if ($this->app->environment('local') && class_exists(\Laravel\Telescope\TelescopeServiceProvider::class)) { + $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); + $this->app->register(TelescopeServiceProvider::class); } +} +``` 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: @@ -103,43 +105,51 @@ After publishing Telescope's assets, its primary configuration file will be loca If desired, you may disable Telescope's data collection entirely using the `enabled` configuration option: - 'enabled' => env('TELESCOPE_ENABLED', true), +```php +'enabled' => env('TELESCOPE_ENABLED', true), +``` ### Data Pruning 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: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('telescope:prune')->daily(); +Schedule::command('telescope:prune')->daily(); +``` By default, all entries older than 24 hours will be pruned. You may use the `hours` option when calling the command to determine how long to retain Telescope data. For example, the following command will delete all records created over 48 hours ago: - use Illuminate\Support\Facades\Schedule; +```php +use Illuminate\Support\Facades\Schedule; - Schedule::command('telescope:prune --hours=48')->daily(); +Schedule::command('telescope:prune --hours=48')->daily(); +``` ### Dashboard Authorization The Telescope dashboard may be accessed via 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: - use App\Models\User; - - /** - * Register the Telescope gate. - * - * This gate determines who can access Telescope in non-local environments. - */ - protected function gate(): void - { - Gate::define('viewTelescope', function (User $user) { - return in_array($user->email, [ - 'taylor@laravel.com', - ]); - }); - } +```php +use App\Models\User; + +/** + * Register the Telescope gate. + * + * This gate determines who can access Telescope in non-local environments. + */ +protected function gate(): void +{ + Gate::define('viewTelescope', function (User $user) { + return in_array($user->email, [ + 'taylor@laravel.com', + ]); + }); +} +``` > [!WARNING] > You should ensure you change your `APP_ENV` environment variable to `production` in your production environment. Otherwise, your Telescope installation will be publicly available. @@ -175,102 +185,112 @@ To keep the assets up-to-date and avoid issues in future updates, you may add th 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; +```php +use Laravel\Telescope\IncomingEntry; +use Laravel\Telescope\Telescope; - /** - * Register any application services. - */ - public function register(): void - { - $this->hideSensitiveRequestDetails(); +/** + * Register any application services. + */ +public function register(): void +{ + $this->hideSensitiveRequestDetails(); - Telescope::filter(function (IncomingEntry $entry) { - if ($this->app->environment('local')) { - return true; - } + Telescope::filter(function (IncomingEntry $entry) { + if ($this->app->environment('local')) { + return true; + } - return $entry->isReportableException() || - $entry->isFailedJob() || - $entry->isScheduledTask() || - $entry->isSlowQuery() || - $entry->hasMonitoredTag(); - }); - } + return $entry->isReportableException() || + $entry->isFailedJob() || + $entry->isScheduledTask() || + $entry->isSlowQuery() || + $entry->hasMonitoredTag(); + }); +} +``` ### Batches 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\IncomingEntry; - use Laravel\Telescope\Telescope; - - /** - * Register any application services. - */ - public function register(): void - { - $this->hideSensitiveRequestDetails(); - - Telescope::filterBatch(function (Collection $entries) { - if ($this->app->environment('local')) { - return true; - } - - return $entries->contains(function (IncomingEntry $entry) { - return $entry->isReportableException() || - $entry->isFailedJob() || - $entry->isScheduledTask() || - $entry->isSlowQuery() || - $entry->hasMonitoredTag(); - }); - }); - } +```php +use Illuminate\Support\Collection; +use Laravel\Telescope\IncomingEntry; +use Laravel\Telescope\Telescope; + +/** + * Register any application services. + */ +public function register(): void +{ + $this->hideSensitiveRequestDetails(); + + Telescope::filterBatch(function (Collection $entries) { + if ($this->app->environment('local')) { + return true; + } + + return $entries->contains(function (IncomingEntry $entry) { + return $entry->isReportableException() || + $entry->isFailedJob() || + $entry->isScheduledTask() || + $entry->isSlowQuery() || + $entry->hasMonitoredTag(); + }); + }); +} +``` ## 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 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; +```php +use Laravel\Telescope\IncomingEntry; +use Laravel\Telescope\Telescope; - /** - * Register any application services. - */ - public function register(): void - { - $this->hideSensitiveRequestDetails(); +/** + * Register any application services. + */ +public function register(): void +{ + $this->hideSensitiveRequestDetails(); - Telescope::tag(function (IncomingEntry $entry) { - return $entry->type === 'request' - ? ['status:'.$entry->content['response_status']] - : []; - }); - } + Telescope::tag(function (IncomingEntry $entry) { + 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: - 'watchers' => [ - Watchers\CacheWatcher::class => true, - Watchers\CommandWatcher::class => true, - ... - ], +```php +'watchers' => [ + Watchers\CacheWatcher::class => true, + Watchers\CommandWatcher::class => true, + ... +], +``` Some watchers also allow you to provide additional customization options: - 'watchers' => [ - Watchers\QueryWatcher::class => [ - 'enabled' => env('TELESCOPE_QUERY_WATCHER', true), - 'slow' => 100, - ], - ... +```php +'watchers' => [ + Watchers\QueryWatcher::class => [ + 'enabled' => env('TELESCOPE_QUERY_WATCHER', true), + 'slow' => 100, ], + ... +], +``` ### Batch Watcher @@ -287,13 +307,15 @@ The cache watcher records data when a cache key is hit, missed, updated and forg 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 => [ - 'enabled' => env('TELESCOPE_COMMAND_WATCHER', true), - 'ignore' => ['key:generate'], - ], - ... +```php +'watchers' => [ + Watchers\CommandWatcher::class => [ + 'enabled' => env('TELESCOPE_COMMAND_WATCHER', true), + 'ignore' => ['key:generate'], ], + ... +], +``` ### Dump Watcher @@ -315,13 +337,15 @@ The exception watcher records the data and stack trace for any reportable except 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 => [ - 'enabled' => env('TELESCOPE_GATE_WATCHER', true), - 'ignore_abilities' => ['viewNova'], - ], - ... +```php +'watchers' => [ + Watchers\GateWatcher::class => [ + 'enabled' => env('TELESCOPE_GATE_WATCHER', true), + 'ignore_abilities' => ['viewNova'], ], + ... +], +``` ### HTTP Client Watcher @@ -340,15 +364,17 @@ The log watcher records the [log data](/docs/{{version}}/logging) for any logs w By default, Telescope will only record logs at the `error` level and above. However, you can modify the `level` option in your application's `config/telescope.php` configuration file to modify this behavior: - 'watchers' => [ - Watchers\LogWatcher::class => [ - 'enabled' => env('TELESCOPE_LOG_WATCHER', true), - 'level' => 'debug', - ], - - // ... +```php +'watchers' => [ + Watchers\LogWatcher::class => [ + 'enabled' => env('TELESCOPE_LOG_WATCHER', true), + 'level' => 'debug', ], + // ... +], +``` + ### Mail Watcher @@ -359,24 +385,28 @@ The mail watcher allows you to view an in-browser preview of [emails](/docs/{{ve 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 => [ - 'enabled' => env('TELESCOPE_MODEL_WATCHER', true), - 'events' => ['eloquent.created*', 'eloquent.updated*'], - ], - ... +```php +'watchers' => [ + Watchers\ModelWatcher::class => [ + 'enabled' => env('TELESCOPE_MODEL_WATCHER', true), + 'events' => ['eloquent.created*', 'eloquent.updated*'], ], + ... +], +``` If you would like to record the number of models hydrated during a given request, enable the `hydrations` option: - 'watchers' => [ - Watchers\ModelWatcher::class => [ - 'enabled' => env('TELESCOPE_MODEL_WATCHER', true), - 'events' => ['eloquent.created*', 'eloquent.updated*'], - 'hydrations' => true, - ], - ... +```php +'watchers' => [ + Watchers\ModelWatcher::class => [ + 'enabled' => env('TELESCOPE_MODEL_WATCHER', true), + 'events' => ['eloquent.created*', 'eloquent.updated*'], + 'hydrations' => true, ], + ... +], +``` ### Notification Watcher @@ -388,13 +418,15 @@ The notification watcher records all [notifications](/docs/{{version}}/notificat 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 => [ - 'enabled' => env('TELESCOPE_QUERY_WATCHER', true), - 'slow' => 50, - ], - ... +```php +'watchers' => [ + Watchers\QueryWatcher::class => [ + 'enabled' => env('TELESCOPE_QUERY_WATCHER', true), + 'slow' => 50, ], + ... +], +``` ### Redis Watcher @@ -406,13 +438,15 @@ The Redis watcher records all [Redis](/docs/{{version}}/redis) commands executed 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 => [ - 'enabled' => env('TELESCOPE_REQUEST_WATCHER', true), - 'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64), - ], - ... +```php +'watchers' => [ + Watchers\RequestWatcher::class => [ + 'enabled' => env('TELESCOPE_REQUEST_WATCHER', true), + 'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64), ], + ... +], +``` ### Schedule Watcher @@ -429,17 +463,19 @@ The view watcher records the [view](/docs/{{version}}/views) name, path, data, a 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; +```php +use App\Models\User; +use Laravel\Telescope\Telescope; - /** - * Register any application services. - */ - public function register(): void - { - // ... +/** + * Register any application services. + */ +public function register(): void +{ + // ... - Telescope::avatar(function (string $id, string $email) { - return '/avatars/'.User::find($id)->avatar_path; - }); - } + Telescope::avatar(function (string $id, string $email) { + return '/avatars/'.User::find($id)->avatar_path; + }); +} +``` diff --git a/testing.md b/testing.md index 84c7e216418..740b1c01b86 100644 --- a/testing.md +++ b/testing.md @@ -144,44 +144,46 @@ Occasionally, you may need to prepare certain resources used by your application Using the `ParallelTesting` facade, you may specify code to be executed on the `setUp` and `tearDown` of a process or test case. The given closures receive the `$token` and `$testCase` variables that contain the process token and the current test case, respectively: - #### Accessing the Parallel Testing Token diff --git a/urls.md b/urls.md index d3bf482ca81..765c839e73d 100644 --- a/urls.md +++ b/urls.md @@ -22,98 +22,120 @@ Laravel provides several helpers to assist you in generating URLs for your appli 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); +```php +$post = App\Models\Post::find(1); - echo url("https://github.com/posts/%7B$post-%3Eid%7D"); +echo url("https://github.com/posts/%7B$post-%3Eid%7D"); - // http://example.com/posts/1 +// http://example.com/posts/1 +``` To generate a URL with query string parameters, you may use the `query` method: - echo url()->query('/posts', ['search' => 'Laravel']); +```php +echo url()->query('/posts', ['search' => 'Laravel']); - // https://example.com/posts?search=Laravel +// https://example.com/posts?search=Laravel - echo url()->query('/posts?sort=latest', ['search' => 'Laravel']); +echo url()->query('/posts?sort=latest', ['search' => 'Laravel']); - // http://example.com/posts?sort=latest&search=Laravel +// http://example.com/posts?sort=latest&search=Laravel +``` Providing query string parameters that already exist in the path will overwrite their existing value: - echo url()->query('/posts?sort=latest', ['sort' => 'oldest']); +```php +echo url()->query('/posts?sort=latest', ['sort' => 'oldest']); - // http://example.com/posts?sort=oldest +// http://example.com/posts?sort=oldest +``` Arrays of values may also be passed as query parameters. These values will be properly keyed and encoded in the generated URL: - echo $url = url()->query('/posts', ['columns' => ['title', 'body']]); +```php +echo $url = url()->query('/posts', ['columns' => ['title', 'body']]); - // http://example.com/posts?columns%5B0%5D=title&columns%5B1%5D=body +// http://example.com/posts?columns%5B0%5D=title&columns%5B1%5D=body - echo urldecode($url); +echo urldecode($url); - // http://example.com/posts?columns[0]=title&columns[1]=body +// http://example.com/posts?columns[0]=title&columns[1]=body +``` ### Accessing the Current URL If no path is provided to the `url` helper, an `Illuminate\Routing\UrlGenerator` instance is returned, allowing you to access information about the current URL: - // Get the current URL without the query string... - echo url()->current(); +```php +// Get the current URL without the query string... +echo url()->current(); - // Get the current URL including the query string... - echo url()->full(); +// Get the current URL including the query string... +echo url()->full(); - // Get the full URL for the previous request... - echo url()->previous(); +// Get the full URL for the previous request... +echo url()->previous(); - // Get the path for the previous request... - echo url()->previousPath(); +// Get the path for the previous request... +echo url()->previousPath(); +``` Each of these methods may also be accessed via the `URL` [facade](/docs/{{version}}/facades): - use Illuminate\Support\Facades\URL; +```php +use Illuminate\Support\Facades\URL; - echo URL::current(); +echo URL::current(); +``` ## URLs for Named Routes 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 (Post $post) { - // ... - })->name('post.show'); +```php +Route::get('/post/{post}', function (Post $post) { + // ... +})->name('post.show'); +``` To generate a URL to this route, you may use the `route` helper like so: - echo route('post.show', ['post' => 1]); +```php +echo route('post.show', ['post' => 1]); - // http://example.com/post/1 +// http://example.com/post/1 +``` Of course, the `route` helper may also be used to generate URLs for routes with multiple parameters: - Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) { - // ... - })->name('comment.show'); +```php +Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) { + // ... +})->name('comment.show'); - echo route('comment.show', ['post' => 1, 'comment' => 3]); +echo route('comment.show', ['post' => 1, 'comment' => 3]); - // http://example.com/post/1/comment/3 +// 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']); +```php +echo route('post.show', ['post' => 1, 'search' => 'rocket']); - // http://example.com/post/1?search=rocket +// http://example.com/post/1?search=rocket +``` #### Eloquent Models You will often be generating URLs using the route key (typically 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 route key: - echo route('post.show', ['post' => $post]); +```php +echo route('post.show', ['post' => $post]); +``` ### Signed URLs @@ -122,115 +144,139 @@ Laravel allows you to easily create "signed" URLs to named routes. These URLs ha For example, you might use signed URLs to implement a public "unsubscribe" link that is emailed to your customers. To create a signed URL to a named route, use the `signedRoute` method of the `URL` facade: - use Illuminate\Support\Facades\URL; +```php +use Illuminate\Support\Facades\URL; - return URL::signedRoute('unsubscribe', ['user' => 1]); +return URL::signedRoute('unsubscribe', ['user' => 1]); +``` You may exclude the domain from the signed URL hash by providing the `absolute` argument to the `signedRoute` method: - return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false); +```php +return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false); +``` 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; +```php +use Illuminate\Support\Facades\URL; - return URL::temporarySignedRoute( - 'unsubscribe', now()->addMinutes(30), ['user' => 1] - ); +return URL::temporarySignedRoute( + 'unsubscribe', now()->addMinutes(30), ['user' => 1] +); +``` #### Validating Signed Route Requests To verify that an incoming request has a valid signature, you should call the `hasValidSignature` method on the incoming `Illuminate\Http\Request` instance: - use Illuminate\Http\Request; +```php +use Illuminate\Http\Request; - Route::get('/unsubscribe/{user}', function (Request $request) { - if (! $request->hasValidSignature()) { - abort(401); - } +Route::get('/unsubscribe/{user}', function (Request $request) { + if (! $request->hasValidSignature()) { + abort(401); + } - // ... - })->name('unsubscribe'); + // ... +})->name('unsubscribe'); +``` Sometimes, you may need to allow your application's frontend to append data to a signed URL, such as when performing client-side pagination. Therefore, you can specify request query parameters that should be ignored when validating a signed URL using the `hasValidSignatureWhileIgnoring` method. Remember, ignoring parameters allows anyone to modify those parameters on the request: - if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) { - abort(401); - } +```php +if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) { + abort(401); +} +``` Instead of validating signed URLs using the incoming request instance, you may assign the `signed` (`Illuminate\Routing\Middleware\ValidateSignature`) [middleware](/docs/{{version}}/middleware) to the 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) { - // ... - })->name('unsubscribe')->middleware('signed'); +```php +Route::post('/unsubscribe/{user}', function (Request $request) { + // ... +})->name('unsubscribe')->middleware('signed'); +``` If your signed URLs do not include the domain in the URL hash, you should provide the `relative` argument to the middleware: - Route::post('/unsubscribe/{user}', function (Request $request) { - // ... - })->name('unsubscribe')->middleware('signed:relative'); +```php +Route::post('/unsubscribe/{user}', function (Request $request) { + // ... +})->name('unsubscribe')->middleware('signed:relative'); +``` #### Responding to Invalid Signed Routes When someone visits a signed URL that has expired, they will receive a generic error page for the `403` HTTP status code. However, you can customize this behavior by defining a custom "render" closure for the `InvalidSignatureException` exception in your application's `bootstrap/app.php` file: - use Illuminate\Routing\Exceptions\InvalidSignatureException; +```php +use Illuminate\Routing\Exceptions\InvalidSignatureException; - ->withExceptions(function (Exceptions $exceptions) { - $exceptions->render(function (InvalidSignatureException $e) { - return response()->view('errors.link-expired', status: 403); - }); - }) +->withExceptions(function (Exceptions $exceptions) { + $exceptions->render(function (InvalidSignatureException $e) { + return response()->view('errors.link-expired', status: 403); + }); +}) +``` ## URLs for Controller Actions The `action` function generates a URL for the given controller action: - use App\Http\Controllers\HomeController; +```php +use App\Http\Controllers\HomeController; - $url = action([HomeController::class, 'index']); +$url = action([HomeController::class, 'index']); +``` 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]); +```php +$url = action([UserController::class, 'profile'], ['id' => 1]); +``` ## Default Values For some applications, you may wish to specify request-wide default values for certain URL parameters. For example, imagine many of your routes define a `{locale}` parameter: - Route::get('/{locale}/posts', function () { - // ... - })->name('post.index'); +```php +Route::get('/{locale}/posts', function () { + // ... +})->name('post.index'); +``` It is cumbersome to always pass the `locale` every time you call the `route` helper. So, you may use the `URL::defaults` method to define a default value for this parameter that will always be applied during the current request. You may wish to call this method from a [route middleware](/docs/{{version}}/middleware#assigning-middleware-to-routes) so that you have access to the current request: - $request->user()->locale]); - - return $next($request); - } + URL::defaults(['locale' => $request->user()->locale]); + + return $next($request); } +} +``` Once the default value for the `locale` parameter has been set, you are no longer required to pass its value when generating URLs via the `route` helper. diff --git a/valet.md b/valet.md index 0a324237735..75e9704b05b 100644 --- a/valet.md +++ b/valet.md @@ -332,19 +332,21 @@ Once you have updated your Nginx configuration, run the `valet restart` command 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: - [ - 'key' => 'value', - ], - - // Set $_SERVER['key'] to "value" for all sites... - '*' => [ - 'key' => 'value', - ], - ]; +```php + [ + 'key' => 'value', + ], + + // Set $_SERVER['key'] to "value" for all sites... + '*' => [ + 'key' => 'value', + ], +]; +``` ## Proxying Services @@ -391,33 +393,37 @@ The `serves` method should return `true` if your driver should handle the incomi 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. - */ - public function serves(string $sitePath, string $siteName, string $uri): bool - { - return is_dir($sitePath.'/wp-admin'); - } +```php +/** + * Determine if the driver serves the request. + */ +public function serves(string $sitePath, string $siteName, string $uri): bool +{ + return is_dir($sitePath.'/wp-admin'); +} +``` #### The `isStaticFile` Method The `isStaticFile` should determine if the incoming request is for a file that is "static", such as an image or a stylesheet. If the file is static, the method should return the fully qualified path to the static file on disk. If the incoming request is not for a static file, the method should return `false`: - /** - * Determine if the incoming request is for a static file. - * - * @return string|false - */ - public function isStaticFile(string $sitePath, string $siteName, string $uri) - { - if (file_exists($staticFilePath = $sitePath.'/public/'.$uri)) { - return $staticFilePath; - } - - return false; +```php +/** + * Determine if the incoming request is for a static file. + * + * @return string|false + */ +public function isStaticFile(string $sitePath, string $siteName, string $uri) +{ + if (file_exists($staticFilePath = $sitePath.'/public/'.$uri)) { + return $staticFilePath; } + return false; +} +``` + > [!WARNING] > The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. @@ -426,40 +432,44 @@ The `isStaticFile` should determine if the incoming request is for a file that i 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. - */ - public function frontControllerPath(string $sitePath, string $siteName, string $uri): string - { - return $sitePath.'/public/index.php'; - } +```php +/** + * Get the fully resolved path to the application's front controller. + */ +public function frontControllerPath(string $sitePath, string $siteName, string $uri): string +{ + return $sitePath.'/public/index.php'; +} +``` ### Local Drivers 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`: - use Valet\Drivers\LaravelValetDriver; +```php +use Valet\Drivers\LaravelValetDriver; - class LocalValetDriver extends LaravelValetDriver +class LocalValetDriver extends LaravelValetDriver +{ + /** + * Determine if the driver serves the request. + */ + public function serves(string $sitePath, string $siteName, string $uri): bool { - /** - * Determine if the driver serves the request. - */ - public function serves(string $sitePath, string $siteName, string $uri): bool - { - return true; - } - - /** - * Get the fully resolved path to the application's front controller. - */ - public function frontControllerPath(string $sitePath, string $siteName, string $uri): string - { - return $sitePath.'/public_html/index.php'; - } + return true; } + /** + * Get the fully resolved path to the application's front controller. + */ + public function frontControllerPath(string $sitePath, string $siteName, string $uri): string + { + return $sitePath.'/public_html/index.php'; + } +} +``` + ## Other Valet Commands diff --git a/validation.md b/validation.md index 6faf67c72ca..891d50c881e 100644 --- a/validation.md +++ b/validation.md @@ -53,10 +53,12 @@ To learn about Laravel's powerful validation features, let's look at a complete First, let's assume we have the following routes defined in our `routes/web.php` file: - use App\Http\Controllers\PostController; +```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. @@ -65,36 +67,38 @@ The `GET` route will display a form for the user to create a new blog post, whil 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: - $post->id]); - } + return to_route('post.show', ['post' => $post->id]); } +} +``` ### Writing the Validation Logic @@ -105,46 +109,54 @@ If validation fails during a traditional HTTP request, a redirect response to th To get a better understanding of the `validate` method, let's jump back into the `store` method: - /** - * Store a new blog post. - */ - public function store(Request $request): RedirectResponse - { - $validated = $request->validate([ - 'title' => 'required|unique:posts|max:255', - 'body' => 'required', - ]); +```php +/** + * Store a new blog post. + */ +public function store(Request $request): RedirectResponse +{ + $validated = $request->validate([ + 'title' => 'required|unique:posts|max:255', + 'body' => 'required', + ]); - // The blog post is valid... + // The blog post is valid... - return redirect('/posts'); - } + return redirect('/posts'); +} +``` 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: - $validatedData = $request->validate([ - 'title' => ['required', 'unique:posts', 'max:255'], - 'body' => ['required'], - ]); +```php +$validatedData = $request->validate([ + 'title' => ['required', 'unique:posts', 'max:255'], + 'body' => ['required'], +]); +``` 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'], - 'body' => ['required'], - ]); +```php +$validatedData = $request->validateWithBag('post', [ + 'title' => ['required', 'unique:posts', 'max:255'], + 'body' => ['required'], +]); +``` #### Stopping on First Validation Failure Sometimes you may wish to stop running validation rules on an attribute after the first validation failure. To do so, assign the `bail` rule to the attribute: - $request->validate([ - 'title' => 'bail|required|unique:posts|max:255', - 'body' => 'required', - ]); +```php +$request->validate([ + 'title' => 'bail|required|unique:posts|max:255', + 'body' => 'required', +]); +``` In this example, if the `unique` rule on the `title` attribute fails, the `max` rule will not be checked. Rules will be validated in the order they are assigned. @@ -153,18 +165,22 @@ In this example, if the `unique` rule on the `title` attribute fails, the `max` 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', - 'author.name' => 'required', - 'author.description' => 'required', - ]); +```php +$request->validate([ + 'title' => 'required|unique:posts|max:255', + 'author.name' => 'required', + 'author.description' => 'required', +]); +``` On the other hand, if your field name contains a literal period, you can explicitly prevent this from being interpreted as "dot" syntax by escaping the period with a backslash: - $request->validate([ - 'title' => 'required|unique:posts|max:255', - 'v1\.0' => 'required', - ]); +```php +$request->validate([ + 'title' => 'required|unique:posts|max:255', + 'v1\.0' => 'required', +]); +``` ### Displaying the Validation Errors @@ -245,7 +261,9 @@ When Laravel generates a redirect response due to a validation error, the framew 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'); +```php +$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: @@ -258,11 +276,13 @@ Laravel also provides a global `old` helper. If you are displaying old input wit By default, Laravel includes the `TrimStrings` and `ConvertEmptyStringsToNull` middleware in your application's global middleware stack. Because of this, you will often need to mark your "optional" request fields as `nullable` if you do not want the validator to consider `null` values as invalid. For example: - $request->validate([ - 'title' => 'required|unique:posts|max:255', - 'body' => 'required', - 'publish_at' => 'nullable|date', - ]); +```php +$request->validate([ + 'title' => 'required|unique:posts|max:255', + 'body' => 'required', + 'publish_at' => 'nullable|date', +]); +``` 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. @@ -310,42 +330,46 @@ The generated form request class will be placed in the `app/Http/Requests` direc 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', - ]; - } +```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). 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 a new blog post. - */ - public function store(StorePostRequest $request): RedirectResponse - { - // The incoming request is valid... +```php +/** + * Store a new blog post. + */ +public function store(StorePostRequest $request): RedirectResponse +{ + // The incoming request is valid... - // Retrieve the validated input data... - $validated = $request->validated(); + // Retrieve the validated input data... + $validated = $request->validated(); - // Retrieve a portion of the validated input data... - $validated = $request->safe()->only(['name', 'email']); - $validated = $request->safe()->except(['name', 'email']); + // Retrieve a portion of the validated input data... + $validated = $request->safe()->only(['name', 'email']); + $validated = $request->safe()->except(['name', 'email']); - // Store the blog post... + // Store the blog post... - return redirect('/posts'); - } + return redirect('/posts'); +} +``` 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](#validation-error-response-format). @@ -359,24 +383,26 @@ Sometimes you need to perform additional validation after your initial validatio The `after` method should return an array of callables or closures which will be invoked after validation is complete. The given callables will receive an `Illuminate\Validation\Validator` instance, allowing you to raise additional error messages if necessary: - use Illuminate\Validation\Validator; +```php +use Illuminate\Validation\Validator; - /** - * Get the "after" validation callables for the request. - */ - public function after(): array - { - return [ - function (Validator $validator) { - if ($this->somethingElseIsInvalid()) { - $validator->errors()->add( - 'field', - 'Something is wrong with this field!' - ); - } +/** + * Get the "after" validation callables for the request. + */ +public function after(): array +{ + return [ + function (Validator $validator) { + if ($this->somethingElseIsInvalid()) { + $validator->errors()->add( + 'field', + 'Something is wrong with this field!' + ); } - ]; - } + } + ]; +} +``` As noted, the array returned by the `after` method may also contain invokable classes. The `__invoke` method of these classes will receive an `Illuminate\Validation\Validator` instance: @@ -405,70 +431,84 @@ public function after(): array By adding a `stopOnFirstFailure` property to your request class, you may inform the validator that it should stop validating all attributes once a single validation failure has occurred: - /** - * Indicates if the validator should stop on the first rule failure. - * - * @var bool - */ - protected $stopOnFirstFailure = true; +```php +/** + * Indicates if the validator should stop on the first rule failure. + * + * @var bool + */ +protected $stopOnFirstFailure = true; +``` #### Customizing the Redirect Location When form request validation fails, a redirect response will be generated to send the user back to their previous location. However, you are free to customize this behavior. To do so, define a `$redirect` property on your form request: - /** - * The URI that users should be redirected to if validation fails. - * - * @var string - */ - protected $redirect = '/dashboard'; +```php +/** + * The URI that users should be redirected to if validation fails. + * + * @var string + */ +protected $redirect = '/dashboard'; +``` Or, if you would like to redirect users to a named route, you may define a `$redirectRoute` property instead: - /** - * The route that users should be redirected to if validation fails. - * - * @var string - */ - protected $redirectRoute = 'dashboard'; +```php +/** + * The route that users should be redirected to if validation fails. + * + * @var string + */ +protected $redirectRoute = 'dashboard'; +``` ### Authorizing Form Requests 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; +```php +use App\Models\Comment; - /** - * Determine if the user is authorized to make this request. - */ - public function authorize(): bool - { - $comment = Comment::find($this->route('comment')); +/** + * Determine if the user is authorized to make this request. + */ +public function authorize(): bool +{ + $comment = Comment::find($this->route('comment')); - return $comment && $this->user()->can('update', $comment); - } + return $comment && $this->user()->can('update', $comment); +} +``` 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}'); +```php +Route::post('/comment/{comment}'); +``` Therefore, if your application is taking advantage of [route model binding](/docs/{{version}}/routing#route-model-binding), your code may be made even more succinct by accessing the resolved model as a property of the request: - return $this->user()->can('update', $this->comment); +```php +return $this->user()->can('update', $this->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 handle authorization logic for the request in another part of your application, you may remove the `authorize` method completely, or simply return `true`: - /** - * Determine if the user is authorized to make this request. - */ - public function authorize(): bool - { - return true; - } +```php +/** + * Determine if the user is authorized to make this request. + */ +public function authorize(): bool +{ + return true; +} +``` > [!NOTE] > You may type-hint any dependencies you need within the `authorize` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). @@ -478,106 +518,116 @@ If you plan to handle authorization logic for the request in another part of you You may customize the error messages used by the form request by overriding the `messages` method. This method should return an array of attribute / rule pairs and their corresponding error messages: - /** - * Get the error messages for the defined validation rules. - * - * @return array - */ - public function messages(): array - { - return [ - 'title.required' => 'A title is required', - 'body.required' => 'A message is required', - ]; - } +```php +/** + * Get the error messages for the defined validation rules. + * + * @return array + */ +public function messages(): array +{ + return [ + 'title.required' => 'A title is required', + 'body.required' => 'A message is required', + ]; +} +``` #### Customizing the Validation Attributes 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. - * - * @return array - */ - public function attributes(): array - { - return [ - 'email' => 'email address', - ]; - } +```php +/** + * Get custom attributes for validator errors. + * + * @return array + */ +public function attributes(): array +{ + return [ + 'email' => 'email address', + ]; +} +``` ### Preparing Input for Validation 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; +```php +use Illuminate\Support\Str; - /** - * Prepare the data for validation. - */ - protected function prepareForValidation(): void - { - $this->merge([ - 'slug' => Str::slug($this->slug), - ]); - } +/** + * Prepare the data for validation. + */ +protected function prepareForValidation(): void +{ + $this->merge([ + 'slug' => Str::slug($this->slug), + ]); +} +``` Likewise, if you need to normalize any request data after validation is complete, you may use the `passedValidation` method: - /** - * Handle a passed validation attempt. - */ - protected function passedValidation(): void - { - $this->replace(['name' => 'Taylor']); - } +```php +/** + * Handle a passed validation attempt. + */ +protected function passedValidation(): void +{ + $this->replace(['name' => 'Taylor']); +} +``` ## Manually Creating Validators If you do not want to use the `validate` method on the request, you may create a validator instance manually using the `Validator` [facade](/docs/{{version}}/facades). The `make` method on the facade generates a new validator instance: - all(), [ - 'title' => 'required|unique:posts|max:255', - 'body' => 'required', - ]); - - if ($validator->fails()) { - return redirect('/post/create') - ->withErrors($validator) - ->withInput(); - } + $validator = Validator::make($request->all(), [ + 'title' => 'required|unique:posts|max:255', + 'body' => 'required', + ]); - // Retrieve the validated input... - $validated = $validator->validated(); + if ($validator->fails()) { + return redirect('/post/create') + ->withErrors($validator) + ->withInput(); + } - // Retrieve a portion of the validated input... - $validated = $validator->safe()->only(['name', 'email']); - $validated = $validator->safe()->except(['name', 'email']); + // Retrieve the validated input... + $validated = $validator->validated(); - // Store the blog post... + // Retrieve a portion of the validated input... + $validated = $validator->safe()->only(['name', 'email']); + $validated = $validator->safe()->except(['name', 'email']); - return redirect('/posts'); - } + // Store the blog post... + + return redirect('/posts'); } +} +``` 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. @@ -587,33 +637,41 @@ After determining whether the request validation failed, you may use the `withEr The `stopOnFirstFailure` method will inform the validator that it should stop validating all attributes once a single validation failure has occurred: - if ($validator->stopOnFirstFailure()->fails()) { - // ... - } +```php +if ($validator->stopOnFirstFailure()->fails()) { + // ... +} +``` ### Automatic Redirection 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](#validation-error-response-format): - Validator::make($request->all(), [ - 'title' => 'required|unique:posts|max:255', - 'body' => 'required', - ])->validate(); +```php +Validator::make($request->all(), [ + 'title' => 'required|unique:posts|max:255', + 'body' => 'required', +])->validate(); +``` You may use the `validateWithBag` method to store the error messages in a [named error bag](#named-error-bags) if validation fails: - Validator::make($request->all(), [ - 'title' => 'required|unique:posts|max:255', - 'body' => 'required', - ])->validateWithBag('post'); +```php +Validator::make($request->all(), [ + 'title' => 'required|unique:posts|max:255', + 'body' => 'required', +])->validateWithBag('post'); +``` ### Named Error Bags 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'); +```php +return redirect('/register')->withErrors($validator, 'login'); +``` You may then access the named `MessageBag` instance from the `$errors` variable: @@ -626,57 +684,67 @@ You may then access the named `MessageBag` instance from the `$errors` variable: If needed, you may provide custom error messages that a validator instance should use instead of the default error messages provided by Laravel. 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.', - ]); +```php +$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', - ]; +```php +$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!', - ]; +```php +$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', - ]); +```php +$validator = Validator::make($input, $rules, $messages, [ + 'email' => 'email address', +]); +``` ### Performing Additional Validation Sometimes you need to perform additional validation after your initial validation is complete. You can accomplish this using the validator's `after` method. The `after` method accepts a closure or an array of callables which will be invoked after validation is complete. The given callables will receive an `Illuminate\Validation\Validator` instance, allowing you to raise additional error messages if necessary: - use Illuminate\Support\Facades\Validator; +```php +use Illuminate\Support\Facades\Validator; - $validator = Validator::make(/* ... */); +$validator = Validator::make(/* ... */); - $validator->after(function ($validator) { - if ($this->somethingElseIsInvalid()) { - $validator->errors()->add( - 'field', 'Something is wrong with this field!' - ); - } - }); - - if ($validator->fails()) { - // ... +$validator->after(function ($validator) { + if ($this->somethingElseIsInvalid()) { + $validator->errors()->add( + 'field', 'Something is wrong with this field!' + ); } +}); + +if ($validator->fails()) { + // ... +} +``` As noted, the `after` method also accepts an array of callables, which is particularly convenient if your "after validation" logic is encapsulated in invokable classes, which will receive an `Illuminate\Validation\Validator` instance via their `__invoke` method: @@ -698,37 +766,47 @@ $validator->after([ After validating incoming request data using a form request or a manually created validator instance, you may wish to retrieve the incoming request data that actually underwent validation. This can be accomplished in several ways. First, you may call the `validated` method on a form request or validator instance. This method returns an array of the data that was validated: - $validated = $request->validated(); +```php +$validated = $request->validated(); - $validated = $validator->validated(); +$validated = $validator->validated(); +``` Alternatively, you may call the `safe` method on a form request or validator instance. This method returns an instance of `Illuminate\Support\ValidatedInput`. This object exposes `only`, `except`, and `all` methods to retrieve a subset of the validated data or the entire array of validated data: - $validated = $request->safe()->only(['name', 'email']); +```php +$validated = $request->safe()->only(['name', 'email']); - $validated = $request->safe()->except(['name', 'email']); +$validated = $request->safe()->except(['name', 'email']); - $validated = $request->safe()->all(); +$validated = $request->safe()->all(); +``` In addition, the `Illuminate\Support\ValidatedInput` instance may be iterated over and accessed like an array: - // Validated data may be iterated... - foreach ($request->safe() as $key => $value) { - // ... - } +```php +// Validated data may be iterated... +foreach ($request->safe() as $key => $value) { + // ... +} - // Validated data may be accessed as an array... - $validated = $request->safe(); +// Validated data may be accessed as an array... +$validated = $request->safe(); - $email = $validated['email']; +$email = $validated['email']; +``` If you would like to add additional fields to the validated data, you may call the `merge` method: - $validated = $request->safe()->merge(['name' => 'Taylor Otwell']); +```php +$validated = $request->safe()->merge(['name' => 'Taylor Otwell']); +``` If you would like to retrieve the validated data as a [collection](/docs/{{version}}/collections) instance, you may call the `collect` method: - $collection = $request->safe()->collect(); +```php +$collection = $request->safe()->collect(); +``` ## Working With Error Messages @@ -740,42 +818,52 @@ After calling the `errors` method on a `Validator` instance, you will receive an To retrieve the first error message for a given field, use the `first` method: - $errors = $validator->errors(); +```php +$errors = $validator->errors(); - echo $errors->first('email'); +echo $errors->first('email'); +``` #### Retrieving All Error Messages for a Field If you need to retrieve an array of all the messages for a given field, use the `get` method: - foreach ($errors->get('email') as $message) { - // ... - } +```php +foreach ($errors->get('email') as $message) { + // ... +} +``` If you are validating an array form field, you may retrieve all of the messages for each of the array elements using the `*` character: - foreach ($errors->get('attachments.*') as $message) { - // ... - } +```php +foreach ($errors->get('attachments.*') as $message) { + // ... +} +``` #### Retrieving All Error Messages for All Fields To retrieve an array of all messages for all fields, use the `all` method: - foreach ($errors->all() as $message) { - // ... - } +```php +foreach ($errors->all() as $message) { + // ... +} +``` #### Determining if Messages Exist for a Field The `has` method may be used to determine if any error messages exist for a given field: - if ($errors->has('email')) { - // ... - } +```php +if ($errors->has('email')) { + // ... +} +``` ### Specifying Custom Messages in Language Files @@ -794,21 +882,25 @@ In addition, you may copy this file to another language directory to translate t 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 `lang/xx/validation.php` language file: - 'custom' => [ - 'email' => [ - 'required' => 'We need to know your email address!', - 'max' => 'Your email address is too long!' - ], +```php +'custom' => [ + 'email' => [ + 'required' => 'We need to know your email address!', + 'max' => 'Your email address is too long!' ], +], +``` ### Specifying Attributes in Language Files 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 `lang/xx/validation.php` language file: - 'attributes' => [ - 'email' => 'email address', - ], +```php +'attributes' => [ + 'email' => 'email address', +], +``` > [!WARNING] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -818,9 +910,11 @@ Many of Laravel's built-in error messages include an `:attribute` placeholder th 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`: - Validator::make($request->all(), [ - 'credit_card_number' => 'required_if:payment_type,cc' - ]); +```php +Validator::make($request->all(), [ + 'credit_card_number' => 'required_if:payment_type,cc' +]); +``` If this validation rule fails, it will produce the following error message: @@ -830,11 +924,13 @@ 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 more user-friendly value representation in your `lang/xx/validation.php` language file by defining a `values` array: - 'values' => [ - 'payment_type' => [ - 'cc' => 'credit card' - ], +```php +'values' => [ + 'payment_type' => [ + 'cc' => 'credit card' ], +], +``` > [!WARNING] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -1056,27 +1152,35 @@ The field under validation must have a valid A or AAAA record according to the ` 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' +```php +'start_date' => 'required|date|after:tomorrow' +``` Instead of passing a date string to be evaluated by `strtotime`, you may specify another field to compare against the date: - 'finish_date' => 'required|date|after:start_date' +```php +'finish_date' => 'required|date|after:start_date' +``` For convenience, date based rules may be constructed using the fluent `date` rule builder: - use Illuminate\Validation\Rule; +```php +use Illuminate\Validation\Rule; - 'start_date' => [ - 'required', - Rule::date()->after(today()->addDays(7)), - ], +'start_date' => [ + 'required', + Rule::date()->after(today()->addDays(7)), +], +``` The `afterToday` and `todayOrAfter` methods may be used to fluently express the date must be after today or or today or after, respectively: - 'start_date' => [ - 'required', - Rule::date()->afterToday(), - ], +```php +'start_date' => [ + 'required', + Rule::date()->afterToday(), +], +``` #### after\_or\_equal:_date_ @@ -1085,12 +1189,14 @@ The field under validation must be a value after or equal to the given date. For For convenience, date based rules may be constructed using the fluent `date` rule builder: - use Illuminate\Validation\Rule; +```php +use Illuminate\Validation\Rule; - 'start_date' => [ - 'required', - Rule::date()->afterOrEqual(today()->addDays(7)), - ], +'start_date' => [ + 'required', + Rule::date()->afterOrEqual(today()->addDays(7)), +], +``` #### alpha @@ -1132,19 +1238,21 @@ The field under validation must be a PHP `array`. When additional values are provided to the `array` rule, each key in the input array must be present within the list of values provided to the rule. In the following example, the `admin` key in the input array is invalid since it is not contained in the list of values provided to the `array` rule: - use Illuminate\Support\Facades\Validator; +```php +use Illuminate\Support\Facades\Validator; - $input = [ - 'user' => [ - 'name' => 'Taylor Otwell', - 'username' => 'taylorotwell', - 'admin' => true, - ], - ]; +$input = [ + 'user' => [ + 'name' => 'Taylor Otwell', + 'username' => 'taylorotwell', + 'admin' => true, + ], +]; - Validator::make($input, [ - 'user' => 'array:name,username', - ]); +Validator::make($input, [ + 'user' => 'array:name,username', +]); +``` In general, you should always specify the array keys that are allowed to be present within your array. @@ -1160,9 +1268,11 @@ Stop running validation rules for the field after the first validation failure. While the `bail` rule will only stop validating a specific field when it encounters a validation failure, the `stopOnFirstFailure` method will inform the validator that it should stop validating all attributes once a single validation failure has occurred: - if ($validator->stopOnFirstFailure()->fails()) { - // ... - } +```php +if ($validator->stopOnFirstFailure()->fails()) { + // ... +} +``` #### before:_date_ @@ -1171,19 +1281,23 @@ The field under validation must be a value preceding the given date. The dates w For convenience, date based rules may also be constructed using the fluent `date` rule builder: - use Illuminate\Validation\Rule; +```php +use Illuminate\Validation\Rule; - 'start_date' => [ - 'required', - Rule::date()->before(today()->subDays(7)), - ], +'start_date' => [ + 'required', + Rule::date()->before(today()->subDays(7)), +], +``` The `beforeToday` and `todayOrBefore` methods may be used to fluently express the date must be before today or or today or before, respectively: - 'start_date' => [ - 'required', - Rule::date()->beforeToday(), - ], +```php +'start_date' => [ + 'required', + Rule::date()->beforeToday(), +], +``` #### before\_or\_equal:_date_ @@ -1192,12 +1306,14 @@ The field under validation must be a value preceding or equal to the given date. For convenience, date based rules may also be constructed using the fluent `date` rule builder: - use Illuminate\Validation\Rule; +```php +use Illuminate\Validation\Rule; - 'start_date' => [ - 'required', - Rule::date()->beforeOrEqual(today()->subDays(7)), - ], +'start_date' => [ + 'required', + Rule::date()->beforeOrEqual(today()->subDays(7)), +], +``` #### between:_min_,_max_ @@ -1226,7 +1342,9 @@ The field under validation must be an array that contains all of the given param 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' => 'current_password:api' +```php +'password' => 'current_password:api' +``` #### date @@ -1245,23 +1363,27 @@ The field under validation must match one of the given _formats_. You should use For convenience, date based rules may be constructed using the fluent `date` rule builder: - use Illuminate\Validation\Rule; +```php +use Illuminate\Validation\Rule; - 'start_date' => [ - 'required', - Rule::date()->format('Y-m-d'), - ], +'start_date' => [ + 'required', + Rule::date()->format('Y-m-d'), +], +``` #### decimal:_min_,_max_ The field under validation must be numeric and must contain the specified number of decimal places: - // Must have exactly two decimal places (9.99)... - 'price' => 'decimal:2' +```php +// Must have exactly two decimal places (9.99)... +'price' => 'decimal:2' - // Must have between 2 and 4 decimal places... - 'price' => 'decimal:2,4' +// Must have between 2 and 4 decimal places... +'price' => 'decimal:2,4' +``` #### declined @@ -1293,43 +1415,55 @@ The integer validation must have a length between the given _min_ and _max_. The file under validation must be an image meeting the dimension constraints as specified by the rule's parameters: - 'avatar' => 'dimensions:min_width=100,min_height=200' +```php +'avatar' => 'dimensions:min_width=100,min_height=200' +``` 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 fraction like `3/2` or a float like `1.5`: - 'avatar' => 'dimensions:ratio=3/2' +```php +'avatar' => 'dimensions:ratio=3/2' +``` Since this rule requires several arguments, it is often more convenient to use use the `Rule::dimensions` method to fluently construct the rule: - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - Validator::make($data, [ - 'avatar' => [ - 'required', - Rule::dimensions() - ->maxWidth(1000) - ->maxHeight(500) - ->ratio(3 / 2), - ], - ]); +Validator::make($data, [ + 'avatar' => [ + 'required', + Rule::dimensions() + ->maxWidth(1000) + ->maxHeight(500) + ->ratio(3 / 2), + ], +]); +``` #### distinct When validating arrays, the field under validation must not have any duplicate values: - 'foo.*.id' => 'distinct' +```php +'foo.*.id' => 'distinct' +``` Distinct uses loose variable comparisons by default. To use strict comparisons, you may add the `strict` parameter to your validation rule definition: - 'foo.*.id' => 'distinct:strict' +```php +'foo.*.id' => 'distinct:strict' +``` You may add `ignore_case` to the validation rule's arguments to make the rule ignore capitalization differences: - 'foo.*.id' => 'distinct:ignore_case' +```php +'foo.*.id' => 'distinct:ignore_case' +``` #### doesnt_start_with:_foo_,_bar_,... @@ -1346,7 +1480,9 @@ The field under validation must not end with one of the given values. 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' +```php +'email' => 'email:rfc,dns' +``` The example above will apply the `RFCValidation` and `DNSCheckValidation` validations. Here's a full list of validation styles you can apply: @@ -1390,20 +1526,24 @@ The field under validation must end with one of the given values. The `Enum` rule is a class based rule that validates whether the field under validation contains a valid enum value. The `Enum` rule accepts the name of the enum as its only constructor argument. When validating primitive values, a backed Enum should be provided to the `Enum` rule: - use App\Enums\ServerStatus; - use Illuminate\Validation\Rule; +```php +use App\Enums\ServerStatus; +use Illuminate\Validation\Rule; - $request->validate([ - 'status' => [Rule::enum(ServerStatus::class)], - ]); +$request->validate([ + 'status' => [Rule::enum(ServerStatus::class)], +]); +``` The `Enum` rule's `only` and `except` methods may be used to limit which enum cases should be considered valid: - Rule::enum(ServerStatus::class) - ->only([ServerStatus::Pending, ServerStatus::Active]); +```php +Rule::enum(ServerStatus::class) + ->only([ServerStatus::Pending, ServerStatus::Active]); - Rule::enum(ServerStatus::class) - ->except([ServerStatus::Pending, ServerStatus::Active]); +Rule::enum(ServerStatus::class) + ->except([ServerStatus::Pending, ServerStatus::Active]); +``` The `when` method may be used to conditionally modify the `Enum` rule: @@ -1431,16 +1571,18 @@ The field under validation will be excluded from the request data returned by th If complex conditional exclusion logic is required, you may utilize the `Rule::excludeIf` method. This method accepts a boolean or a closure. When given a closure, the closure should return `true` or `false` to indicate if the field under validation should be excluded: - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - Validator::make($request->all(), [ - 'role_id' => Rule::excludeIf($request->user()->is_admin), - ]); +Validator::make($request->all(), [ + 'role_id' => Rule::excludeIf($request->user()->is_admin), +]); - Validator::make($request->all(), [ - 'role_id' => Rule::excludeIf(fn () => $request->user()->is_admin), - ]); +Validator::make($request->all(), [ + 'role_id' => Rule::excludeIf(fn () => $request->user()->is_admin), +]); +``` #### exclude_unless:_anotherfield_,_value_ @@ -1465,7 +1607,9 @@ The field under validation must exist in a given database table. #### Basic Usage of Exists Rule - 'state' => 'exists:states' +```php +'state' => 'exists:states' +``` 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. @@ -1474,41 +1618,53 @@ If the `column` option is not specified, the field name will be used. So, in thi 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' +```php +'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: - 'email' => 'exists:connection.staff,email' +```php +'email' => 'exists:connection.staff,email' +``` Instead of specifying the table name directly, you may specify the Eloquent model which should be used to determine the table name: - 'user_id' => 'exists:App\Models\User,id' +```php +'user_id' => 'exists:App\Models\User,id' +``` 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\Database\Query\Builder; - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; +```php +use Illuminate\Database\Query\Builder; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - Validator::make($data, [ - 'email' => [ - 'required', - Rule::exists('staff')->where(function (Builder $query) { - $query->where('account_id', 1); - }), - ], - ]); +Validator::make($data, [ + 'email' => [ + 'required', + Rule::exists('staff')->where(function (Builder $query) { + $query->where('account_id', 1); + }), + ], +]); +``` You may explicitly specify the database column name that should be used by the `exists` rule generated by the `Rule::exists` method by providing the column name as the second argument to the `exists` method: - 'state' => Rule::exists('states', 'abbreviation'), +```php +'state' => Rule::exists('states', 'abbreviation'), +``` #### extensions:_foo_,_bar_,... The file under validation must have a user-assigned extension corresponding to one of the listed extensions: - 'photo' => ['required', 'extensions:jpg,png'], +```php +'photo' => ['required', 'extensions:jpg,png'], +``` > [!WARNING] > You should never rely on validating a file by its user-assigned extension alone. This rule should typically always be used in combination with the [`mimes`](#rule-mimes) or [`mimetypes`](#rule-mimetypes) rules. @@ -1551,32 +1707,36 @@ The file under validation must be an image (jpg, jpeg, png, bmp, gif, or webp). 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; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - Validator::make($data, [ - 'zones' => [ - 'required', - Rule::in(['first-zone', 'second-zone']), - ], - ]); +Validator::make($data, [ + 'zones' => [ + 'required', + Rule::in(['first-zone', 'second-zone']), + ], +]); +``` When the `in` rule is combined with the `array` rule, each value in the input array must be present within the list of values provided to the `in` rule. In the following example, the `LAS` airport code in the input array is invalid since it is not contained in the list of airports provided to the `in` rule: - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - $input = [ - 'airports' => ['NYC', 'LAS'], - ]; +$input = [ + 'airports' => ['NYC', 'LAS'], +]; - Validator::make($input, [ - 'airports' => [ - 'required', - 'array', - ], - 'airports.*' => Rule::in(['NYC', 'LIT']), - ]); +Validator::make($input, [ + 'airports' => [ + 'required', + 'array', + ], + 'airports.*' => Rule::in(['NYC', 'LIT']), +]); +``` #### in_array:_anotherfield_.* @@ -1651,7 +1811,9 @@ The integer under validation must have a maximum length of _value_. The file under validation must match one of the given MIME types: - 'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime' +```php +'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's provided MIME type. @@ -1660,7 +1822,9 @@ To determine the MIME type of the uploaded file, the file's contents will be rea The file under validation must have a MIME type corresponding to one of the listed extensions: - 'photo' => 'mimes:jpg,bmp,png' +```php +'photo' => 'mimes:jpg,bmp,png' +``` 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: @@ -1716,14 +1880,16 @@ The field under validation must not be present _only if_ all of the other specif The field under validation must not be included in the given list of values. The `Rule::notIn` method may be used to fluently construct the rule: - use Illuminate\Validation\Rule; +```php +use Illuminate\Validation\Rule; - Validator::make($data, [ - 'toppings' => [ - 'required', - Rule::notIn(['sprinkles', 'cherries']), - ], - ]); +Validator::make($data, [ + 'toppings' => [ + 'required', + Rule::notIn(['sprinkles', 'cherries']), + ], +]); +``` #### not_regex:_pattern_ @@ -1800,16 +1966,18 @@ The field under validation must be missing or empty if the _anotherfield_ field If complex conditional prohibition logic is required, you may utilize the `Rule::prohibitedIf` method. This method accepts a boolean or a closure. When given a closure, the closure should return `true` or `false` to indicate if the field under validation should be prohibited: - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - Validator::make($request->all(), [ - 'role_id' => Rule::prohibitedIf($request->user()->is_admin), - ]); +Validator::make($request->all(), [ + 'role_id' => Rule::prohibitedIf($request->user()->is_admin), +]); - Validator::make($request->all(), [ - 'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin), - ]); +Validator::make($request->all(), [ + 'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin), +]); +``` #### prohibited_unless:_anotherfield_,_value_,... @@ -1870,16 +2038,18 @@ The field under validation must be present and not empty if the _anotherfield_ f If you would like to construct a more complex condition for the `required_if` rule, you may use the `Rule::requiredIf` method. This method 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; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - Validator::make($request->all(), [ - 'role_id' => Rule::requiredIf($request->user()->is_admin), - ]); +Validator::make($request->all(), [ + 'role_id' => Rule::requiredIf($request->user()->is_admin), +]); - Validator::make($request->all(), [ - 'role_id' => Rule::requiredIf(fn () => $request->user()->is_admin), - ]); +Validator::make($request->all(), [ + 'role_id' => Rule::requiredIf(fn () => $request->user()->is_admin), +]); +``` #### required_if_accepted:_anotherfield_,... @@ -1931,17 +2101,19 @@ The given _field_ must match the field under validation. The field under validation must have a size matching the given _value_. For string data, _value_ corresponds to the number of characters. For numeric data, _value_ corresponds to a given integer value (the attribute must also have the `numeric` or `integer` rule). For an array, _size_ corresponds to the `count` of the array. For files, _size_ corresponds to the file size in kilobytes. Let's look at some examples: - // Validate that a string is exactly 12 characters long... - 'title' => 'size:12'; +```php +// Validate that a string is exactly 12 characters long... +'title' => 'size:12'; - // Validate that a provided integer equals 10... - 'seats' => 'integer|size:10'; +// Validate that a provided integer equals 10... +'seats' => 'integer|size:10'; - // Validate that an array has exactly 5 elements... - 'tags' => 'array|size:5'; +// Validate that an array has exactly 5 elements... +'tags' => 'array|size:5'; - // Validate that an uploaded file is exactly 512 kilobytes... - 'image' => 'file|size:512'; +// Validate that an uploaded file is exactly 512 kilobytes... +'image' => 'file|size:512'; +``` #### starts_with:_foo_,_bar_,... @@ -1960,11 +2132,13 @@ The field under validation must be a valid timezone identifier according to the The arguments [accepted by the `DateTimeZone::listIdentifiers` method](https://www.php.net/manual/en/datetimezone.listidentifiers.php) may also be provided to this validation rule: - 'timezone' => 'required|timezone:all'; +```php +'timezone' => 'required|timezone:all'; - 'timezone' => 'required|timezone:Africa'; +'timezone' => 'required|timezone:Africa'; - 'timezone' => 'required|timezone:per_country,US'; +'timezone' => 'required|timezone:per_country,US'; +``` #### unique:_table_,_column_ @@ -1975,17 +2149,23 @@ The field under validation must not exist within the given database table. Instead of specifying the table name directly, you may specify the Eloquent model which should be used to determine the table name: - 'email' => 'unique:App\Models\User,email_address' +```php +'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 name of the field under validation will be used. - 'email' => 'unique:users,email_address' +```php +'email' => 'unique:users,email_address' +``` **Specifying a Custom Database Connection** 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' +```php +'email' => 'unique:connection.users,email_address' +``` **Forcing a Unique Rule to Ignore a Given ID:** @@ -1993,46 +2173,60 @@ Sometimes, you may wish to ignore a given ID during unique validation. For examp 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; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - Validator::make($data, [ - 'email' => [ - 'required', - Rule::unique('users')->ignore($user->id), - ], - ]); +Validator::make($data, [ + 'email' => [ + 'required', + Rule::unique('users')->ignore($user->id), + ], +]); +``` > [!WARNING] > 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 also pass the entire model instance. Laravel will automatically extract the key from the model: - Rule::unique('users')->ignore($user) +```php +Rule::unique('users')->ignore($user) +``` If your table uses a primary key column name other than `id`, you may specify the name of the column when calling the `ignore` method: - Rule::unique('users')->ignore($user->id, 'user_id') +```php +Rule::unique('users')->ignore($user->id, 'user_id') +``` By default, the `unique` rule will check the uniqueness of the column matching the name of the attribute being validated. However, you may pass a different column name as the second argument to the `unique` method: - Rule::unique('users', 'email_address')->ignore($user->id) +```php +Rule::unique('users', 'email_address')->ignore($user->id) +``` **Adding Additional Where Clauses:** 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(fn (Builder $query) => $query->where('account_id', 1)) +```php +'email' => Rule::unique('users')->where(fn (Builder $query) => $query->where('account_id', 1)) +``` **Ignoring Soft Deleteded Records in Unique Checks:** By default, the unique rule includes soft deleted records when determining uniqueness. To exclude soft deleted records from the uniqueness check, you may invoke the `withoutTrashed` method: - Rule::unique('users')->withoutTrashed(); +```php +Rule::unique('users')->withoutTrashed(); +``` If your model uses a column name other than `deleted_at` for soft deleted records, you may provide the column name when invoking the `withoutTrashed` method: - Rule::unique('users')->withoutTrashed('was_deleted_at'); +```php +Rule::unique('users')->withoutTrashed('was_deleted_at'); +``` #### uppercase @@ -2076,30 +2270,36 @@ You may also validate that the given UUID matches a UUID specification by versio 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`: - use Illuminate\Support\Facades\Validator; +```php +use Illuminate\Support\Facades\Validator; - $validator = Validator::make($data, [ - 'has_appointment' => 'required|boolean', - 'appointment_date' => 'exclude_if:has_appointment,false|required|date', - 'doctor_name' => 'exclude_if:has_appointment,false|required|string', - ]); +$validator = Validator::make($data, [ + 'has_appointment' => 'required|boolean', + 'appointment_date' => 'exclude_if:has_appointment,false|required|date', + 'doctor_name' => 'exclude_if:has_appointment,false|required|string', +]); +``` Alternatively, you may use the `exclude_unless` rule to not validate a given field unless another field has a given value: - $validator = Validator::make($data, [ - 'has_appointment' => 'required|boolean', - 'appointment_date' => 'exclude_unless:has_appointment,true|required|date', - 'doctor_name' => 'exclude_unless:has_appointment,true|required|string', - ]); +```php +$validator = Validator::make($data, [ + 'has_appointment' => 'required|boolean', + 'appointment_date' => 'exclude_unless:has_appointment,true|required|date', + 'doctor_name' => 'exclude_unless:has_appointment,true|required|string', +]); +``` #### Validating When Present 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: - $validator = Validator::make($data, [ - 'email' => 'sometimes|required|email', - ]); +```php +$validator = Validator::make($data, [ + 'email' => 'sometimes|required|email', +]); +``` In the example above, the `email` field will only be validated if it is present in the `$data` array. @@ -2111,26 +2311,32 @@ 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: - use Illuminate\Support\Facades\Validator; +```php +use Illuminate\Support\Facades\Validator; - $validator = Validator::make($request->all(), [ - 'email' => 'required|email', - 'games' => 'required|numeric', - ]); +$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 games. To conditionally add this requirement, we can use the `sometimes` method on the `Validator` instance. - use Illuminate\Support\Fluent; +```php +use Illuminate\Support\Fluent; - $validator->sometimes('reason', 'required|max:500', function (Fluent $input) { - return $input->games >= 100; - }); +$validator->sometimes('reason', 'required|max:500', function (Fluent $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: - $validator->sometimes(['reason', 'cost'], 'required', function (Fluent $input) { - return $input->games >= 100; - }); +```php +$validator->sometimes(['reason', 'cost'], 'required', function (Fluent $input) { + return $input->games >= 100; +}); +``` > [!NOTE] > The `$input` parameter passed to your closure will be an instance of `Illuminate\Support\Fluent` and may be used to access your input and files under validation. @@ -2140,26 +2346,28 @@ The first argument passed to the `sometimes` method is the name of the field we Sometimes you may want to validate a field based on another field in the same nested array whose index you do not know. In these situations, you may allow your closure to receive a second argument which will be the current individual item in the array being validated: - $input = [ - 'channels' => [ - [ - 'type' => 'email', - 'address' => 'abigail@example.com', - ], - [ - 'type' => 'url', - 'address' => 'https://example.com/', - ], +```php +$input = [ + 'channels' => [ + [ + 'type' => 'email', + 'address' => 'abigail@example.com', ], - ]; + [ + 'type' => 'url', + 'address' => 'https://example.com/', + ], + ], +]; - $validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) { - return $item->type === 'email'; - }); +$validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) { + return $item->type === 'email'; +}); - $validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) { - return $item->type !== 'email'; - }); +$validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) { + return $item->type !== 'email'; +}); +``` Like the `$input` parameter passed to the closure, the `$item` parameter is an instance of `Illuminate\Support\Fluent` when the attribute data is an array; otherwise, it is a string. @@ -2168,19 +2376,21 @@ Like the `$input` parameter passed to the closure, the `$item` parameter is an i As discussed in the [`array` validation rule documentation](#rule-array), the `array` rule accepts a list of allowed array keys. If any additional keys are present within the array, validation will fail: - use Illuminate\Support\Facades\Validator; +```php +use Illuminate\Support\Facades\Validator; - $input = [ - 'user' => [ - 'name' => 'Taylor Otwell', - 'username' => 'taylorotwell', - 'admin' => true, - ], - ]; +$input = [ + 'user' => [ + 'name' => 'Taylor Otwell', + 'username' => 'taylorotwell', + 'admin' => true, + ], +]; - Validator::make($input, [ - 'user' => 'array:name,username', - ]); +Validator::make($input, [ + 'user' => 'array:name,username', +]); +``` In general, you should always specify the array keys that are allowed to be present within your array. Otherwise, the validator's `validate` and `validated` methods will return all of the validated data, including the array and all of its keys, even if those keys were not validated by other nested array validation rules. @@ -2189,93 +2399,107 @@ In general, you should always specify the array keys that are allowed to be pres Validating nested 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; +```php +use Illuminate\Support\Facades\Validator; - $validator = Validator::make($request->all(), [ - 'photos.profile' => 'required|image', - ]); +$validator = Validator::make($request->all(), [ + 'photos.profile' => 'required|image', +]); +``` 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', - ]); +```php +$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 [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 email address', - ] - ], +```php +'custom' => [ + 'person.*.email' => [ + 'unique' => 'Each person must have a unique email address', + ] +], +``` #### Accessing Nested Array Data Sometimes you may need to access the value for a given nested array element when assigning validation rules to the attribute. You may accomplish this using the `Rule::forEach` method. The `forEach` method accepts a closure that will be invoked for each iteration of the array attribute under validation and will receive the attribute's value and explicit, fully-expanded attribute name. The closure should return an array of rules to assign to the array element: - use App\Rules\HasPermission; - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; +```php +use App\Rules\HasPermission; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; - $validator = Validator::make($request->all(), [ - 'companies.*.id' => Rule::forEach(function (string|null $value, string $attribute) { - return [ - Rule::exists(Company::class, 'id'), - new HasPermission('manage-company', $value), - ]; - }), - ]); +$validator = Validator::make($request->all(), [ + 'companies.*.id' => Rule::forEach(function (string|null $value, string $attribute) { + return [ + Rule::exists(Company::class, 'id'), + new HasPermission('manage-company', $value), + ]; + }), +]); +``` ### Error Message Indexes and Positions When validating arrays, you may want to reference the index or position of a particular item that failed validation within the error message displayed by your application. To accomplish this, you may include the `:index` (starts from `0`) and `:position` (starts from `1`) placeholders within your [custom validation message](#manual-customizing-the-error-messages): - use Illuminate\Support\Facades\Validator; - - $input = [ - 'photos' => [ - [ - 'name' => 'BeachVacation.jpg', - 'description' => 'A photo of my beach vacation!', - ], - [ - 'name' => 'GrandCanyon.jpg', - 'description' => '', - ], +```php +use Illuminate\Support\Facades\Validator; + +$input = [ + 'photos' => [ + [ + 'name' => 'BeachVacation.jpg', + 'description' => 'A photo of my beach vacation!', ], - ]; + [ + 'name' => 'GrandCanyon.jpg', + 'description' => '', + ], + ], +]; - Validator::validate($input, [ - 'photos.*.description' => 'required', - ], [ - 'photos.*.description.required' => 'Please describe photo #:position.', - ]); +Validator::validate($input, [ + 'photos.*.description' => 'required', +], [ + 'photos.*.description.required' => 'Please describe photo #:position.', +]); +``` Given the example above, validation will fail and the user will be presented with the following error of _"Please describe photo #2."_ If necessary, you may reference more deeply nested indexes and positions via `second-index`, `second-position`, `third-index`, `third-position`, etc. - 'photos.*.attributes.*.string' => 'Invalid attribute for photo #:second-position.', +```php +'photos.*.attributes.*.string' => 'Invalid attribute for photo #:second-position.', +``` ## Validating Files Laravel provides a variety of validation rules that may be used to validate uploaded files, such as `mimes`, `image`, `min`, and `max`. While you are free to specify these rules individually when validating files, Laravel also offers a fluent file validation rule builder that you may find convenient: - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rules\File; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rules\File; - Validator::validate($input, [ - 'attachment' => [ - 'required', - File::types(['mp3', 'wav']) - ->min(1024) - ->max(12 * 1024), - ], - ]); +Validator::validate($input, [ + 'attachment' => [ + 'required', + File::types(['mp3', 'wav']) + ->min(1024) + ->max(12 * 1024), + ], +]); +``` #### Validating File Types @@ -2302,19 +2526,21 @@ If your application accepts images uploaded by your users, you may use the `File In addition, the `dimensions` rule may be used to limit the dimensions of the image: - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rule; - use Illuminate\Validation\Rules\File; - - Validator::validate($input, [ - 'photo' => [ - 'required', - File::image() - ->min(1024) - ->max(12 * 1024) - ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)), - ], - ]); +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\File; + +Validator::validate($input, [ + 'photo' => [ + 'required', + File::image() + ->min(1024) + ->max(12 * 1024) + ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)), + ], +]); +``` > [!NOTE] > More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). @@ -2346,49 +2572,59 @@ File::image()->dimensions( To ensure that passwords have an adequate level of complexity, you may use Laravel's `Password` rule object: - use Illuminate\Support\Facades\Validator; - use Illuminate\Validation\Rules\Password; +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rules\Password; - $validator = Validator::make($request->all(), [ - 'password' => ['required', 'confirmed', Password::min(8)], - ]); +$validator = Validator::make($request->all(), [ + 'password' => ['required', 'confirmed', Password::min(8)], +]); +``` The `Password` rule object allows you to easily customize the password complexity requirements for your application, such as specifying that passwords require at least one letter, number, symbol, or characters with mixed casing: - // Require at least 8 characters... - Password::min(8) +```php +// Require at least 8 characters... +Password::min(8) - // Require at least one letter... - Password::min(8)->letters() +// Require at least one letter... +Password::min(8)->letters() - // Require at least one uppercase and one lowercase letter... - Password::min(8)->mixedCase() +// Require at least one uppercase and one lowercase letter... +Password::min(8)->mixedCase() - // Require at least one number... - Password::min(8)->numbers() +// Require at least one number... +Password::min(8)->numbers() - // Require at least one symbol... - Password::min(8)->symbols() +// Require at least one symbol... +Password::min(8)->symbols() +``` In addition, you may ensure that a password has not been compromised in a public password data breach leak using the `uncompromised` method: - Password::min(8)->uncompromised() +```php +Password::min(8)->uncompromised() +``` Internally, the `Password` rule object uses the [k-Anonymity](https://en.wikipedia.org/wiki/K-anonymity) model to determine if a password has been leaked via the [haveibeenpwned.com](https://haveibeenpwned.com) service without sacrificing the user's privacy or security. By default, if a password appears at least once in a data leak, it will be considered compromised. You can customize this threshold using the first argument of the `uncompromised` method: - // Ensure the password appears less than 3 times in the same data leak... - Password::min(8)->uncompromised(3); +```php +// Ensure the password appears less than 3 times in the same data leak... +Password::min(8)->uncompromised(3); +``` Of course, you may chain all the methods in the examples above: - Password::min(8) - ->letters() - ->mixedCase() - ->numbers() - ->symbols() - ->uncompromised() +```php +Password::min(8) + ->letters() + ->mixedCase() + ->numbers() + ->symbols() + ->uncompromised() +``` #### Defining Default Password Rules @@ -2415,17 +2651,21 @@ public function boot(): void Then, when you would like to apply the default rules to a particular password undergoing validation, you may invoke the `defaults` method with no arguments: - 'password' => ['required', Password::defaults()], +```php +'password' => ['required', Password::defaults()], +``` Occasionally, you may want to attach additional validation rules to your default password validation rules. You may use the `rules` method to accomplish this: - use App\Rules\ZxcvbnRule; +```php +use App\Rules\ZxcvbnRule; - Password::defaults(function () { - $rule = Password::min(8)->rules([new ZxcvbnRule]); +Password::defaults(function () { + $rule = Password::min(8)->rules([new ZxcvbnRule]); - // ... - }); + // ... +}); +``` ## Custom Validation Rules @@ -2441,147 +2681,163 @@ 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 Instead of providing a literal error message to the `$fail` closure, you may also provide a [translation string key](/docs/{{version}}/localization) and instruct Laravel to translate the error message: - if (strtoupper($value) !== $value) { - $fail('validation.uppercase')->translate(); - } +```php +if (strtoupper($value) !== $value) { + $fail('validation.uppercase')->translate(); +} +``` If necessary, you may provide placeholder replacements and the preferred language as the first and second arguments to the `translate` method: - $fail('validation.location')->translate([ - 'value' => $this->value, - ], 'fr') +```php +$fail('validation.location')->translate([ + 'value' => $this->value, +], 'fr') +``` #### Accessing Additional Data If your custom validation rule class needs to access all of the other data undergoing validation, your rule class may implement the `Illuminate\Contracts\Validation\DataAwareRule` interface. This interface requires your class to define a `setData` method. This method will automatically be invoked by Laravel (before validation proceeds) with all of the data under validation: - - */ - protected $data = []; +class Uppercase implements DataAwareRule, ValidationRule +{ + /** + * All of the data under validation. + * + * @var array + */ + protected $data = []; - // ... + // ... - /** - * Set the data under validation. - * - * @param array $data - */ - public function setData(array $data): static - { - $this->data = $data; + /** + * Set the data under validation. + * + * @param array $data + */ + public function setData(array $data): static + { + $this->data = $data; - return $this; - } + return $this; } +} +``` Or, if your validation rule requires access to the validator instance performing the validation, you may implement the `ValidatorAwareRule` interface: - validator = $validator; + /** + * Set the current validator. + */ + public function setValidator(Validator $validator): static + { + $this->validator = $validator; - return $this; - } + return $this; } +} +``` ### 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: - use Illuminate\Support\Facades\Validator; - use Closure; - - $validator = Validator::make($request->all(), [ - 'title' => [ - 'required', - 'max:255', - function (string $attribute, mixed $value, Closure $fail) { - if ($value === 'foo') { - $fail("The {$attribute} is invalid."); - } - }, - ], - ]); +```php +use Illuminate\Support\Facades\Validator; +use Closure; + +$validator = Validator::make($request->all(), [ + 'title' => [ + 'required', + 'max:255', + function (string $attribute, mixed $value, Closure $fail) { + if ($value === 'foo') { + $fail("The {$attribute} is invalid."); + } + }, + ], +]); +``` ### Implicit Rules 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\Facades\Validator; +```php +use Illuminate\Support\Facades\Validator; - $rules = ['name' => 'unique:users,name']; +$rules = ['name' => 'unique:users,name']; - $input = ['name' => '']; +$input = ['name' => '']; - Validator::make($input, $rules)->passes(); // true +Validator::make($input, $rules)->passes(); // true +``` For a custom rule to run even when an attribute is empty, the rule must imply that the attribute is required. To quickly generate a new implicit rule object, you may use the `make:rule` Artisan command with the `--implicit` option: diff --git a/verification.md b/verification.md index d6c0a752499..c959d72835e 100644 --- a/verification.md +++ b/verification.md @@ -24,28 +24,32 @@ Many web applications require users to verify their email addresses before using Before getting started, verify that your `App\Models\User` model implements the `Illuminate\Contracts\Auth\MustVerifyEmail` contract: - ### Database Preparation @@ -66,9 +70,11 @@ Third, a route will be needed to resend a verification link if the user accident 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'); +```php +Route::get('/email/verify', function () { + return view('auth.verify-email'); +})->middleware('auth')->name('verification.notice'); +``` The route that returns the email verification notice should be named `verification.notice`. It is important that the route is 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. @@ -80,13 +86,15 @@ The route that returns the email verification notice should be named `verificati 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; +```php +use Illuminate\Foundation\Auth\EmailVerificationRequest; - Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) { - $request->fulfill(); +Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) { + $request->fulfill(); - return redirect('/home'); - })->middleware(['auth', 'signed'])->name('verification.verify'); + 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 automatically take care of validating the request's `id` and `hash` parameters. @@ -97,22 +105,26 @@ Next, we can proceed directly to calling the `fulfill` method on the request. Th Sometimes a user may misplace or accidentally delete the email address verification email. To accommodate this, you may wish to define a route to allow the user to request that the verification email be resent. You may then make a request to this route by placing a simple form submission button within your [verification notice view](#the-email-verification-notice): - use Illuminate\Http\Request; +```php +use Illuminate\Http\Request; - Route::post('/email/verification-notification', function (Request $request) { - $request->user()->sendEmailVerificationNotification(); +Route::post('/email/verification-notification', function (Request $request) { + $request->user()->sendEmailVerificationNotification(); - return back()->with('message', 'Verification link sent!'); - })->middleware(['auth', 'throttle:6,1'])->name('verification.send'); + return back()->with('message', 'Verification link sent!'); +})->middleware(['auth', 'throttle:6,1'])->name('verification.send'); +``` ### Protecting Routes [Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel includes a `verified` [middleware alias](/docs/{{version}}/middleware#middleware-aliases), which is an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` middleware class. Since this alias is already automatically registered by Laravel, all you need to do is attach the `verified` middleware to a route definition. Typically, this middleware is paired with the `auth` middleware: - Route::get('/profile', function () { - // Only verified users may access this route... - })->middleware(['auth', 'verified']); +```php +Route::get('/profile', function () { + // Only verified users may access this route... +})->middleware(['auth', '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). @@ -126,23 +138,25 @@ Although the default email verification notification should satisfy the requirem To get started, pass a closure to the `toMailUsing` method provided by the `Illuminate\Auth\Notifications\VerifyEmail` notification. The closure will receive the notifiable model instance that is receiving the notification as well as the signed email verification URL that the user must visit to verify their email address. The closure should return an instance of `Illuminate\Notifications\Messages\MailMessage`. Typically, you should call the `toMailUsing` method from the `boot` method of your application's `AppServiceProvider` class: - use Illuminate\Auth\Notifications\VerifyEmail; - use Illuminate\Notifications\Messages\MailMessage; - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - // ... - - VerifyEmail::toMailUsing(function (object $notifiable, string $url) { - return (new MailMessage) - ->subject('Verify Email Address') - ->line('Click the button below to verify your email address.') - ->action('Verify Email Address', $url); - }); - } +```php +use Illuminate\Auth\Notifications\VerifyEmail; +use Illuminate\Notifications\Messages\MailMessage; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + // ... + + VerifyEmail::toMailUsing(function (object $notifiable, string $url) { + return (new MailMessage) + ->subject('Verify Email Address') + ->line('Click the button below to verify your email address.') + ->action('Verify Email Address', $url); + }); +} +``` > [!NOTE] > To learn more about mail notifications, please consult the [mail notification documentation](/docs/{{version}}/notifications#mail-notifications). diff --git a/views.md b/views.md index d57a46f1cce..cc55a972431 100644 --- a/views.md +++ b/views.md @@ -31,9 +31,11 @@ Views separate your controller / application logic from your presentation logic Since this view is stored at `resources/views/greeting.blade.php`, we may return it using the global `view` helper like so: - Route::get('/', function () { - return view('greeting', ['name' => 'James']); - }); +```php +Route::get('/', function () { + return view('greeting', ['name' => 'James']); +}); +``` > [!NOTE] > Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. @@ -58,15 +60,19 @@ The `.blade.php` extension informs the framework that the file contains a [Blade Once you have created a view, you may return it from one of your application's routes or controllers using the global `view` helper: - Route::get('/', function () { - return view('greeting', ['name' => 'James']); - }); +```php +Route::get('/', function () { + return view('greeting', ['name' => 'James']); +}); +``` Views may also be returned using the `View` facade: - use Illuminate\Support\Facades\View; +```php +use Illuminate\Support\Facades\View; - return View::make('greeting', ['name' => 'James']); +return View::make('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). @@ -75,7 +81,9 @@ As you can see, the first argument passed to the `view` helper corresponds to th 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); +```php +return view('admin.profile', $data); +``` > [!WARNING] > View directory names should not contain the `.` character. @@ -85,65 +93,75 @@ Views may also be nested within subdirectories of the `resources/views` director 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; +```php +use Illuminate\Support\Facades\View; - return View::first(['custom.admin', 'admin'], $data); +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; +```php +use Illuminate\Support\Facades\View; - if (View::exists('admin.profile')) { - // ... - } +if (View::exists('admin.profile')) { + // ... +} +``` ## Passing 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']); +```php +return view('greetings', ['name' => 'Victoria']); +``` 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 ``. 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'); +```php +return view('greeting') + ->with('name', 'Victoria') + ->with('occupation', 'Astronaut'); +``` ### Sharing Data With All Views 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 @@ -154,70 +172,74 @@ Typically, view composers will be registered within one of your application's [s 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/View/Composers` directory to house all of your application's view composers: - with('count', $this->users->count()); - } + $view->with('count', $this->users->count()); } +} +``` 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. @@ -226,32 +248,38 @@ As you can see, all view composers are resolved via the [service container](/doc 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\Views\Composers\MultiComposer; - use Illuminate\Support\Facades\View; +```php +use App\Views\Composers\MultiComposer; +use Illuminate\Support\Facades\View; - View::composer( - ['profile', 'dashboard'], - MultiComposer::class - ); +View::composer( + ['profile', 'dashboard'], + MultiComposer::class +); +``` The `composer` method also accepts the `*` character as a wildcard, allowing you to attach a composer to all views: - use Illuminate\Support\Facades; - use Illuminate\View\View; +```php +use Illuminate\Support\Facades; +use Illuminate\View\View; - Facades\View::composer('*', function (View $view) { - // ... - }); +Facades\View::composer('*', function (View $view) { + // ... +}); +``` ### 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: - use App\View\Creators\ProfileCreator; - use Illuminate\Support\Facades\View; +```php +use App\View\Creators\ProfileCreator; +use Illuminate\Support\Facades\View; - View::creator('profile', ProfileCreator::class); +View::creator('profile', ProfileCreator::class); +``` ## Optimizing Views diff --git a/vite.md b/vite.md index 4464c5d1cc3..7bdd9b9e10f 100644 --- a/vite.md +++ b/vite.md @@ -552,13 +552,15 @@ export default defineConfig({ It is common in JavaScript applications to [create aliases](#aliases) to regularly referenced directories. But, you may also create aliases to use in Blade by using the `macro` method on the `Illuminate\Support\Facades\Vite` class. Typically, "macros" should be defined within the `boot` method of a [service provider](/docs/{{version}}/providers): - /** - * Bootstrap any application services. - */ - public function boot(): void - { - Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}")); - } +```php +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}")); +} +``` Once a macro has been defined, it can be invoked within your templates. For example, we can use the `image` macro defined above to reference an asset located at `resources/images/logo.png`: From 221277cf88efd5e033e9f0e90664d0a627c6b3a3 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 19 Feb 2025 14:53:57 -0600 Subject: [PATCH 2020/2609] remove extra spaces (#10182) errant spaces From 1274dada07e0afb8bd2f0bdf636280e2120a9eca Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 19 Feb 2025 15:26:54 -0600 Subject: [PATCH 2021/2609] fix code fence language (#10185) - use "ini" for `.env` examples - use "text" for plain text blocks - remove some empty line breaks at and inside the end of code blocks - add some missing "php" and "shell" entries --- context.md | 4 ++-- dusk.md | 2 +- filesystem.md | 2 +- http-tests.md | 1 - pennant.md | 1 - pulse.md | 4 ++-- redirects.md | 2 +- reverb.md | 2 +- 8 files changed, 8 insertions(+), 10 deletions(-) diff --git a/context.md b/context.md index 76bd2d01bfc..8c95bde587a 100644 --- a/context.md +++ b/context.md @@ -56,7 +56,7 @@ Log::info('User authenticated.', ['auth_id' => Auth::id()]); The written log will contain the `auth_id` passed to the log entry, but it will also contain the context's `url` and `trace_id` as metadata: -``` +```text User authenticated. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"} ``` @@ -96,7 +96,7 @@ class ProcessPodcast implements ShouldQueue The resulting log entry would contain the information that was added to the context during the request that originally dispatched the job: -``` +```text Processing podcast. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"} ``` diff --git a/dusk.md b/dusk.md index 004ad6b6414..b92e2308970 100644 --- a/dusk.md +++ b/dusk.md @@ -234,8 +234,8 @@ By default, this trait will truncate all tables except the `migrations` table. I * @var array */ protected $tablesToTruncate = ['users']; - ``` + Alternatively, you may define an `$exceptTables` property on your test class to specify which tables should be excluded from truncation: ```php diff --git a/filesystem.md b/filesystem.md index d50402e30f8..0a586523bcf 100644 --- a/filesystem.md +++ b/filesystem.md @@ -99,7 +99,7 @@ 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 AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 diff --git a/http-tests.md b/http-tests.md index 29a978a6802..8ad87f6920f 100644 --- a/http-tests.md +++ b/http-tests.md @@ -514,7 +514,6 @@ test('asserting an exact json match', function () { 'created' => true, ]); }); - ``` ```php tab=PHPUnit diff --git a/pennant.md b/pennant.md index f158490a59c..d85ee7781aa 100644 --- a/pennant.md +++ b/pennant.md @@ -1178,7 +1178,6 @@ public function boot(): void { Event::listen(UnexpectedNullScopeEncountered::class, fn () => abort(500)); } - ``` ### `Laravel\Pennant\Events\FeatureUpdated` diff --git a/pulse.md b/pulse.md index e129001bfc2..2ebf1bb4603 100644 --- a/pulse.md +++ b/pulse.md @@ -476,13 +476,13 @@ PULSE_DB_CONNECTION=pulse By default, Pulse will store entries directly to the [configured database connection](#using-a-different-database) after the HTTP response has been sent to the client or a job has been processed; however, you may use Pulse's Redis ingest driver to send entries to a Redis stream instead. This can be enabled by configuring the `PULSE_INGEST_DRIVER` environment variable: -``` +```ini PULSE_INGEST_DRIVER=redis ``` Pulse will use your default [Redis connection](/docs/{{version}}/redis#configuration) by default, but you may customize this via the `PULSE_REDIS_CONNECTION` environment variable: -``` +```ini PULSE_REDIS_CONNECTION=pulse ``` diff --git a/redirects.md b/redirects.md index a4fbf8772d2..884ff8d39db 100644 --- a/redirects.md +++ b/redirects.md @@ -14,8 +14,8 @@ Redirect responses are instances of the `Illuminate\Http\RedirectResponse` class 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: ```php diff --git a/reverb.md b/reverb.md index 49f2d3e60aa..8dd0ed34a69 100644 --- a/reverb.md +++ b/reverb.md @@ -29,7 +29,7 @@ You may install Reverb using the `install:broadcasting` Artisan command: -``` +```shell php artisan install:broadcasting ``` From 92f71c041506cbdb478fc8bce9cb7c55972e0308 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 19 Feb 2025 15:27:07 -0600 Subject: [PATCH 2022/2609] remove extra linebreaks (#10184) 2 consecutive empty lines --- collections.md | 3 --- dusk.md | 1 - 2 files changed, 4 deletions(-) diff --git a/collections.md b/collections.md index d4164de0e8f..8037ab297c7 100644 --- a/collections.md +++ b/collections.md @@ -442,7 +442,6 @@ $collection = collect([ ['third' => collect([7, 8, 9])] ]); - $collapsed = $collection->collapseWithKeys(); $collapsed->all(); @@ -3594,7 +3593,6 @@ $collection->all(); // ['Michael', 'Tom'] - $collection = collect(); $collection->whenEmpty(function (Collection $collection) { @@ -3640,7 +3638,6 @@ $collection->all(); // ['michael', 'tom', 'adam'] - $collection = collect(); $collection->whenNotEmpty(function (Collection $collection) { diff --git a/dusk.md b/dusk.md index b92e2308970..09c26e14acd 100644 --- a/dusk.md +++ b/dusk.md @@ -1958,7 +1958,6 @@ Assert that the element matching the given selector is missing the provided attr $browser->assertAttributeMissing($selector, $attribute); ``` - #### assertAttributeContains From 1cd90c88ffb9b99f98610e0893ff2f503ec9a2ec Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 19 Feb 2025 15:50:46 -0600 Subject: [PATCH 2023/2609] standardize ellipsis usage (#10187) in order to indicate "more content" or "content not relevant to example", we often use an ellipsis. in PHP and other languages, this pattern actually is relevant to the language. this change standardizes usage of the ellipsis for the above examples to be in a comment. this give us valid PHP and Javascipt, and reduced ambiguity with the language construct. --- broadcasting.md | 2 +- passport.md | 4 ++-- queues.md | 4 ++-- reverb.md | 2 +- telescope.md | 16 ++++++++-------- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index cc6b664619a..a0bd1ab3e08 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -500,7 +500,7 @@ If you customize the broadcast name using the `broadcastAs` method, you should m ```javascript .listen('.server.created', function (e) { - .... + // ... }); ``` diff --git a/passport.md b/passport.md index 26729dd3ec0..262228c3f8d 100644 --- a/passport.md +++ b/passport.md @@ -853,7 +853,7 @@ Then, attach the middleware to a route: ```php Route::get('/orders', function (Request $request) { - ... + // ... })->middleware('client'); ``` @@ -861,7 +861,7 @@ To restrict access to the route to specific scopes, you may provide a comma-deli ```php Route::get('/orders', function (Request $request) { - ... + // ... })->middleware('client:check-status,your-scope'); ``` diff --git a/queues.md b/queues.md index 7f17862d426..aae4cba466a 100644 --- a/queues.md +++ b/queues.md @@ -288,7 +288,7 @@ use Illuminate\Contracts\Queue\ShouldBeUnique; class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique { - ... + // ... } ``` @@ -363,7 +363,7 @@ use Illuminate\Support\Facades\Cache; class UpdateSearchIndex implements ShouldQueue, ShouldBeUnique { - ... + // ... /** * Get the cache driver for the unique job lock. diff --git a/reverb.md b/reverb.md index 8dd0ed34a69..7c9a6788a80 100644 --- a/reverb.md +++ b/reverb.md @@ -176,7 +176,7 @@ use Laravel\Reverb\Pulse\Recorders\ReverbMessages; 'sample_rate' => 1, ], - ... + // ... ], ``` diff --git a/telescope.md b/telescope.md index 4365ffcba65..7b82058b64d 100644 --- a/telescope.md +++ b/telescope.md @@ -276,7 +276,7 @@ Telescope "watchers" gather application data when a request or console command i 'watchers' => [ Watchers\CacheWatcher::class => true, Watchers\CommandWatcher::class => true, - ... + // ... ], ``` @@ -288,7 +288,7 @@ Some watchers also allow you to provide additional customization options: 'enabled' => env('TELESCOPE_QUERY_WATCHER', true), 'slow' => 100, ], - ... + // ... ], ``` @@ -313,7 +313,7 @@ The command watcher records the arguments, options, exit code, and output whenev 'enabled' => env('TELESCOPE_COMMAND_WATCHER', true), 'ignore' => ['key:generate'], ], - ... + // ... ], ``` @@ -343,7 +343,7 @@ The gate watcher records the data and result of [gate and policy](/docs/{{versio 'enabled' => env('TELESCOPE_GATE_WATCHER', true), 'ignore_abilities' => ['viewNova'], ], - ... + // ... ], ``` @@ -391,7 +391,7 @@ The model watcher records model changes whenever an Eloquent [model event](/docs 'enabled' => env('TELESCOPE_MODEL_WATCHER', true), 'events' => ['eloquent.created*', 'eloquent.updated*'], ], - ... + // ... ], ``` @@ -404,7 +404,7 @@ If you would like to record the number of models hydrated during a given request 'events' => ['eloquent.created*', 'eloquent.updated*'], 'hydrations' => true, ], - ... + // ... ], ``` @@ -424,7 +424,7 @@ The query watcher records the raw SQL, bindings, and execution time for all quer 'enabled' => env('TELESCOPE_QUERY_WATCHER', true), 'slow' => 50, ], - ... + // ... ], ``` @@ -444,7 +444,7 @@ The request watcher records the request, headers, session, and response data ass 'enabled' => env('TELESCOPE_REQUEST_WATCHER', true), 'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64), ], - ... + // ... ], ``` From 40c254f33b0bcb4a58e60af7ba9171167baa2440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A9Phil-Graham=E2=84=A2=20=E2=98=95?= Date: Thu, 20 Feb 2025 14:41:05 +0000 Subject: [PATCH 2024/2609] Update strings.md (#10189) Fix typo shown in the wrap code block --- strings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings.md b/strings.md index 15166f189fd..d489dff62da 100644 --- a/strings.md +++ b/strings.md @@ -3100,6 +3100,6 @@ The `wrap` method wraps the given string with an additional string or pair of st // "Laravel" - Str::is('is')->wrap(before: 'This ', after: ' Laravel!'); + Str::of('is')->wrap(before: 'This ', after: ' Laravel!'); // This is Laravel! From bea48ec9fd314ebb6d578854fcc7d33c0ac77108 Mon Sep 17 00:00:00 2001 From: Pandi Selvam M <84308225+pandiselvamm@users.noreply.github.com> Date: Fri, 21 Feb 2025 18:30:57 +0530 Subject: [PATCH 2025/2609] [11.x] Updated Maintenance Mode Configuration Doc for Env (#10193) * Updated Maintenance Mode Configuration Doc for Env * Removed extra empty line * Update configuration.md --------- Co-authored-by: Pandi Selvam Co-authored-by: Taylor Otwell --- configuration.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/configuration.md b/configuration.md index e5ba140778b..03316696b6b 100644 --- a/configuration.md +++ b/configuration.md @@ -310,13 +310,11 @@ When accessing this hidden route, you will then be redirected to the `/` route o By default, Laravel determines if your application is in maintenance mode using a file-based system. This means to activate maintenance mode, the `php artisan down` command has to be executed on each server hosting your application. -Alternatively, Laravel offers a cache-based method for handling maintenance mode. This method requires running the `php artisan down` command on just one server. To use this approach, modify the "driver" setting in the `config/app.php` file of your application to `cache`. Then, select a cache `store` that is accessible by all your servers. This ensures the maintenance mode status is consistently maintained across every server: +Alternatively, Laravel offers a cache-based method for handling maintenance mode. This method requires running the `php artisan down` command on just one server. To use this approach, modify the maintenance mode variables in your application's `.env` file. You should select a cache `store` that is accessible by all of your servers. This ensures the maintenance mode status is consistently maintained across every server: -```php -'maintenance' => [ - 'driver' => 'cache', - 'store' => 'database', -], +```ini +APP_MAINTENANCE_DRIVER=cache +APP_MAINTENANCE_STORE=database ``` From 68c1d757aa86b2ebecf5f4085564c96241b57a82 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 21 Feb 2025 17:38:49 -0600 Subject: [PATCH 2026/2609] 12.x Docs (#10188) --- authentication.md | 24 +-- cache.md | 4 +- configuration.md | 2 +- container.md | 10 +- contributions.md | 5 +- deployment.md | 25 ++- documentation.md | 2 - eloquent-relationships.md | 22 +-- eloquent.md | 4 - fortify.md | 6 +- frontend.md | 8 +- horizon.md | 2 +- installation.md | 11 +- octane.md | 2 +- queues.md | 2 +- releases.md | 20 ++- scheduling.md | 2 +- starter-kits.md | 362 ++++++++++++++++++++++++++++++-------- structure.md | 3 - upgrade.md | 80 +++++++-- views.md | 2 +- vite.md | 8 +- 22 files changed, 437 insertions(+), 169 deletions(-) diff --git a/authentication.md b/authentication.md index dd82403a01e..a06f281b202 100644 --- a/authentication.md +++ b/authentication.md @@ -48,7 +48,7 @@ Your application's authentication configuration file is located at `config/auth. 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! -**Even if you choose not to 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. +**Even if you choose not to use a starter kit in your final Laravel application, installing a [starter kit](/docs/{{version}}/starter-kits) can be a wonderful opportunity to learn how to implement all of Laravel's authentication functionality in an actual Laravel project.** Since the Laravel starter kits contain 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 @@ -77,13 +77,7 @@ Laravel includes built-in authentication and session services which are typicall **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](/docs/{{version}}/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](https://tailwindcss.com). To get started, check out the documentation on Laravel's [application starter kits](/docs/{{version}}/starter-kits). - -_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 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://livewire.laravel.com), and / or [Inertia](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. +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 starter kits](/docs/{{version}}/starter-kits) that provide robust, modern scaffolding of the entire authentication layer. #### Laravel's API Authentication Services @@ -100,8 +94,6 @@ In response to the complexity of OAuth2 and developer confusion, we set out to b 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) application starter kit because we believe it is the best fit for the majority of web application's authentication needs. - #### Summary and Choosing Your Stack @@ -113,7 +105,7 @@ If you are building a single-page application (SPA) that will be powered by a La Passport may be chosen when your application absolutely needs all of the features provided by the OAuth2 specification. -And, if you would like to get started quickly, we are pleased to recommend [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze) as a quick way to start a new Laravel application that already uses our preferred authentication stack of Laravel's built-in authentication services and Laravel Sanctum. +And, if you would like to get started quickly, we are pleased to recommend [our application starter kits](/docs/{{version}}/starter-kits) as a quick way to start a new Laravel application that already uses our preferred authentication stack of Laravel's built-in authentication services. ## Authentication Quickstart @@ -124,16 +116,12 @@ And, if you would like to get started quickly, we are pleased to recommend [Lara ### Install a Starter Kit -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. - -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](https://tailwindcss.com). Additionally, Breeze provides scaffolding options based on [Livewire](https://livewire.laravel.com) or [Inertia](https://inertiajs.com), with the choice of using Vue or React for the Inertia-based scaffolding. - -[Laravel Jetstream](https://jetstream.laravel.com) is a more robust application starter kit that includes support for scaffolding your application with [Livewire](https://livewire.laravel.com) or [Inertia 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. +First, you should [install a Laravel application starter kit](/docs/{{version}}/starter-kits). Our starter kits offer beautifully designed starting points for incorporating authentication into your fresh Laravel application. ### Retrieving the Authenticated User -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: +After creating an application from a 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: ```php use Illuminate\Support\Facades\Auth; @@ -228,7 +216,7 @@ Route::get('/flights', function () { ### Login Throttling -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. +If you are using one of our [application 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. > [!NOTE] > If you would like to rate limit other routes in your application, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). diff --git a/cache.md b/cache.md index 0693ffc80e7..4bc9aad60b3 100644 --- a/cache.md +++ b/cache.md @@ -83,7 +83,7 @@ If needed, you may set the `host` option to a UNIX socket path. If you do this, #### 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 (~2.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. +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 (~2.0) via Composer. [Laravel Sail](/docs/{{version}}/sail) already includes this extension. In addition, official Laravel application platforms such as [Laravel Cloud](https://cloud.laravel.com) and [Laravel Forge](https://forge.laravel.com) have the PhpRedis extension installed by default. For more information on configuring Redis, consult its [Laravel documentation page](/docs/{{version}}/redis#configuration). @@ -362,7 +362,7 @@ cache()->remember('users', $seconds, function () { ### Managing Locks -Atomic locks allow for the manipulation of distributed locks without worrying about race conditions. For example, [Laravel Forge](https://forge.laravel.com) uses atomic locks to ensure that only one remote task is being executed on a server at a time. You may create and manage locks using the `Cache::lock` method: +Atomic locks allow for the manipulation of distributed locks without worrying about race conditions. For example, [Laravel Cloud](https://cloud.laravel.com) uses atomic locks to ensure that only one remote task is being executed on a server at a time. You may create and manage locks using the `Cache::lock` method: ```php use Illuminate\Support\Facades\Cache; diff --git a/configuration.md b/configuration.md index 17a20e712b3..7d1ac6877d4 100644 --- a/configuration.md +++ b/configuration.md @@ -371,4 +371,4 @@ While your application is in maintenance mode, no [queued jobs](/docs/{{version} #### Alternatives to Maintenance Mode -Since maintenance mode requires your application to have several seconds of downtime, consider alternatives like [Laravel Vapor](https://vapor.laravel.com) and [Envoyer](https://envoyer.io) to accomplish zero-downtime deployment with Laravel. +Since maintenance mode requires your application to have several seconds of downtime, consider running your applications on a fully-managed platform like [Laravel Cloud](https://cloud.laravel.com) to accomplish zero-downtime deployment with Laravel. diff --git a/container.md b/container.md index a7d0c0356bd..0bf69473340 100644 --- a/container.md +++ b/container.md @@ -144,7 +144,15 @@ $this->app->bindIf(Transistor::class, function (Application $app) { }); ``` -> [!NOTE] +For convenience, you may omit providing the class or interface name that you wish to register as a separate argument and instead allow Laravel to infer the type from the return type of the closure you provide to the `bind` method: + +```php +App::bind(function (Application $app): Transistor { + return new Transistor($app->make(PodcastParser::class)); +}); +``` + +> [!NOTE] > There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection. diff --git a/contributions.md b/contributions.md index 485749eb39d..54f24b97c5a 100644 --- a/contributions.md +++ b/contributions.md @@ -28,7 +28,6 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Application](https://github.com/laravel/laravel) - [Laravel Art](https://github.com/laravel/art) -- [Laravel Breeze](https://github.com/laravel/breeze) - [Laravel Documentation](https://github.com/laravel/docs) - [Laravel Dusk](https://github.com/laravel/dusk) - [Laravel Cashier Stripe](https://github.com/laravel/cashier) @@ -39,17 +38,19 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Framework](https://github.com/laravel/framework) - [Laravel Homestead](https://github.com/laravel/homestead) ([Build Scripts](https://github.com/laravel/settler)) - [Laravel Horizon](https://github.com/laravel/horizon) -- [Laravel Jetstream](https://github.com/laravel/jetstream) +- [Laravel Livewire Starter Kit](https://github.com/laravel/livewire-starter-kit) - [Laravel Passport](https://github.com/laravel/passport) - [Laravel Pennant](https://github.com/laravel/pennant) - [Laravel Pint](https://github.com/laravel/pint) - [Laravel Prompts](https://github.com/laravel/prompts) +- [Laravel React Starter Kit](https://github.com/laravel/react-starter-kit) - [Laravel Reverb](https://github.com/laravel/reverb) - [Laravel Sail](https://github.com/laravel/sail) - [Laravel Sanctum](https://github.com/laravel/sanctum) - [Laravel Scout](https://github.com/laravel/scout) - [Laravel Socialite](https://github.com/laravel/socialite) - [Laravel Telescope](https://github.com/laravel/telescope) +- [Laravel Vue Starter Kit](https://github.com/laravel/vue-starter-kit) - [Laravel Website](https://github.com/laravel/laravel.com) diff --git a/deployment.md b/deployment.md index 66490697147..158ce5a2fc3 100644 --- a/deployment.md +++ b/deployment.md @@ -13,7 +13,7 @@ - [Caching Views](#optimizing-view-loading) - [Debug Mode](#debug-mode) - [The Health Route](#the-health-route) -- [Easy Deployment With Forge / Vapor](#deploying-with-forge-or-vapor) +- [Deploying With Laravel Cloud or Forge](#deploying-with-cloud-or-forge) ## Introduction @@ -50,7 +50,7 @@ The Laravel framework has a few system requirements. You should ensure that your ### Nginx -If you are deploying your application to a server that is running Nginx, you may use the following configuration file as a starting point for configuring your web server. Most likely, this file will need to be customized depending on your server's configuration. **If you would like assistance in managing your server, consider using a first-party Laravel server management and deployment service such as [Laravel Forge](https://forge.laravel.com).** +If you are deploying your application to a server that is running Nginx, you may use the following configuration file as a starting point for configuring your web server. Most likely, this file will need to be customized depending on your server's configuration. **If you would like assistance in managing your server, consider using a fully-managed Laravel platform like [Laravel Cloud](https://cloud.laravel.com).** Please ensure, like the configuration below, your web server directs all requests to your application's `public/index.php` file. You should never attempt to move the `index.php` file to your project's root, as serving the application from the project root will expose many sensitive configuration files to the public Internet: @@ -194,20 +194,19 @@ By default, the health check route is served at `/up` and will return a 200 HTTP When HTTP requests are made to this route, Laravel will also dispatch a `Illuminate\Foundation\Events\DiagnosingHealth` event, allowing you to perform additional health checks relevant to your application. Within a [listener](/docs/{{version}}/events) for this event, you may check your application's database or cache status. If you detect a problem with your application, you may simply throw an exception from the listener. - -## Easy Deployment With Forge / Vapor + +## Deploying With Laravel Cloud or Forge - -#### Laravel Forge + +#### Laravel Cloud -If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel application, [Laravel Forge](https://forge.laravel.com) is a wonderful alternative. +If you would like a fully-managed, auto-scaling deployment platform tuned for Laravel, check out [Laravel Cloud](https://cloud.laravel.com). Laravel Cloud is a robust deployment platform for Laravel, offering managed compute, databases, caches, and object storage. -Laravel Forge can create servers on various infrastructure providers such as DigitalOcean, Linode, AWS, and more. In addition, Forge installs and manages all of the tools needed to build robust Laravel applications, such as Nginx, MySQL, Redis, Memcached, Beanstalk, and more. +Launch your Laravel application on Cloud and fall in love with the scalable simplicity. Laravel Cloud is fine-tuned by Laravel's creators to work seamlessly with the framework so you can keep writing your Laravel applications exactly like you're used to. -> [!NOTE] -> Want a full guide to deploying with Laravel Forge? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com/deploying) and the Forge [video series available on Laracasts](https://laracasts.com/series/learn-laravel-forge-2022-edition). + +#### Laravel Forge - -#### Laravel Vapor +If you prefer to manage your own servers but aren't comfortable configuring all of the various services needed to run a robust Laravel application, [Laravel Forge](https://forge.laravel.com) is a VPS server management platform for Laravel applications. -If you would like a totally serverless, auto-scaling deployment platform tuned for Laravel, check out [Laravel Vapor](https://vapor.laravel.com). Laravel Vapor is a serverless deployment platform for Laravel, powered by AWS. Launch your Laravel infrastructure on Vapor and fall in love with the scalable simplicity of serverless. Laravel Vapor is fine-tuned by Laravel's creators to work seamlessly with the framework so you can keep writing your Laravel applications exactly like you're used to. +Laravel Forge can create servers on various infrastructure providers such as DigitalOcean, Linode, AWS, and more. In addition, Forge installs and manages all of the tools needed to build robust Laravel applications, such as Nginx, MySQL, Redis, Memcached, Beanstalk, and more. diff --git a/documentation.md b/documentation.md index c0eecca09eb..a76e612a10d 100644 --- a/documentation.md +++ b/documentation.md @@ -81,7 +81,6 @@ - [Database](/docs/{{version}}/database-testing) - [Mocking](/docs/{{version}}/mocking) - ## Packages - - [Breeze](/docs/{{version}}/starter-kits#laravel-breeze) - [Cashier (Stripe)](/docs/{{version}}/billing) - [Cashier (Paddle)](/docs/{{version}}/cashier-paddle) - [Dusk](/docs/{{version}}/dusk) @@ -90,7 +89,6 @@ - [Folio](/docs/{{version}}/folio) - [Homestead](/docs/{{version}}/homestead) - [Horizon](/docs/{{version}}/horizon) - - [Jetstream](https://jetstream.laravel.com) - [Mix](/docs/{{version}}/mix) - [Octane](/docs/{{version}}/octane) - [Passport](/docs/{{version}}/passport) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index d2020525d2a..f42fb93d84a 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -585,16 +585,16 @@ return $this->throughCars()->hasOwner(); ### Has Many Through -The "has-many-through" relationship provides a convenient way to access distant relations via an intermediate relation. For example, let's assume we are building a deployment platform like [Laravel Vapor](https://vapor.laravel.com). A `Project` model might access many `Deployment` models through an intermediate `Environment` model. Using this example, you could easily gather all deployments for a given project. Let's look at the tables required to define this relationship: +The "has-many-through" relationship provides a convenient way to access distant relations via an intermediate relation. For example, let's assume we are building a deployment platform like [Laravel Cloud](https://cloud.laravel.com). An `Application` model might access many `Deployment` models through an intermediate `Environment` model. Using this example, you could easily gather all deployments for a given application. Let's look at the tables required to define this relationship: ```text -projects +applications id - integer name - string environments id - integer - project_id - integer + application_id - integer name - string deployments @@ -603,7 +603,7 @@ deployments commit_hash - string ``` -Now that we have examined the table structure for the relationship, let's define the relationship on the `Project` model: +Now that we have examined the table structure for the relationship, let's define the relationship on the `Application` model: ```php through('environments')->has('deployments'); return $this->throughEnvironments()->hasDeployments(); ``` -Though the `Deployment` model's table does not contain a `project_id` column, the `hasManyThrough` relation provides access to a project's deployments via `$project->deployments`. To retrieve these models, Eloquent inspects the `project_id` column on the intermediate `Environment` model's table. After finding the relevant environment IDs, they are used to query the `Deployment` model's table. +Though the `Deployment` model's table does not contain a `application_id` column, the `hasManyThrough` relation provides access to a application's deployments via `$application->deployments`. To retrieve these models, Eloquent inspects the `application_id` column on the intermediate `Environment` model's table. After finding the relevant environment IDs, they are used to query the `Deployment` model's table. #### Key Conventions @@ -645,16 +645,16 @@ Though the `Deployment` model's table does not contain a `project_id` column, th Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasManyThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: ```php -class Project extends Model +class Application extends Model { public function deployments(): HasManyThrough { return $this->hasManyThrough( Deployment::class, Environment::class, - 'project_id', // Foreign key on the environments table... + 'application_id', // Foreign key on the environments table... 'environment_id', // Foreign key on the deployments table... - 'id', // Local key on the projects table... + 'id', // Local key on the applications table... 'id' // Local key on the environments table... ); } diff --git a/eloquent.md b/eloquent.md index ab6c8ff076d..643b2d421d8 100644 --- a/eloquent.md +++ b/eloquent.md @@ -47,10 +47,6 @@ Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoy > [!NOTE] > Before getting started, be sure to configure a database connection in your application's `config/database.php` configuration file. For more information on configuring your database, check out [the database configuration documentation](/docs/{{version}}/database#configuration). -#### Laravel Bootcamp - -If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Eloquent. It's a great way to get a tour of everything that Laravel and Eloquent have to offer. - ## Generating Model Classes diff --git a/fortify.md b/fortify.md index 19e5d7a37e8..4df2abef52e 100644 --- a/fortify.md +++ b/fortify.md @@ -41,9 +41,9 @@ As mentioned previously, Laravel Fortify is a frontend agnostic authentication b **You are not required to use Fortify in order to use Laravel's authentication features.** You are always free to manually interact with Laravel's authentication services by following the documentation available in the [authentication](/docs/{{version}}/authentication), [password reset](/docs/{{version}}/passwords), and [email verification](/docs/{{version}}/verification) documentation. -If you are new to Laravel, you may wish to explore the [Laravel Breeze](/docs/{{version}}/starter-kits) application starter kit before attempting to use Laravel Fortify. Laravel Breeze provides an authentication scaffolding for your application that includes a user interface built with [Tailwind CSS](https://tailwindcss.com). Unlike Fortify, Breeze publishes its routes and controllers directly into your application. This allows you to study and get comfortable with Laravel's authentication features before allowing Laravel Fortify to implement these features for you. +If you are new to Laravel, you may wish to explore [our application starter kits](/docs/{{version}}/starter-kits) before attempting to use Laravel Fortify. Our starter kits provide an authentication scaffolding for your application that includes a user interface built with [Tailwind CSS](https://tailwindcss.com). This allows you to study and get comfortable with Laravel's authentication features before allowing Laravel Fortify to implement these features for you. -Laravel Fortify essentially takes the routes and controllers of Laravel Breeze and offers them as a package that does not include a user interface. This allows you to still quickly scaffold the backend implementation of your application's authentication layer without being tied to any particular frontend opinions. +Laravel Fortify essentially takes the routes and controllers of our application starter kits and offers them as a package that does not include a user interface. This allows you to still quickly scaffold the backend implementation of your application's authentication layer without being tied to any particular frontend opinions. ### When Should I Use Fortify? @@ -91,7 +91,7 @@ php artisan migrate ### Fortify Features -The `fortify` configuration file contains a `features` configuration array. This array defines which backend routes / features Fortify will expose by default. If you are not using Fortify in combination with [Laravel Jetstream](https://jetstream.laravel.com), we recommend that you only enable the following features, which are the basic authentication features provided by most Laravel applications: +The `fortify` configuration file contains a `features` configuration array. This array defines which backend routes / features Fortify will expose by default. We recommend that you only enable the following features, which are the basic authentication features provided by most Laravel applications: ```php 'features' => [ diff --git a/frontend.md b/frontend.md index 638a5b468b5..839caf9dcc2 100644 --- a/frontend.md +++ b/frontend.md @@ -102,7 +102,7 @@ If you're new to Laravel, we recommend getting familiar with the basic usage of ### Starter Kits -If you would like to build your frontend using PHP and Livewire, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using [Blade](/docs/{{version}}/blade) and [Tailwind](https://tailwindcss.com) so that you can simply start building your next big idea. +If you would like to build your frontend using PHP and Livewire, you can leverage our [Livewire starter kit](/docs/{{version}}/starter-kits) to jump-start your application's development. ## Using Vue / React @@ -175,12 +175,12 @@ As you can see, Inertia allows you to leverage the full power of Vue or React wh #### Server-Side Rendering -If you're concerned about diving into Inertia because your application requires server-side rendering, don't worry. Inertia offers [server-side rendering support](https://inertiajs.com/server-side-rendering). And, when deploying your application via [Laravel Forge](https://forge.laravel.com), it's a breeze to ensure that Inertia's server-side rendering process is always running. +If you're concerned about diving into Inertia because your application requires server-side rendering, don't worry. Inertia offers [server-side rendering support](https://inertiajs.com/server-side-rendering). And, when deploying your application via [Laravel Cloud](https://cloud.laravel.com) or [Laravel Forge](https://forge.laravel.com), it's a breeze to ensure that Inertia's server-side rendering process is always running. ### Starter Kits -If you would like to build your frontend using Inertia and Vue / React, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits#breeze-and-inertia) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using Inertia, Vue / React, [Tailwind](https://tailwindcss.com), and [Vite](https://vitejs.dev) so that you can start building your next big idea. +If you would like to build your frontend using Inertia and Vue / React, you can leverage our [React or Vue application starter kits](/docs/{{version}}/starter-kits) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using Inertia, Vue / React, [Tailwind](https://tailwindcss.com), and [Vite](https://vitejs.dev) so that you can start building your next big idea. ## Bundling Assets @@ -189,7 +189,7 @@ Regardless of whether you choose to develop your frontend using Blade and Livewi By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. Vite provides lightning-fast build times and near instantaneous Hot Module Replacement (HMR) during local development. In all new Laravel applications, including those using our [starter kits](/docs/{{version}}/starter-kits), you will find a `vite.config.js` file that loads our light-weight Laravel Vite plugin that makes Vite a joy to use with Laravel applications. -The fastest way to get started with Laravel and Vite is by beginning your application's development using [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), our simplest starter kit that jump-starts your application by providing frontend and backend authentication scaffolding. +The fastest way to get started with Laravel and Vite is by beginning your application's development using [our application starter kits](/docs/{{version}}/starter-kits), which jump-starts your application by providing frontend and backend authentication scaffolding. > [!NOTE] > For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). diff --git a/horizon.md b/horizon.md index 6bec738bc63..3fa78e39a56 100644 --- a/horizon.md +++ b/horizon.md @@ -277,7 +277,7 @@ sudo apt-get install supervisor ``` > [!NOTE] -> 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. +> If configuring Supervisor yourself sounds overwhelming, consider using [Laravel Cloud](https://cloud.laravel.com), which can manage background processes for your Laravel applications. #### Supervisor Configuration diff --git a/installation.md b/installation.md index b6c1591328e..7acf69704d5 100644 --- a/installation.md +++ b/installation.md @@ -31,9 +31,6 @@ Laravel strives to provide an amazing developer experience while providing power Whether you are new to PHP web frameworks or have years of experience, Laravel is a framework that can grow with you. We'll help you take your first steps as a web developer or give you a boost as you take your expertise to the next level. We can't wait to see what you build. -> [!NOTE] -> New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. - ### Why Laravel? @@ -49,7 +46,7 @@ If you're a senior developer, Laravel gives you robust tools for [dependency inj Laravel is incredibly scalable. Thanks to the scaling-friendly nature of PHP and Laravel's built-in support for fast, distributed cache systems like Redis, horizontal scaling with Laravel is a breeze. In fact, Laravel applications have been easily scaled to handle hundreds of millions of requests per month. -Need extreme scaling? Platforms like [Laravel Vapor](https://vapor.laravel.com) allow you to run your Laravel application at nearly limitless scale on AWS's latest serverless technology. +Need extreme scaling? Platforms like [Laravel Cloud](https://cloud.laravel.com) allow you to run your Laravel application at nearly limitless scale. #### A Community Framework @@ -379,9 +376,6 @@ Now that you have created your Laravel application, you may be wondering what to How you want to use Laravel will also dictate the next steps on your journey. There are a variety of ways to use Laravel, and we'll explore two primary use cases for the framework below. -> [!NOTE] -> New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. - ### Laravel the Full Stack Framework @@ -400,6 +394,3 @@ If you are using Laravel as a full stack framework, we also strongly encourage y Laravel may also serve as an API backend to a JavaScript single-page application or mobile application. For example, you might use Laravel as an API backend for your [Next.js](https://nextjs.org) application. In this context, you may use Laravel to provide [authentication](/docs/{{version}}/sanctum) and data storage / retrieval for your application, while also taking advantage of Laravel's powerful services such as queues, emails, notifications, and more. If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [Laravel Sanctum](/docs/{{version}}/sanctum), and the [Eloquent ORM](/docs/{{version}}/eloquent). - -> [!NOTE] -> Need a head start scaffolding your Laravel backend and Next.js frontend? Laravel Breeze offers an [API stack](/docs/{{version}}/starter-kits#breeze-and-next) as well as a [Next.js frontend implementation](https://github.com/laravel/breeze-next) so you can get started in minutes. diff --git a/octane.md b/octane.md index 343eb97d931..3149aa86c92 100644 --- a/octane.md +++ b/octane.md @@ -262,7 +262,7 @@ By default, applications running via Octane generate links prefixed with `http:/ ### Serving Your Application via Nginx > [!NOTE] -> If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). +> If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Cloud](https://cloud.laravel.com), which offers fully-managed Laravel Octane support. In production environments, you should serve your Octane application behind a traditional web server such as Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. diff --git a/queues.md b/queues.md index aae4cba466a..49897fb118e 100644 --- a/queues.md +++ b/queues.md @@ -2004,7 +2004,7 @@ sudo apt-get install supervisor ``` > [!NOTE] -> 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. +> If configuring and managing Supervisor yourself sounds overwhelming, consider using [Laravel Cloud](https://cloud.laravel.com), which provides a fully-managed platform for running Laravel queue workers. #### Configuring Supervisor diff --git a/releases.md b/releases.md index 42a95163b13..d0c418ea029 100644 --- a/releases.md +++ b/releases.md @@ -48,5 +48,23 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe ## Laravel 12 -This version of Laravel is in active development. +Laravel 12 continues the improvements made in Laravel 11.x by updating upstream dependencies and introducing new starter kits for React, Vue, and Livewire, including the option of using [WorkOS AuthKit](https://authkit.com) for user authentication. The WorkOS variant of our starter kits offers social authentication, passkeys, and SSO support. + + +### Mimimal Breaking Changes + +Much of our focus during this release cycle has been minimizing breaking changes. Instead, we have dedicated ourselves to shipping continual quality of life improvements throughout the year that do not break existing applications. + +Therefore, the Laravel 12 release is a relatively minor "maintenance release" in order to upgrade existing dependencies. In light of this, most Laravel applications may upgrade to Laravel 12 without changing any application code. + + +### New Application Starter Kits + +Laravel 12 introduces new [application starter kits](/docs/{{version}}/starter-kits) for React, Vue, and Livewire. The React and Vue starter kits utilize Inertia 2, TypeScript, [shadcn/ui](https://ui.shadcn.com), and Tailwind, while the Livewire starter kits utilizes the Tailwind based [Flux UI](https://fluxui.dev) component library and Laravel Volt. + +The React, Vue, and Livewire starter kits all utilize Laravel's built-in authentication system to offer login, registration, password reset, email verification, and more. In addition, we are introducing a [WorkOS AuthKit](https://authkit.com) powered variant of each starter kit, offering social authentication, passkeys, and SSO support. WorkOS offers free authentication for applications up to 1 million monthly active users. + +With the introduction of our new application starter kits, Laravel Breeze and Laravel Jetstream will no longer receive additional updates. + +To get started with our new starter kits, check out the [starter kit documentation](/docs/{{version}}/starter-kits). diff --git a/scheduling.md b/scheduling.md index 3b63243b16a..9e038dbd250 100644 --- a/scheduling.md +++ b/scheduling.md @@ -437,7 +437,7 @@ Schedule::daily() 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: +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 managed platform such as [Laravel Cloud](https://cloud.laravel.com) which can manage the scheduled task execution for you: ```shell * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 diff --git a/starter-kits.md b/starter-kits.md index acff95f6f4f..9d5314872e1 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -1,132 +1,352 @@ # Starter Kits - [Introduction](#introduction) -- [Laravel Breeze](#laravel-breeze) - - [Installation](#laravel-breeze-installation) - - [Breeze and Blade](#breeze-and-blade) - - [Breeze and Livewire](#breeze-and-livewire) - - [Breeze and React / Vue](#breeze-and-inertia) - - [Breeze and Next.js / API](#breeze-and-next) -- [Laravel Jetstream](#laravel-jetstream) +- [Creating an Application Using a Starter Kit](#creating-an-application) +- [Available Starter Kits](#available-starter-kits) + - [React](#react) + - [Vue](#vue) + - [Livewire](#livewire) +- [Starter Kit Customization](#starter-kit-customization) + - [React](#react-customization) + - [Vue](#vue-customization) + - [Livewire](#livewire-customization) +- [WorkOS AuthKit Authentication](#workos) +- [Frequently Asked Questions](#faqs) ## 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. +To give you a head start building your new Laravel application, we are happy to offer [application starter kits](https://laravel.com/starter-kits). These starter kits give you a head start on building your next Laravel application, and include 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 + +## Creating an Application Using a Starter Kit -[Laravel Breeze](https://github.com/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. In addition, Breeze includes a simple "profile" page where the user may update their name, email address, and password. +To create a new Laravel application using one of our starter kits, you should first [install PHP and the Laravel CLI tool](/docs/{{version}}/installation#installing-php). If you already have PHP and Composer installed, you may install the Laravel installer CLI tool via Composer: -Laravel Breeze's default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Additionally, Breeze provides scaffolding options based on [Livewire](https://livewire.laravel.com) or [Inertia](https://inertiajs.com), with the choice of using Vue or React for the Inertia-based scaffolding. +```shell +composer global require laravel/installer +``` + +Then, create a new Laravel application using the Laravel installer CLI. The Laravel installer will prompt you to select your preferred starter kit: + +```shell +laravel new my-app +``` + +After creating your Laravel application, you only need to install its frontend dependencies via NPM and start the Laravel development server: + +```shell +cd my-app +npm install && npm run build +composer run dev +``` + +Once you have started the Laravel development server, your application will be accessible in your web browser at [http://localhost:8000](http://localhost:8000). + + +## Available Starter Kits + + +### React + +Our React starter kit provides a robust, modern starting point for building Laravel applications with a React frontend using [Inertia](https://inertiajs.com). + +Inertia allows you to build modern, single-page React applications using classic server-side routing and controllers. This lets you enjoy the frontend power of React combined with the incredible backend productivity of Laravel and lightning-fast Vite compilation. + +The React starter kit utilizes React 19, TypeScript, Tailwind, and the [shadcn/ui](https://ui.shadcn.com) component library. + + +### Vue + +Our Vue starter kit provides a great starting point for building Laravel applications with a Vue frontend using [Inertia](https://inertiajs.com). + +Inertia allows you to build modern, single-page Vue applications using classic server-side routing and controllers. This lets you enjoy the frontend power of Vue combined with the incredible backend productivity of Laravel and lightning-fast Vite compilation. + +The Vue starter kit utilizes the Vue Composition API, TypeScript, Tailwind, and the [shadcn-vue](https://www.shadcn-vue.com/) component library. + + +### Livewire - +Our Livewire starter kit provides the perfect starting point for building Laravel applications with a [Laravel Livewire](https://livewire.laravel.com) frontend. -#### Laravel Bootcamp +Livewire is a powerful way of building dynamic, reactive, frontend UIs using just PHP. It's a great fit for teams that primarily use Blade templates and are looking for a simpler alternative to JavaScript-driven SPA frameworks like React and Vue. -If you're new to Laravel, feel free to jump into the [Laravel Bootcamp](https://bootcamp.laravel.com). The Laravel Bootcamp will walk you through building your first Laravel application using Breeze. It's a great way to get a tour of everything that Laravel and Breeze have to offer. +The Livewire starter kit utilizes Laravel Volt, Tailwind, and the [Flux UI](https://fluxui.dev) component library. - -### Installation + +## Starter Kit Customization -First, you should [create a new Laravel application](/docs/{{version}}/installation). If you create your application using the [Laravel installer](/docs/{{version}}/installation#creating-a-laravel-project), you will be prompted to install Laravel Breeze during the installation process. Otherwise, you will need to follow the manual installation instructions below. + +### React -If you have already created a new Laravel application without a starter kit, you may manually install Laravel Breeze using Composer: +Our React starter kit is built with Inertia 2, React 19, Tailwind 4, and [shadcn/ui](https://ui.shadcn.com). As with all of our starter kits, all of the backend and frontend code exists within your application to allow for full customization. + +The majority of the frontend code is located in the `resources/js` directory. You are free to modify any of the code to customize the appearance and behavior of your application: + +```text +resources/js/ +├── components/ # Reusable React components +├── hooks/ # React hooks +├── layouts/ # Application layouts +├── lib/ # Utility functions and configuration +├── pages/ # Page components +└── types/ # TypeScript definitions +``` + +To publish additional shadcn components, first [find the component you want to publish](https://ui.shadcn.com). Then, publish the component using `npx`: ```shell -composer require laravel/breeze --dev +npx shadcn@latest add switch ``` -After Composer has installed the Laravel Breeze package, you should 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. +In this example, the command will publish the Switch component to `resources/js/components/ui/switch.tsx`. Once the component has been published, you can use it in any of your pages: -The `breeze:install` command will prompt you for your preferred frontend stack and testing framework: +```jsx +import { Switch } from "@/components/ui/switch" -```shell -php artisan breeze:install +const MyPage = () => { + return ( +
    + +
    + ); +}; -php artisan migrate -npm install -npm run dev +export default MyPage; ``` - -### Breeze and Blade + +#### Available Layouts -The default Breeze "stack" is the Blade stack, which utilizes simple [Blade templates](/docs/{{version}}/blade) to render your application's frontend. The Blade stack may be installed by invoking the `breeze:install` command with no other additional arguments and selecting the Blade frontend stack. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: +The React starter kit includes two different primary layouts for you choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is imported at the top of your application's `resources/js/layouts/app-layout.tsx` file: -```shell -php artisan breeze:install +```js +import AppLayoutTemplate from '@/layouts/app/app-sidebar-layout'; // [tl! remove] +import AppLayoutTemplate from '@/layouts/app/app-header-layout'; // [tl! add] +``` + + +#### Sidebar Variants + +The sidebar layout includes three different variants: the default sidebar variant, the "inset" variant, and the "floating" variant. You may choose the variant you like best by modifying the `resources/js/components/app-sidebar.tsx` component: -php artisan migrate -npm install -npm run dev +```text + [tl! remove] + [tl! add] ``` -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. + +#### Authentication Page Layout Variants -> [!NOTE] -> To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). +The authentication pages included with the React starter kit, such as the login page and registration page, also offer three different layout variants: "simple", "card", and "split". - -### Breeze and Livewire +To change your authentication layout, modify the layout that is imported at the top of your application's `resources/js/layouts/auth-layout.tsx` file: + +```js +import AuthLayoutTemplate from '@/layouts/auth/auth-simple-layout'; // [tl! remove] +import AuthLayoutTemplate from '@/layouts/auth/auth-split-layout'; // [tl! add] +``` -Laravel Breeze also offers [Livewire](https://livewire.laravel.com) scaffolding. Livewire is a powerful way of building dynamic, reactive, front-end UIs using just PHP. + +### Vue -Livewire is a great fit for teams that primarily use Blade templates and are looking for a simpler alternative to JavaScript-driven SPA frameworks like Vue and React. +Our Vue starter kit is built with Inertia 2, Vue 3 Composition API, Tailwind, and [shadcn-vue](https://www.shadcn-vue.com/). As with all of our starter kits, all of the backend and frontend code exists within your application to allow for full customization. -To use the Livewire stack, you may select the Livewire frontend stack when executing the `breeze:install` Artisan command. After Breeze's scaffolding is installed, you should run your database migrations: +The majority of the frontend code is located in the `resources/js` directory. You are free to modify any of the code to customize the appearance and behavior of your application: + +```text +resources/js/ +├── components/ # Reusable Vue components +├── composables/ # Vue composables / hooks +├── layouts/ # Application layouts +├── lib/ # Utility functions and configuration +├── pages/ # Page components +└── types/ # TypeScript definitions +``` + +To publish additional shadcn-vue components, first [find the component you want to publish](https://www.shadcn-vue.com). Then, publish the component using `npx`: ```shell -php artisan breeze:install +npx shadcn-vue@latest add switch +``` + +In this example, the command will publish the Switch component to `resources/js/components/ui/Switch.vue`. Once the component has been published, you can use it in any of your pages: -php artisan migrate +```vue + + + ``` - -### Breeze and React / Vue + +#### Available Layouts -Laravel Breeze also offers React and Vue scaffolding via an [Inertia](https://inertiajs.com) frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers. +The Vue starter kit includes two different primary layouts for you choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is imported at the top of your application's `resources/js/layouts/AppLayout.vue` file: -Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel and lightning-fast [Vite](https://vitejs.dev) compilation. To use an Inertia stack, you may select the Vue or React frontend stacks when executing the `breeze:install` Artisan command. +```js +import AppLayout from '@/layouts/app/AppSidebarLayout.vue'; // [tl! remove] +import AppLayout from '@/layouts/app/AppHeaderLayout.vue'; // [tl! add] +``` -When selecting the Vue or React frontend stack, the Breeze installer will also prompt you to determine if you would like [Inertia SSR](https://inertiajs.com/server-side-rendering) or TypeScript support. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: + +#### Sidebar Variants -```shell -php artisan breeze:install +The sidebar layout includes three different variants: the default sidebar variant, the "inset" variant, and the "floating" variant. You may choose the variant you like best by modifying the `resources/js/components/AppSidebar.vue` component: -php artisan migrate -npm install -npm run dev +```text + [tl! remove] + [tl! add] ``` -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. + +#### Authentication Page Layout Variants - -### Breeze and Next.js / API +The authentication pages included with the Vue starter kit, such as the login page and registration page, also offer three different layout variants: "simple", "card", and "split". -Laravel Breeze can also scaffold an authentication API that is ready to authenticate modern JavaScript applications such as those powered by [Next](https://nextjs.org), [Nuxt](https://nuxt.com), and others. To get started, select the API stack as your desired stack when executing the `breeze:install` Artisan command: +To change your authentication layout, modify the layout that is imported at the top of your application's `resources/js/layouts/AuthLayout.vue` file: -```shell -php artisan breeze:install +```js +import AuthLayout from '@/layouts/auth/AuthSimpleLayout.vue'; // [tl! remove] +import AuthLayout from '@/layouts/auth/AuthSplitLayout.vue'; // [tl! add] +``` -php artisan migrate + +### Livewire + +Our Livewire starter kit is built with Livewire 3, Laravel Volt, Tailwind, and [Flux UI](https://fluxui.dev/). As with all of our starter kits, all of the backend and frontend code exists within your application to allow for full customization. + +The majority of the frontend code is located in the `resources/views` directory. You are free to modify any of the code to customize the appearance and behavior of your application: + +```text +resources/views +├── components # Reusable Livewire components +├── flux # Customized Flux components +├── livewire # Livewire pages +├── partials # Reusable Blade partials +├── dashboard.blade.php # Authenticated user dashboard +├── welcome.blade.php # Guest user welcome page ``` -During installation, Breeze will add a `FRONTEND_URL` environment variable to your application's `.env` file. This URL should be the URL of your JavaScript application. This will typically be `http://localhost:3000` during local development. In addition, you should ensure that your `APP_URL` is set to `http://localhost:8000`, which is the default URL used by the `serve` Artisan command. + +#### Available Layouts + +The Livewire starter kit includes two different primary layouts for you choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is used by your application's `resources/views/components/layouts/app.blade.php` file. In addition, you should add the `container` attribute to the main Flux component: + +```blade + + + {{ $slot }} + + +``` + + +#### Authentication Page Layout Variants + +The authentication pages included with the Livewire starter kit, such as the login page and registration page, also offer three different layout variants: "simple", "card", and "split". + +To change your authentication layout, modify the layout that is used by your application's `resources/views/components/layouts/auth.blade.php` file: + +```blade + + {{ $slot }} + +``` - -#### Next.js Reference Implementation + +## WorkOS AuthKit Authentication + +By default, the React, Vue, and Livewire starter kits all utilize Laravel's built-in authentication system to offer login, registration, password reset, email verification, and more. In addition, we also offer a [WorkOS AuthKit](https://authkit.com) powered variant of each starter kit that offers: + +
    + +- Social authentication (Google, Microsoft, GitHub, and Apple) +- Passkey authentication +- Email based "Magic Auth" +- SSO + +
    + +Using WorkOS as your authentication provider [requires a WorkOS account](https://workos.com). WorkOS offers free authentication for applications up to 1 million monthly active users. + +To use WorkOS AuthKit as your application's authentication provider, select the WorkOS option when creating your new starter kit powered application via `laravel new`. + +### Configuring Your WorkOS Starter Kit + +After creating a new application using a WorkOS powered starter kit, you should set the `WORKOS_CLIENT_ID`, `WORKOS_API_KEY`, and `WORKOS_REDIRECT_URL` environment variables in your application's `.env` file. These variables should match the values provided to you in the WorkOS dashboard for your application: + +```ini +WORKOS_CLIENT_ID=your-client-id +WORKOS_API_KEY=your-api-key +WORKOS_REDIRECT_URL="${APP_URL}/authenticate" +``` -Finally, you are ready to pair this backend with the frontend of your choice. A Next reference implementation of the Breeze frontend is [available on GitHub](https://github.com/laravel/breeze-next). This frontend is maintained by Laravel and contains the same user interface as the traditional Blade and Inertia stacks provided by Breeze. + +#### Configuring AuthKit Authentication Methods - -## Laravel Jetstream +When using a WorkOS powered starter kit, we recommend that you disable "Email + Password" authentication within your application's WorkOS AuthKit configuration settings, allowing users to only authenticate via social authentication providers, passkeys, "Magic Auth", and SSO. This allows your application to totally avoid handling user passwords. -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.** + +#### Configuring AuthKit Session Timeouts + +In addition, we recommend that you configure your WorkOS AuthKit session inactivity timeout to match your Laravel application's configured session timeout threshold, which is typically two hours. + + +### Frequently Asked Questions + + +#### How do I upgrade? + +Every starter kit gives you a solid starting point for your next application. With full ownership of the code, you can tweak, customize, and build your application exactly as you envision. However, there is no need to update the starter kit itself. + + +#### How do I enable email verification? + +Email verification can be added by by uncommenting the `MustVerifyEmail` import in your `App/Models/User.php` model and ensuring the model implements the `MustVerifyEmail` interface: + +```php +group(function () { + Route::get('dashboard', function () { + return Inertia::render('dashboard'); + })->name('dashboard'); +}); +``` + +> [!NOTE] +> Email verification is not required when using the [WorkOS](#workos) variant of the starter kits. + + +#### How do I modify the default email template? + +You may want to customize the default email template to better align with your application's branding. To modify this template, you should publish the email views to your application with the following command: + +``` +php artisan vendor:publish --tag=laravel-mail +``` -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://livewire.laravel.com) or [Inertia](https://inertiajs.com) driven frontend scaffolding. +This will generate several files in `resources/views/vendor/mail`. You can modify any of these files as well as the `resources/views/vendor/mail/themes/default.css` file to change the look and appearance of the default email template. -Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com). diff --git a/structure.md b/structure.md index 669241f1fdd..e1b62d043f0 100644 --- a/structure.md +++ b/structure.md @@ -32,9 +32,6 @@ The default Laravel application structure is intended to provide a great starting point for both large and small applications. But you are free to organize your application however you like. Laravel imposes almost no restrictions on where any given class is located - as long as Composer can autoload the class. -> [!NOTE] -> New to Laravel? Check out the [Laravel Bootcamp](https://bootcamp.laravel.com) for a hands-on tour of the framework while we walk you through building your first Laravel application. - ## The Root Directory diff --git a/upgrade.md b/upgrade.md index 9c5d64fa7ce..90192fa7015 100644 --- a/upgrade.md +++ b/upgrade.md @@ -7,16 +7,7 @@
    -- TBD - -
    - - -## Medium Impact Changes - -
    - -- TBD +- [Updating Dependencies](#updating-dependencies)
    @@ -25,15 +16,17 @@
    -- TBD +- [Carbon 3](#carbon-3) +- [Concurrency Result Index Mapping](#concurrency-result-index-mapping) +- [Image Validation Now Excludes SVGs](#image-validation) +- [Nested Array Request Merging](#nested-array-request-merging)
    ## Upgrading To 12.0 From 11.x - -#### Estimated Upgrade Time: ?? Minutes +#### Estimated Upgrade Time: 5 Minutes > [!NOTE] > We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. @@ -51,7 +44,66 @@ You should update the following dependencies in your application's `composer.jso + +#### Carbon 3 + +**Likelihood Of Impact: Low** + +Support for [Carbon 2.x](https://carbon.nesbot.com/docs/) has been removed. All Laravel 12 applications now require Carbon 3.x. + + +### Concurrency + + +#### Concurrency Result Index Mapping + +**Likelihood Of Impact: Low** + +When invoking the `Concurrency::run` method with an associative array, the results of the concurrent operations are now returned with their associated keys: + +```php +$result = Concurrency::run([ + 'task-1' => fn () => 1 + 1, + 'task-2' => fn () => 2 + 2, +]); + +// ['task-1' => 2, 'task-2' => 4] +``` + + +### Requests + + +#### Nested Array Request Merging + +**Likelihood Of Impact: Low** + +The `$request->mergeIfMissing()` method now allows merging nested array data using "dot" notation. If you were previously relying on this method to create a top-level array key containing the "dot" notation version of the key, you may need to adjust your application to account for this new behavior: + +```php +$request->mergeIfMissing([ + 'user.last_name' => 'Otwell', +]); +``` + + +### Validation + + +#### Image Validation Now Excludes SVGs + +The `image` validation rule no longer allows SVG images by default. If you would like to allow SVGs when using the `image` rule, you must explicitly allow them: + +```php +use Illuminate\Validation\Rules\File; + +'photo' => 'required|image:allow_svg' + +// Or... +'photo' => ['required', File::image(allowSvg: true)], +``` + ### Miscellaneous -We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/11.x...master) and choose which updates are important to you. +We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [GitHub comparison tool](https://github.com/laravel/laravel/compare/11.x...12.x) and choose which updates are important to you. diff --git a/views.md b/views.md index cc55a972431..6c03fa37bb2 100644 --- a/views.md +++ b/views.md @@ -45,7 +45,7 @@ Route::get('/', function () { Instead of writing their frontend templates in PHP via Blade, many developers have begun to prefer to write their templates using React or Vue. Laravel makes this painless thanks to [Inertia](https://inertiajs.com/), a library that makes it a cinch to tie your React / Vue frontend to your Laravel backend without the typical complexities of building an SPA. -Our Breeze and Jetstream [starter kits](/docs/{{version}}/starter-kits) give you a great starting point for your next Laravel application powered by Inertia. In addition, the [Laravel Bootcamp](https://bootcamp.laravel.com) provides a full demonstration of building a Laravel application powered by Inertia, including examples in Vue and React. +Our [React and Vue application starter kits](/docs/{{version}}/starter-kits) give you a great starting point for your next Laravel application powered by Inertia. ## Creating and Rendering Views diff --git a/vite.md b/vite.md index 7bdd9b9e10f..7ab83151fd0 100644 --- a/vite.md +++ b/vite.md @@ -347,7 +347,7 @@ export default defineConfig({ ``` > [!NOTE] -> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Vue, and Vite. +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration.These starter kits offer the fastest way to get started with Laravel, Vue, and Vite. ### React @@ -385,7 +385,7 @@ You will also need to include the additional `@viteReactRefresh` Blade directive The `@viteReactRefresh` directive must be called before the `@vite` directive. > [!NOTE] -> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, React, and Vite. +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration.These starter kits offer the fastest way to get started with Laravel, React, and Vite. ### Inertia @@ -410,7 +410,7 @@ createInertiaApp({ If you are using Vite's code splitting feature with Inertia, we recommend configuring [asset prefetching](#asset-prefetching). > [!NOTE] -> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration.These starter kits offer the fastest way to get started with Laravel, Inertia, and Vite. ### URL Processing @@ -761,7 +761,7 @@ php artisan inertia:start-ssr ``` > [!NOTE] -> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration.These starter kits offer the fastest way to get started with Laravel, Inertia SSR, and Vite. ## Script and Style Tag Attributes From 271e9a15ce8c8a1f61f5c49a8aa51bfb1d834553 Mon Sep 17 00:00:00 2001 From: Jordan Dalton Date: Sun, 23 Feb 2025 08:19:39 -0600 Subject: [PATCH 2027/2609] Update container.md : Defining Custom Attributes : Improved Code Sample (#10194) * Update container.md Added classes to namespace so documentation could be copied/pasted into IDE. * Update container.md --------- Co-authored-by: Taylor Otwell --- container.md | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/container.md b/container.md index 9e809664bf8..0073f52d78c 100644 --- a/container.md +++ b/container.md @@ -312,34 +312,38 @@ Route::get('/user', function (#[CurrentUser] User $user) { You can create your own contextual attributes by implementing the `Illuminate\Contracts\Container\ContextualAttribute` contract. The container will call your attribute's `resolve` method, which should resolve the value that should be injected into the class utilizing the attribute. In the example below, we will re-implement Laravel's built-in `Config` attribute: - make('config')->get($attribute->key, $attribute->default); - } + /** + * Resolve the configuration value. + * + * @param self $attribute + * @param \Illuminate\Contracts\Container\Container $container + * @return mixed + */ + public static function resolve(self $attribute, Container $container) + { + return $container->make('config')->get($attribute->key, $attribute->default); } +} +``` ### Binding Primitives From 82f5b3853ce035bab92e0e37f549243813edbda2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 23 Feb 2025 08:47:52 -0600 Subject: [PATCH 2028/2609] update trial docs --- cashier-paddle.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 2400d1abfb1..e3aa5aba613 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -748,7 +748,7 @@ To create a subscription, first retrieve an instance of your billable model from use Illuminate\Http\Request; Route::get('/user/subscribe', function (Request $request) { - $checkout = $request->user()->subscribe($premium = 12345, 'default') + $checkout = $request->user()->subscribe($premium = 'pri_123', 'default') ->returnTo(route('home')); return view('billing', ['checkout' => $checkout]); @@ -760,7 +760,7 @@ The first argument given to the `subscribe` method is the specific price the use You may also provide an array of custom metadata regarding the subscription using the `customData` method: ```php -$checkout = $request->user()->subscribe($premium = 12345, 'default') +$checkout = $request->user()->subscribe($premium = 'pri_123', 'default') ->customData(['key' => 'value']) ->returnTo(route('home')); ``` @@ -1235,16 +1235,12 @@ When your application receives the `subscription_created` event, Cashier will se > [!WARNING] > If the customer's subscription is not canceled 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 are equivalent: +You may determine if the user is within their trial period using either the `onTrial` method of the user instance: ```php if ($user->onTrial()) { // ... } - -if ($user->subscription()->onTrial()) { - // ... -} ``` To determine if an existing trial has expired, you may use the `hasExpiredTrial` methods: @@ -1253,10 +1249,6 @@ To determine if an existing trial has expired, you may use the `hasExpiredTrial` if ($user->hasExpiredTrial()) { // ... } - -if ($user->subscription()->hasExpiredTrial()) { - // ... -} ``` To determine if a user is on trial for a specific subscription type, you may provide the type to the `onTrial` or `hasExpiredTrial` methods: From 2935587d0d8bc7b7203072856b3dca6bfb4bda01 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 23 Feb 2025 10:26:01 -0600 Subject: [PATCH 2029/2609] update react example --- frontend.md | 52 +++++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/frontend.md b/frontend.md index 839caf9dcc2..a0e9f48fc34 100644 --- a/frontend.md +++ b/frontend.md @@ -5,7 +5,7 @@ - [PHP and Blade](#php-and-blade) - [Livewire](#livewire) - [Starter Kits](#php-starter-kits) -- [Using Vue / React](#using-vue-react) +- [Using React or Vue](#using-react-or-vue) - [Inertia](#inertia) - [Starter Kits](#inertia-starter-kits) - [Bundling Assets](#bundling-assets) @@ -104,19 +104,19 @@ If you're new to Laravel, we recommend getting familiar with the basic usage of If you would like to build your frontend using PHP and Livewire, you can leverage our [Livewire starter kit](/docs/{{version}}/starter-kits) to jump-start your application's development. - -## Using Vue / React + +## Using React or Vue -Although it's possible to build modern frontends using Laravel and Livewire, many developers still prefer to leverage the power of a JavaScript framework like Vue or React. This allows developers to take advantage of the rich ecosystem of JavaScript packages and tools available via NPM. +Although it's possible to build modern frontends using Laravel and Livewire, many developers still prefer to leverage the power of a JavaScript framework like React or Vue. This allows developers to take advantage of the rich ecosystem of JavaScript packages and tools available via NPM. -However, without additional tooling, pairing Laravel with Vue or React would leave us needing to solve a variety of complicated problems such as client-side routing, data hydration, and authentication. Client-side routing is often simplified by using opinionated Vue / React frameworks such as [Nuxt](https://nuxt.com/) and [Next](https://nextjs.org/); however, data hydration and authentication remain complicated and cumbersome problems to solve when pairing a backend framework like Laravel with these frontend frameworks. +However, without additional tooling, pairing Laravel with React or Vue would leave us needing to solve a variety of complicated problems such as client-side routing, data hydration, and authentication. Client-side routing is often simplified by using opinionated React / Vue frameworks such as [Next](https://nextjs.org/) and [Nuxt](https://nuxt.com/); however, data hydration and authentication remain complicated and cumbersome problems to solve when pairing a backend framework like Laravel with these frontend frameworks. In addition, developers are left maintaining two separate code repositories, often needing to coordinate maintenance, releases, and deployments across both repositories. While these problems are not insurmountable, we don't believe it's a productive or enjoyable way to develop applications. ### Inertia -Thankfully, Laravel offers the best of both worlds. [Inertia](https://inertiajs.com) bridges the gap between your Laravel application and your modern Vue or React frontend, allowing you to build full-fledged, modern frontends using Vue or React while leveraging Laravel routes and controllers for routing, data hydration, and authentication — all within a single code repository. With this approach, you can enjoy the full power of both Laravel and Vue / React without crippling the capabilities of either tool. +Thankfully, Laravel offers the best of both worlds. [Inertia](https://inertiajs.com) bridges the gap between your Laravel application and your modern React or Vue frontend, allowing you to build full-fledged, modern frontends using React or Vue while leveraging Laravel routes and controllers for routing, data hydration, and authentication — all within a single code repository. With this approach, you can enjoy the full power of both Laravel and React / Vue without crippling the capabilities of either tool. After installing Inertia into your Laravel application, you will write routes and controllers like normal. However, instead of returning a Blade template from your controller, you will return an Inertia page: @@ -137,41 +137,31 @@ class UserController extends Controller */ public function show(string $id): Response { - return Inertia::render('Users/Profile', [ + return Inertia::render('users/show', [ 'user' => User::findOrFail($id) ]); } } ``` -An Inertia page corresponds to a Vue or React component, typically stored within the `resources/js/Pages` directory of your application. The data given to the page via the `Inertia::render` method will be used to hydrate the "props" of the page component: +An Inertia page corresponds to a React or Vue component, typically stored within the `resources/js/pages` directory of your application. The data given to the page via the `Inertia::render` method will be used to hydrate the "props" of the page component: -```vue - - - +export default function Show({ user }) { + return ( + + +

    Welcome

    +

    Hello {user.name}, welcome to Laravel and Inertia.

    +
    + ) +} ``` -As you can see, Inertia allows you to leverage the full power of Vue or React when building your frontend, while providing a light-weight bridge between your Laravel powered backend and your JavaScript powered frontend. +As you can see, Inertia allows you to leverage the full power of React or Vue when building your frontend, while providing a light-weight bridge between your Laravel powered backend and your JavaScript powered frontend. #### Server-Side Rendering From 72aa9f5995a3b3759ab807545ccfdf30703a889f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 23 Feb 2025 10:26:47 -0600 Subject: [PATCH 2030/2609] shorten wording --- frontend.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend.md b/frontend.md index a0e9f48fc34..104a6698971 100644 --- a/frontend.md +++ b/frontend.md @@ -155,7 +155,7 @@ export default function Show({ user }) {

    Welcome

    -

    Hello {user.name}, welcome to Laravel and Inertia.

    +

    Hello {user.name}, welcome to Inertia.

    ) } From 86136d43af9ebdaacfae6cbd6079427dea6e0974 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 23 Feb 2025 15:19:01 -0600 Subject: [PATCH 2031/2609] installer upgrade instructions --- upgrade.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/upgrade.md b/upgrade.md index 90192fa7015..d026a4e096b 100644 --- a/upgrade.md +++ b/upgrade.md @@ -8,6 +8,7 @@
    - [Updating Dependencies](#updating-dependencies) +- [Updating the Laravel Installer](#updating-the-laravel-installer)
    @@ -51,6 +52,32 @@ You should update the following dependencies in your application's `composer.jso Support for [Carbon 2.x](https://carbon.nesbot.com/docs/) has been removed. All Laravel 12 applications now require Carbon 3.x. + +### Updating the Laravel Installer + +If you are using the Laravel installer CLI tool to create new Laravel applications, you should update your installer installation to be compatible with Laravel 12.x and the [new Laravel starter kits](https://laravel.com/starter-kits). If you installed the Laravel installer via `composer global require`, you may update the installer using `composer global update`: + +```shell +composer global update laravel/installer +``` + +If you originally installed PHP and Laravel via `php.new`, you may simply re-run the `php.new` installation commands for your operating system to install the latest version of PHP and the Laravel installer: + +```shell tab=macOS +/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.4)" +``` + +```shell tab=Windows PowerShell +# Run as administrator... +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://php.new/install/windows/8.4')) +``` + +```shell tab=Linux +/bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.4)" +``` + +Or, if you are using [Laravel Herd's](https://herd.laravel.com) bundled copy of the Laravel installer, you should update your Herd installation to the latest release. + ### Concurrency From ba26bf537650a1d758f14140f6372cebde5b5535 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 23 Feb 2025 18:50:00 -0600 Subject: [PATCH 2032/2609] update api docs --- documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation.md b/documentation.md index a76e612a10d..3fca04eb6a4 100644 --- a/documentation.md +++ b/documentation.md @@ -104,4 +104,4 @@ - [Socialite](/docs/{{version}}/socialite) - [Telescope](/docs/{{version}}/telescope) - [Valet](/docs/{{version}}/valet) -- [API Documentation](/api/11.x) +- [API Documentation](https://api.laravel.com/docs/12.x) From e0bfeee7ba932f41751e4053aa2fe56824562281 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 23 Feb 2025 18:50:19 -0600 Subject: [PATCH 2033/2609] update api docs --- documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation.md b/documentation.md index c0eecca09eb..8e867dc4ab5 100644 --- a/documentation.md +++ b/documentation.md @@ -106,4 +106,4 @@ - [Socialite](/docs/{{version}}/socialite) - [Telescope](/docs/{{version}}/telescope) - [Valet](/docs/{{version}}/valet) -- [API Documentation](/api/11.x) +- [API Documentation](https://api.laravel.com/docs/11.x) From f15d17fcea5caaf8b0f1626703649645b21af4ec Mon Sep 17 00:00:00 2001 From: Mehdi <9340937+meduzen@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:50:40 +0100 Subject: [PATCH 2034/2609] Add link to migrate Carbon from version 2 to 3 (#10198) --- upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.md b/upgrade.md index d026a4e096b..888cb60dd18 100644 --- a/upgrade.md +++ b/upgrade.md @@ -50,7 +50,7 @@ You should update the following dependencies in your application's `composer.jso **Likelihood Of Impact: Low** -Support for [Carbon 2.x](https://carbon.nesbot.com/docs/) has been removed. All Laravel 12 applications now require Carbon 3.x. +Support for [Carbon 2.x](https://carbon.nesbot.com/docs/) has been removed. All Laravel 12 applications now require [Carbon 3.x](https://carbon.nesbot.com/docs/#api-carbon-3). ### Updating the Laravel Installer From 99902600577a01d3ba99d4961158ec6ce9e92f7d Mon Sep 17 00:00:00 2001 From: Chris <31969757+ChrisvanChip@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:51:07 +0100 Subject: [PATCH 2035/2609] Minor fluency fix (#10197) --- errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors.md b/errors.md index 15c743d1fc6..e35606f2775 100644 --- a/errors.md +++ b/errors.md @@ -32,7 +32,7 @@ During local development, you should set the `APP_DEBUG` environment variable to ### Reporting Exceptions -In Laravel, exception reporting is used to log exceptions or send them to an external service [Sentry](https://github.com/getsentry/sentry-laravel) or [Flare](https://flareapp.io). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. +In Laravel, exception reporting is used to log exceptions or send them to an external service like [Sentry](https://github.com/getsentry/sentry-laravel) or [Flare](https://flareapp.io). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. If you need to report different types of exceptions in different ways, you may use the `report` exception method in your application's `bootstrap/app.php` to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will determine what type of exception the closure reports by examining the type-hint of the closure: From 48d5f2a9d9bd4a514ed007ddba8dd91783cc169c Mon Sep 17 00:00:00 2001 From: Daniel Schmelz Date: Mon, 24 Feb 2025 16:36:23 +0100 Subject: [PATCH 2036/2609] Fix typos (#10200) --- starter-kits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index 9d5314872e1..bb35a29b557 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -120,7 +120,7 @@ export default MyPage; #### Available Layouts -The React starter kit includes two different primary layouts for you choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is imported at the top of your application's `resources/js/layouts/app-layout.tsx` file: +The React starter kit includes two different primary layouts for you to choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is imported at the top of your application's `resources/js/layouts/app-layout.tsx` file: ```js import AppLayoutTemplate from '@/layouts/app/app-sidebar-layout'; // [tl! remove] @@ -310,7 +310,7 @@ Every starter kit gives you a solid starting point for your next application. Wi #### How do I enable email verification? -Email verification can be added by by uncommenting the `MustVerifyEmail` import in your `App/Models/User.php` model and ensuring the model implements the `MustVerifyEmail` interface: +Email verification can be added by uncommenting the `MustVerifyEmail` import in your `App/Models/User.php` model and ensuring the model implements the `MustVerifyEmail` interface: ```php Date: Mon, 24 Feb 2025 19:15:57 +0330 Subject: [PATCH 2037/2609] [12.x] Upgrade guide entry for Multi-schema Database Inspecting (#10199) * multi-schema database inspecting * better examples * formatting * upgrade guide --------- Co-authored-by: Taylor Otwell --- upgrade.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/upgrade.md b/upgrade.md index 888cb60dd18..9622366bce8 100644 --- a/upgrade.md +++ b/upgrade.md @@ -20,6 +20,7 @@ - [Carbon 3](#carbon-3) - [Concurrency Result Index Mapping](#concurrency-result-index-mapping) - [Image Validation Now Excludes SVGs](#image-validation) +- [Multi-Schema Database Inspecting](#multi-schema-database-inspecting) - [Nested Array Request Merging](#nested-array-request-merging) @@ -97,6 +98,42 @@ $result = Concurrency::run([ // ['task-1' => 2, 'task-2' => 4] ``` + +### Database + + +#### Multi-Schema Database Inspecting + +**Likelihood Of Impact: Low** + +The `Schema::getTables()`, `Schema::getViews()`, and `Schema::getTypes()` methods now include the results from all schemas by default. You may pass the `schema` argument to retrieve the result for the given schema only: + +```php +// All tables on all schemas... +$tables = Schema::getTables(); + +// All tables on the 'main' schema... +$table = Schema::getTables(schema: 'main'); + +// All tables on the 'main' and 'blog' schemas... +$table = Schema::getTables(schema: ['main', 'blog']); +``` + +The `Schema::getTableListing()` method now returns schema-qualified table names by default. You may pass the `schemaQualified` argument to change the behavior as desired: + +```php +$tables = Schema::getTableListing(); +// ['main.migrations', 'main.users', 'blog.posts'] + +$table = Schema::getTableListing(schema: 'main'); +// ['main.migrations', 'main.users'] + +$table = Schema::getTableListing(schema: 'main', schemaQualified: false); +// ['migrations', 'users'] +``` + +The `db:table` and `db:show` commands now output the results of all schemas on MySQL, MariaDB, and SQLite, just like PostgreSQL and SQL Server. + ### Requests From fcaed388b0f1d78637d210e3e4b7d42a964ee94c Mon Sep 17 00:00:00 2001 From: Pj Date: Tue, 25 Feb 2025 00:03:02 +0800 Subject: [PATCH 2038/2609] Fix typo (#10201) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index d0c418ea029..bdbaf1c820d 100644 --- a/releases.md +++ b/releases.md @@ -51,7 +51,7 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe Laravel 12 continues the improvements made in Laravel 11.x by updating upstream dependencies and introducing new starter kits for React, Vue, and Livewire, including the option of using [WorkOS AuthKit](https://authkit.com) for user authentication. The WorkOS variant of our starter kits offers social authentication, passkeys, and SSO support. -### Mimimal Breaking Changes +### Minimal Breaking Changes Much of our focus during this release cycle has been minimizing breaking changes. Instead, we have dedicated ourselves to shipping continual quality of life improvements throughout the year that do not break existing applications. From d0ecae6419ec01a7727339be6ad8804b7e5f5a26 Mon Sep 17 00:00:00 2001 From: AkrAm Date: Tue, 25 Feb 2025 01:40:48 +0600 Subject: [PATCH 2039/2609] [12.x] Added Laravel official Extension support (#10202) * [12.x] Added Laravel official Extension support * Update installation.md --------- Co-authored-by: Taylor Otwell --- installation.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/installation.md b/installation.md index 7acf69704d5..9c1c484c5d7 100644 --- a/installation.md +++ b/installation.md @@ -105,7 +105,7 @@ composer run dev Once you have started the development server, your application will be accessible in your web browser at [http://localhost:8000](http://localhost:8000). Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). -> [!NOTE] +> [!NOTE] > If you would like a head start when developing your Laravel application, 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. @@ -122,7 +122,7 @@ Since many of Laravel's configuration option values may vary depending on whethe Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would be exposed. -> [!NOTE] +> [!NOTE] > For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). @@ -149,7 +149,7 @@ If you choose to use a database other than SQLite, you will need to create the d php artisan migrate ``` -> [!NOTE] +> [!NOTE] > If you are developing on macOS or Windows and need to install MySQL, PostgreSQL, or Redis locally, consider using [Herd Pro](https://herd.laravel.com/#plans). @@ -164,7 +164,7 @@ Laravel should always be served out of the root of the "web directory" configure Once you install Herd, you're ready to start developing with Laravel. Herd includes command line tools for `php`, `composer`, `laravel`, `expose`, `node`, `npm`, and `nvm`. -> [!NOTE] +> [!NOTE] > [Herd Pro](https://herd.laravel.com/#plans) augments Herd with additional powerful features, such as the ability to create and manage local MySQL, Postgres, and Redis databases, as well as local mail viewing and log monitoring. @@ -216,7 +216,7 @@ Docker is a tool for running applications and services in small, light-weight "c Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. -> [!NOTE] +> [!NOTE] > Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. @@ -248,7 +248,7 @@ Once the application's Docker containers have started, you should run your appli Finally, you can access the application in your web browser at: http://localhost. -> [!NOTE] +> [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). @@ -256,7 +256,7 @@ Finally, you can access the application in your web browser at: http://localhost Before we create a new Laravel application on your Windows machine, make sure to install [Docker Desktop](https://www.docker.com/products/docker-desktop). Next, you should ensure that Windows Subsystem for Linux 2 (WSL2) is installed and enabled. WSL allows you to run Linux binary executables natively on Windows 10. Information on how to install and enable WSL2 can be found within Microsoft's [developer environment documentation](https://docs.microsoft.com/en-us/windows/wsl/install-win10). -> [!NOTE] +> [!NOTE] > After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). Next, you are ready to create your first Laravel application. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel application. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: @@ -285,7 +285,7 @@ Once the application's Docker containers have started, you should run your appli Finally, you can access the application in your web browser at: http://localhost. -> [!NOTE] +> [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). #### Developing Within WSL2 @@ -331,7 +331,7 @@ Once the application's Docker containers have started, you should run your appli Finally, you can access the application in your web browser at: http://localhost. -> [!NOTE] +> [!NOTE] > To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). @@ -358,6 +358,8 @@ You are free to use any code editor you wish when developing Laravel application In addition, the community maintained [Laravel Idea](https://laravel-idea.com/) PhpStorm plugin offers a variety of helpful IDE augmentations, including code generation, Eloquent syntax completion, validation rule completion, and more. +If you develop in [Visual Studio Code (VS Code)](https://code.visualstudio.com), the official [Laravel VS Code Extension](https://marketplace.visualstudio.com/items?itemName=laravel.vscode-laravel) is now available. This extension brings Laravel-specific tools directly into your VS Code environment, enhancing productivity. + ## Next Steps @@ -385,7 +387,7 @@ If this is how you plan to use Laravel, you may want to check out our documentat If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Vite](/docs/{{version}}/vite). -> [!NOTE] +> [!NOTE] > If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). From c47fca37d2d517e98f11d87a94bbe103c90c6812 Mon Sep 17 00:00:00 2001 From: Monayem Islam Date: Tue, 25 Feb 2025 19:51:27 +0600 Subject: [PATCH 2040/2609] Fix anchor link for assertServiceUnavailable and line break in Artisan documentation (#10203) * fix line break in Artisan documentation * Fix anchor link for assertServiceUnavailable method --- artisan.md | 3 +-- http-tests.md | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/artisan.md b/artisan.md index 9db6f9db2f2..f2c40fc96a3 100644 --- a/artisan.md +++ b/artisan.md @@ -692,8 +692,7 @@ $this->newLine(3); #### Tables -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: +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: ```php use App\Models\User; diff --git a/http-tests.md b/http-tests.md index 8ad87f6920f..1814c7b9cb5 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1004,7 +1004,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertSeeText](#assert-see-text) [assertSeeTextInOrder](#assert-see-text-in-order) [assertServerError](#assert-server-error) -[assertServiceUnavailable](#assert-server-unavailable) +[assertServiceUnavailable](#assert-service-unavailable) [assertSessionHas](#assert-session-has) [assertSessionHasInput](#assert-session-has-input) [assertSessionHasAll](#assert-session-has-all) @@ -1607,7 +1607,7 @@ Assert that the response has a server error (>= 500 , < 600) HTTP status code: $response->assertServerError(); ``` - + #### assertServiceUnavailable Assert that the response has a "Service Unavailable" (503) HTTP status code: From 6c2fd51dc320d3ddc551fc3ac8b7724faeec07c9 Mon Sep 17 00:00:00 2001 From: Hasan Orhan Date: Tue, 25 Feb 2025 17:08:16 +0300 Subject: [PATCH 2041/2609] [12.x] Add UUID v7 section for Models to upgrade guide (#10204) * uuid v7 section added to upgrade page * removal of the `HasVersion7Uuids` trait added to section * fix --------- Co-authored-by: Taylor Otwell --- upgrade.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/upgrade.md b/upgrade.md index 9622366bce8..3c36db1d397 100644 --- a/upgrade.md +++ b/upgrade.md @@ -12,6 +12,15 @@ + +## Medium Impact Changes + +
    + +- [Models and UUIDv7](#models-and-uuidv7) + +
    + ## Low Impact Changes @@ -134,6 +143,23 @@ $table = Schema::getTableListing(schema: 'main', schemaQualified: false); The `db:table` and `db:show` commands now output the results of all schemas on MySQL, MariaDB, and SQLite, just like PostgreSQL and SQL Server. + +### Eloquent + + +#### Models and UUIDv7 + +**Likelihood Of Impact: Medium** + +The `HasUuids` trait now returns UUIDs that are compatible with version 7 of the UUID spec (ordered UUIDs). If you would like to continue using ordered UUIDv4 strings for your model's IDs, you should now use the `HasVersion4Uuids` trait: + +```php +use Illuminate\Database\Eloquent\Concerns\HasUuids; // [tl! remove] +use Illuminate\Database\Eloquent\Concerns\HasVersion4Uuids as HasUuids; // [tl! add] +``` + +The `HasVersion7Uuids` trait has been removed. If you were previously using this trait, you should use the `HasUuids` trait instead, which now provides the same behavior. + ### Requests From 5098e264dd073b4bcd80b643e717cf6c341e169b Mon Sep 17 00:00:00 2001 From: Utsav Somaiya Date: Tue, 25 Feb 2025 19:55:38 +0530 Subject: [PATCH 2042/2609] Add `initialDatabaseReadAtValue` docs (#10191) * Add `initialDatabaseReadAtValue` docs * Update notifications.md --------- Co-authored-by: Taylor Otwell --- notifications.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/notifications.md b/notifications.md index 58dc24b2b4f..1878105d2da 100644 --- a/notifications.md +++ b/notifications.md @@ -95,7 +95,7 @@ The `notify` method that is provided by this trait expects to receive a notifica $user->notify(new InvoicePaid($invoice)); -> [!NOTE] +> [!NOTE] > Remember, you may use the `Notifiable` trait on any of your models. You are not limited to only including it on your `User` model. @@ -116,7 +116,7 @@ You can also send notifications immediately using the `sendNow` method. This met Every notification class has a `via` method that determines on which channels the notification will be delivered. Notifications may be sent on the `mail`, `database`, `broadcast`, `vonage`, and `slack` channels. -> [!NOTE] +> [!NOTE] > If you would like to use other delivery channels such as Telegram or Pusher, check out the community driven [Laravel Notification Channels website](http://laravel-notification-channels.com). The `via` method receives a `$notifiable` instance, which will be an instance of the class to which the notification is being sent. You may use `$notifiable` to determine which channels the notification should be delivered on: @@ -134,7 +134,7 @@ The `via` method receives a `$notifiable` instance, which will be an instance of ### Queueing Notifications -> [!WARNING] +> [!WARNING] > Before queueing notifications you should configure your queue and [start a worker](/docs/{{version}}/queues#running-the-queue-worker). 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: @@ -305,7 +305,7 @@ Alternatively, you may call the `afterCommit` method from your notification's co } } -> [!NOTE] +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -375,14 +375,14 @@ The `MailMessage` class contains a few simple methods to help you build transact ->line('Thank you for using our application!'); } -> [!NOTE] +> [!NOTE] > 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 beautiful, responsive HTML email template with a plain-text counterpart. Here is an example of an email generated by the `mail` channel: -> [!NOTE] +> [!NOTE] > 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. @@ -542,7 +542,7 @@ To add attachments to an email notification, use the `attach` method while build ->attach('/path/to/file'); } -> [!NOTE] +> [!NOTE] > The `attach` method offered by notification mail messages also accepts [attachable objects](/docs/{{version}}/mail#attachable-objects). Please consult the comprehensive [attachable object documentation](/docs/{{version}}/mail#attachable-objects) to learn more. When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: @@ -834,7 +834,7 @@ php artisan make:notifications-table php artisan migrate ``` -> [!NOTE] +> [!NOTE] > If your notifiable models are using [UUID or ULID primary keys](/docs/{{version}}/eloquent#uuid-and-ulid-keys), you should replace the `morphs` method with [`uuidMorphs`](/docs/{{version}}/migrations#column-method-uuidMorphs) or [`ulidMorphs`](/docs/{{version}}/migrations#column-method-ulidMorphs) in the notification table migration. @@ -855,18 +855,26 @@ If a notification supports being stored in a database table, you should define a ]; } -When the notification is stored in your application's database, the `type` column will be populated with the notification's class name. However, you may customize this behavior by defining a `databaseType` method on your notification class: +When a notification is stored in your application's database, the `type` column will be set to the notification's class name by default, and the `read_at` column will be `null`. However, you can customize this behavior by defining the `databaseType` and `initialDatabaseReadAtValue` methods in your notification class: + + use Illuminate\Support\Carbon; /** * Get the notification's database type. - * - * @return string */ public function databaseType(object $notifiable): string { return 'invoice-paid'; } + /** + * Get the initial value for the "read_at" column. + */ + public function initialDatabaseReadAtValue(): ?Carbon + { + return null; + } + #### `toDatabase` vs. `toArray` @@ -891,7 +899,7 @@ If you want to retrieve only the "unread" notifications, you may use the `unread echo $notification->type; } -> [!NOTE] +> [!NOTE] > 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. @@ -1196,7 +1204,7 @@ Instead of using the fluent message builder methods to construct your Block Kit "type": "plain_text", "text": "Team Announcement" } - }, + }, { "type": "section", "text": { @@ -1241,7 +1249,7 @@ In the following example, which utilizes the `actionsBlock` method, Slack will s ->actionsBlock(function (ActionsBlock $block) { // ID defaults to "button_acknowledge_invoice"... $block->button('Acknowledge Invoice')->primary(); - + // Manually configure the ID... $block->button('Deny')->danger()->id('deny_invoice'); }); @@ -1330,7 +1338,7 @@ For instance, returning `#support-channel` from the `routeNotificationForSlack` ### Notifying External Slack Workspaces -> [!NOTE] +> [!NOTE] > Before sending notifications to external Slack workspaces, your Slack App must be [distributed](#slack-app-distribution). Of course, you will often want to send notifications to the Slack workspaces owned by your application's users. To do so, you will first need to obtain a Slack OAuth token for the user. Thankfully, [Laravel Socialite](/docs/{{version}}/socialite) includes a Slack driver that will allow you to easily authenticate your application's users with Slack and [obtain a bot token](/docs/{{version}}/socialite#slack-bot-scopes). From 3029128b77d2893bc8e509cf358bcaf34a5f2e50 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 25 Feb 2025 10:31:11 -0600 Subject: [PATCH 2043/2609] document onlyInvalid --- http-tests.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/http-tests.md b/http-tests.md index 1814c7b9cb5..f81128ed2ae 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1822,6 +1822,12 @@ $response->assertInvalid([ ]); ``` +If you would like to assert that the given fields are the only fields with validation errors, you may use the `assertOnlyInvalid` method: + +```php +$response->assertOnlyInvalid(['name', 'email']); +``` + #### assertViewHas From e82816b331c7045cda1fc0e7c98230b803171a21 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 25 Feb 2025 11:58:55 -0700 Subject: [PATCH 2044/2609] streamline install docs --- installation.md | 155 +----------------------------------------------- sail.md | 20 +------ 2 files changed, 4 insertions(+), 171 deletions(-) diff --git a/installation.md b/installation.md index 9c1c484c5d7..d97be46aa52 100644 --- a/installation.md +++ b/installation.md @@ -9,14 +9,9 @@ - [Environment Based Configuration](#environment-based-configuration) - [Databases and Migrations](#databases-and-migrations) - [Directory Configuration](#directory-configuration) -- [Local Installation Using Herd](#local-installation-using-herd) +- [Installation Using Herd](#installation-using-herd) - [Herd on macOS](#herd-on-macos) - [Herd on Windows](#herd-on-windows) -- [Docker Installation Using Sail](#docker-installation-using-sail) - - [Sail on macOS](#sail-on-macos) - - [Sail on Windows](#sail-on-windows) - - [Sail on Linux](#sail-on-linux) - - [Choosing Your Sail Services](#choosing-your-sail-services) - [IDE Support](#ide-support) - [Next Steps](#next-steps) - [Laravel the Full Stack Framework](#laravel-the-fullstack-framework) @@ -157,8 +152,8 @@ php artisan migrate Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files present within your application. - -## Local Installation Using Herd + +## Installation Using Herd [Laravel Herd](https://herd.laravel.com) is a blazing fast, native Laravel and PHP development environment for macOS and Windows. Herd includes everything you need to get started with Laravel development, including PHP and Nginx. @@ -207,150 +202,6 @@ herd open You can learn more about Herd by checking out the [Herd documentation for Windows](https://herd.laravel.com/docs/windows). - -## Docker Installation Using Sail - -We want it to be as easy as possible to get started with Laravel regardless of your preferred operating system. So, there are a variety of options for developing and running a Laravel application on your local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel application using [Docker](https://www.docker.com). - -Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local machine's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your local machine. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). - -Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. - -> [!NOTE] -> Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. - - -### Sail on macOS - -If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is already installed, you can use a simple terminal command to create a new Laravel application. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: - -```shell -curl -s "https://laravel.build/example-app" | bash -``` - -Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. - -Sail installation may take several minutes while Sail's application containers are built on your local machine. - -After the application has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: - -```shell -cd example-app - -./vendor/bin/sail up -``` - -Once the application's Docker containers have started, you should run your application's [database migrations](/docs/{{version}}/migrations): - -```shell -./vendor/bin/sail artisan migrate -``` - -Finally, you can access the application in your web browser at: http://localhost. - -> [!NOTE] -> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). - - -### Sail on Windows - -Before we create a new Laravel application on your Windows machine, make sure to install [Docker Desktop](https://www.docker.com/products/docker-desktop). Next, you should ensure that Windows Subsystem for Linux 2 (WSL2) is installed and enabled. WSL allows you to run Linux binary executables natively on Windows 10. Information on how to install and enable WSL2 can be found within Microsoft's [developer environment documentation](https://docs.microsoft.com/en-us/windows/wsl/install-win10). - -> [!NOTE] -> After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). - -Next, you are ready to create your first Laravel application. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel application. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: - -```shell -curl -s https://laravel.build/example-app | bash -``` - -Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. - -Sail installation may take several minutes while Sail's application containers are built on your local machine. - -After the application has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: - -```shell -cd example-app - -./vendor/bin/sail up -``` - -Once the application's Docker containers have started, you should run your application's [database migrations](/docs/{{version}}/migrations): - -```shell -./vendor/bin/sail artisan migrate -``` - -Finally, you can access the application in your web browser at: http://localhost. - -> [!NOTE] -> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). - -#### Developing Within WSL2 - -Of course, you will need to be able to modify the Laravel application files that were created within your WSL2 installation. To accomplish this, we recommend using Microsoft's [Visual Studio Code](https://code.visualstudio.com) editor and their first-party extension for [Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack). - -Once these tools are installed, you may open any Laravel application by executing the `code .` command from your application's root directory using Windows Terminal. - - -### Sail on Linux - -If you're developing on Linux and [Docker Compose](https://docs.docker.com/compose/install/) is already installed, you can use a simple terminal command to create a new Laravel application. - -First, if you are using Docker Desktop for Linux, you should execute the following command. If you are not using Docker Desktop for Linux, you may skip this step: - -```shell -docker context use default -``` - -Then, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: - -```shell -curl -s https://laravel.build/example-app | bash -``` - -Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. - -Sail installation may take several minutes while Sail's application containers are built on your local machine. - -After the application has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: - -```shell -cd example-app - -./vendor/bin/sail up -``` - -Once the application's Docker containers have started, you should run your application's [database migrations](/docs/{{version}}/migrations): - -```shell -./vendor/bin/sail artisan migrate -``` - -Finally, you can access the application in your web browser at: http://localhost. - -> [!NOTE] -> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). - - -### Choosing Your Sail Services - -When creating a new Laravel application via Sail, you may use the `with` query string variable to choose which services should be configured in your new application's `docker-compose.yml` file. Available services include `mysql`, `pgsql`, `mariadb`, `redis`, `valkey`, `memcached`, `meilisearch`, `typesense`, `minio`, `selenium`, and `mailpit`: - -```shell -curl -s "https://laravel.build/example-app?with=mysql,redis" | bash -``` - -If you do not specify which services you would like configured, a default stack of `mysql`, `redis`, `meilisearch`, `mailpit`, and `selenium` will be configured. - -You may instruct Sail to install a default [Devcontainer](/docs/{{version}}/sail#using-devcontainers) by adding the `devcontainer` parameter to the URL: - -```shell -curl -s "https://laravel.build/example-app?with=mysql,redis&devcontainer" | bash -``` - ## IDE Support diff --git a/sail.md b/sail.md index a9932622b25..239c99e6e02 100644 --- a/sail.md +++ b/sail.md @@ -43,7 +43,7 @@ Laravel Sail is supported on macOS, Linux, and Windows (via [WSL2](https://docs. ## Installation and 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#docker-installation-using-sail) for your operating system. During installation, you will be asked to choose which Sail supported services your application will be interacting with. +Laravel Sail is automatically installed with all new Laravel applications so you may start using it immediately. ### Installing Sail Into Existing Applications @@ -183,24 +183,6 @@ Composer commands may be executed using the `composer` command. Laravel Sail's a sail composer require laravel/sanctum ``` - -#### Installing Composer Dependencies for Existing Applications - -If you are developing an application with a team, you may not be the one that initially creates the Laravel application. Therefore, none of the application's Composer dependencies, including Sail, will be installed after you clone the application's repository to your local computer. - -You may install the application's dependencies by navigating to the application's directory and executing the following command. This command uses a small Docker container containing PHP and Composer to install the application's dependencies: - -```shell -docker run --rm \ - -u "$(id -u):$(id -g)" \ - -v "$(pwd):/var/www/html" \ - -w /var/www/html \ - laravelsail/php84-composer:latest \ - composer install --ignore-platform-reqs -``` - -When using the `laravelsail/phpXX-composer` image, you should use the same version of PHP that you plan to use for your application (`80`, `81`, `82`, `83`, or `84`). - ### Executing Artisan Commands From b1339c0a98c57e0f7b85cce603090a315ac01c86 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 25 Feb 2025 12:09:58 -0700 Subject: [PATCH 2045/2609] wip --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index d97be46aa52..39710050e56 100644 --- a/installation.md +++ b/installation.md @@ -145,7 +145,7 @@ php artisan migrate ``` > [!NOTE] -> If you are developing on macOS or Windows and need to install MySQL, PostgreSQL, or Redis locally, consider using [Herd Pro](https://herd.laravel.com/#plans). +> If you are developing on macOS or Windows and need to install MySQL, PostgreSQL, or Redis locally, consider using [Herd Pro](https://herd.laravel.com/#plans) or [DBngin](https://dbngin.com/). ### Directory Configuration From f1468536f121e95250eb05b5a26e69e88490ab1f Mon Sep 17 00:00:00 2001 From: Lux Bontorostro <46734882+iamlux20@users.noreply.github.com> Date: Wed, 26 Feb 2025 21:23:32 +0800 Subject: [PATCH 2046/2609] Added PHPUnit and PEST as dependency update (#10205) These were updated mid-year and there's a possibility devs may have not upgraded this --- upgrade.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/upgrade.md b/upgrade.md index 3c36db1d397..a7aa7694292 100644 --- a/upgrade.md +++ b/upgrade.md @@ -52,6 +52,8 @@ You should update the following dependencies in your application's `composer.jso
    - `laravel/framework` to `^12.0` +- `phpunit/phpunit` to `^11.0` +- `pestphp/pest` to `^3.0`
    From 7589d0d3515172587ff625a48969afe6cb8056ae Mon Sep 17 00:00:00 2001 From: Rubaiyat E Mohammad Date: Thu, 27 Feb 2025 21:04:13 +0600 Subject: [PATCH 2047/2609] Updated releases.md (#10211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here are the changes I made and why: 1. Subject-Verb Agreement Fix: Changed "starter kits utilizes" → "starter kits utilize" (plural subject requires plural verb). 2. Hyphenation Fixes: Changed "Tailwind based" → "Tailwind-based" (compound adjective). Changed "WorkOS AuthKit powered" → "WorkOS AuthKit-powered" (compound adjective). Changed "quality of life" → "quality-of-life" (compound adjective). 3. Word Choice Improvement: Changed "continual quality of life improvements" → "continuous quality-of-life improvements" ("continuous" is better for ongoing changes). 4. Clarified Meaning: Fixed awkward phrasing and ensured clarity in how Laravel Breeze and Jetstream updates are described. These changes improve grammatical accuracy, readability, and clarity. ✅ --- releases.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/releases.md b/releases.md index bdbaf1c820d..53cc96bd39e 100644 --- a/releases.md +++ b/releases.md @@ -53,18 +53,17 @@ Laravel 12 continues the improvements made in Laravel 11.x by updating upstream ### Minimal Breaking Changes -Much of our focus during this release cycle has been minimizing breaking changes. Instead, we have dedicated ourselves to shipping continual quality of life improvements throughout the year that do not break existing applications. +Much of our focus during this release cycle has been minimizing breaking changes. Instead, we have dedicated ourselves to shipping continuous quality-of-life improvements throughout the year that do not break existing applications. Therefore, the Laravel 12 release is a relatively minor "maintenance release" in order to upgrade existing dependencies. In light of this, most Laravel applications may upgrade to Laravel 12 without changing any application code. ### New Application Starter Kits -Laravel 12 introduces new [application starter kits](/docs/{{version}}/starter-kits) for React, Vue, and Livewire. The React and Vue starter kits utilize Inertia 2, TypeScript, [shadcn/ui](https://ui.shadcn.com), and Tailwind, while the Livewire starter kits utilizes the Tailwind based [Flux UI](https://fluxui.dev) component library and Laravel Volt. +Laravel 12 introduces new [application starter kits](/docs/{{version}}/starter-kits) for React, Vue, and Livewire. The React and Vue starter kits utilize Inertia 2, TypeScript, [shadcn/ui](https://ui.shadcn.com), and Tailwind, while the Livewire starter kits utilize the Tailwind-based [Flux UI](https://fluxui.dev) component library and Laravel Volt. -The React, Vue, and Livewire starter kits all utilize Laravel's built-in authentication system to offer login, registration, password reset, email verification, and more. In addition, we are introducing a [WorkOS AuthKit](https://authkit.com) powered variant of each starter kit, offering social authentication, passkeys, and SSO support. WorkOS offers free authentication for applications up to 1 million monthly active users. +The React, Vue, and Livewire starter kits all utilize Laravel's built-in authentication system to offer login, registration, password reset, email verification, and more. In addition, we are introducing a [WorkOS AuthKit-powered](https://authkit.com) variant of each starter kit, offering social authentication, passkeys, and SSO support. WorkOS offers free authentication for applications up to 1 million monthly active users. With the introduction of our new application starter kits, Laravel Breeze and Laravel Jetstream will no longer receive additional updates. To get started with our new starter kits, check out the [starter kit documentation](/docs/{{version}}/starter-kits). - From 296e2befec043e48862049e4f88bc7ea08b50496 Mon Sep 17 00:00:00 2001 From: Md Rafsan Jani Rafin <48161278+itsrafsanjani@users.noreply.github.com> Date: Fri, 28 Feb 2025 00:25:36 +0600 Subject: [PATCH 2048/2609] [12.x] Format code examples in helpers.md for consistency (#10210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * format code examples in helpers.md for better readability * Update helpers.md Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> --------- Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> --- helpers.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/helpers.md b/helpers.md index 4bd56ddcf39..734cdac1cce 100644 --- a/helpers.md +++ b/helpers.md @@ -834,15 +834,19 @@ $items = Arr::random($array, 2); The `Arr::reject` method removes items from an array using the given closure: - use Illuminate\Support\Arr; +```php + 100, 2 => 300, 4 => 500] +$filtered = Arr::reject($array, function (string|int $value, int $key) { + return is_string($value); +}); + +// [0 => 100, 2 => 300, 4 => 500] +``` #### `Arr::set()` {.collection-method} From 97e713da10ce740be90f259c765a6cd7c1a4c504 Mon Sep 17 00:00:00 2001 From: Subham Chakraborty <45362908+subhamchbty@users.noreply.github.com> Date: Thu, 27 Feb 2025 23:59:04 +0530 Subject: [PATCH 2049/2609] fix link reference in installation documentation (#10207) --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 39710050e56..8918a939952 100644 --- a/installation.md +++ b/installation.md @@ -79,7 +79,7 @@ composer global require laravel/installer ``` > [!NOTE] -> For a fully-featured, graphical PHP installation and management experience, check out [Laravel Herd](#local-installation-using-herd). +> For a fully-featured, graphical PHP installation and management experience, check out [Laravel Herd](#installation-using-herd). ### Creating an Application From 5d35bc11ad9e0f8928e1880c20d2f23cbe7e90c3 Mon Sep 17 00:00:00 2001 From: Pj Date: Fri, 28 Feb 2025 02:29:24 +0800 Subject: [PATCH 2050/2609] correct version 11.x to 12.x (#10206) --- contributions.md | 4 ++-- passport.md | 2 +- readme.md | 2 +- strings.md | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contributions.md b/contributions.md index 54f24b97c5a..9aa1ac757ac 100644 --- a/contributions.md +++ b/contributions.md @@ -82,9 +82,9 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest version that supports bug fixes (currently `11.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `12.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. -**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `11.x`). +**Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `12.x`). **Major** new features or features with breaking changes should always be sent to the `master` branch, which contains the upcoming release. diff --git a/passport.md b/passport.md index 262228c3f8d..952d5a5fb24 100644 --- a/passport.md +++ b/passport.md @@ -241,7 +241,7 @@ public function register(): void } ``` -Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/11.x/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: +Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/12.x/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: ```php Route::group([ diff --git a/readme.md b/readme.md index 0999c0db95a..60c207bec3e 100644 --- a/readme.md +++ b/readme.md @@ -4,4 +4,4 @@ You can find the online version of the Laravel documentation at [https://laravel ## Contribution Guidelines -If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 11 would be submitted to the `11.x` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. +If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 12 would be submitted to the `12.x` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. diff --git a/strings.md b/strings.md index 8b13c50dc11..ada13b201ec 100644 --- a/strings.md +++ b/strings.md @@ -1172,11 +1172,11 @@ The `Str::replace` method replaces a given string within the string: ```php use Illuminate\Support\Str; -$string = 'Laravel 10.x'; +$string = 'Laravel 11.x'; -$replaced = Str::replace('10.x', '11.x', $string); +$replaced = Str::replace('11.x', '12.x', $string); -// Laravel 11.x +// Laravel 12.x ``` The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: From e06f2935eb93c778624d83e6116f55ec1b649ac4 Mon Sep 17 00:00:00 2001 From: Christoph Stockinger Date: Thu, 27 Feb 2025 23:22:23 +0100 Subject: [PATCH 2051/2609] [12.x] Add Str::uuid7 to string helpers (#10213) * Add uuid7 string helper * Update strings.md --------- Co-authored-by: Taylor Otwell --- strings.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/strings.md b/strings.md index ada13b201ec..2a11cf35708 100644 --- a/strings.md +++ b/strings.md @@ -110,6 +110,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::ulid](#method-str-ulid) [Str::unwrap](#method-str-unwrap) [Str::uuid](#method-str-uuid) +[Str::uuid7](#method-str-uuid7) [Str::wordCount](#method-str-word-count) [Str::wordWrap](#method-str-word-wrap) [Str::words](#method-str-words) @@ -1682,6 +1683,23 @@ To instruct the `uuid` method to return to generating UUIDs normally, you may in Str::createUuidsNormally(); ``` + +#### `Str::uuid7()` {.collection-method} + +The `Str::uuid7` method generates a UUID (version 7): + +```php +use Illuminate\Support\Str; + +return (string) Str::uuid7(); +``` + +A `DateTimeInterface` may be passed as an optional parameter which will be used to generate the ordered UUID: + +```php +return (string) Str::uuid7(time: now()); +``` + #### `Str::wordCount()` {.collection-method} From 6a37e9b9534976b0c9e6311cc4ea904b9d58ca27 Mon Sep 17 00:00:00 2001 From: Nurul Amin Ratul <95366111+Rat01047@users.noreply.github.com> Date: Fri, 28 Feb 2025 22:38:44 +0600 Subject: [PATCH 2052/2609] Update starter-kits.md (#10217) --- starter-kits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index bb35a29b557..c475b85147e 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -189,7 +189,7 @@ import { Switch } from '@/Components/ui/switch' #### Available Layouts -The Vue starter kit includes two different primary layouts for you choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is imported at the top of your application's `resources/js/layouts/AppLayout.vue` file: +The Vue starter kit includes two different primary layouts for you to choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is imported at the top of your application's `resources/js/layouts/AppLayout.vue` file: ```js import AppLayout from '@/layouts/app/AppSidebarLayout.vue'; // [tl! remove] @@ -238,7 +238,7 @@ resources/views #### Available Layouts -The Livewire starter kit includes two different primary layouts for you choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is used by your application's `resources/views/components/layouts/app.blade.php` file. In addition, you should add the `container` attribute to the main Flux component: +The Livewire starter kit includes two different primary layouts for you to choose from: a "sidebar" layout and a "header" layout. The sidebar layout is the default, but you can switch to the header layout by modifying the layout that is used by your application's `resources/views/components/layouts/app.blade.php` file. In addition, you should add the `container` attribute to the main Flux component: ```blade From 554b6a8b9b387258e7da5e9fd569a84c18d8f925 Mon Sep 17 00:00:00 2001 From: Al-Amin Firdows Date: Fri, 28 Feb 2025 22:58:52 +0600 Subject: [PATCH 2053/2609] [12.x] Add Arr::select method documentation (#10216) * Add Arr::select method documentation * Add PHP opening tag to Arr::select method documentation * fix grammar issue * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index 734cdac1cce..161a54645d4 100644 --- a/helpers.md +++ b/helpers.md @@ -67,6 +67,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::query](#method-array-query) [Arr::random](#method-array-random) [Arr::reject](#method-array-reject) +[Arr::select](#method-array-select) [Arr::set](#method-array-set) [Arr::shuffle](#method-array-shuffle) [Arr::sort](#method-array-sort) @@ -835,8 +836,6 @@ $items = Arr::random($array, 2); The `Arr::reject` method removes items from an array using the given closure: ```php - 100, 2 => 300, 4 => 500] ``` + +#### `Arr::select()` {.collection-method} + +The `Arr::select` method selects an array of values from an array: + +```php +use Illuminate\Support\Arr; + +$array = [ + ['id' => 1, 'name' => 'Desk', 'price' => 200], + ['id' => 2, 'name' => 'Table', 'price' => 150], + ['id' => 3, 'name' => 'Chair', 'price' => 300], +]; + +Arr::select($array, ['name', 'price']); + +// [['name' => 'Desk', 'price' => 200], ['name' => 'Table', 'price' => 150], ['name' => 'Chair', 'price' => 300]] +``` + #### `Arr::set()` {.collection-method} From 0815e22f5f81c6d0e5f0dce67eb9f9791b357f5c Mon Sep 17 00:00:00 2001 From: A G Date: Mon, 3 Mar 2025 09:40:57 -0500 Subject: [PATCH 2054/2609] update facade urls to new api doc links (#10222) --- facades.md | 110 ++++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/facades.md b/facades.md index d48b3188f26..09382f5257f 100644 --- a/facades.md +++ b/facades.md @@ -306,60 +306,60 @@ Below you will find every facade and its underlying class. This is a useful tool | Facade | Class | Service Container Binding | | --- | --- | --- | -| App | [Illuminate\Foundation\Application](https://laravel.com/api/{{version}}/Illuminate/Foundation/Application.html) | `app` | -| Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/{{version}}/Illuminate/Contracts/Console/Kernel.html) | `artisan` | -| Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` | -| Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/{{version}}/Illuminate/Auth/AuthManager.html) | `auth` | -| Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/{{version}}/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` | -| Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/{{version}}/Illuminate/Contracts/Broadcasting/Broadcaster.html) |   | -| Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Broadcasting/Factory.html) |   | -| Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Bus/Dispatcher.html) |   | -| Cache (Instance) | [Illuminate\Cache\Repository](https://laravel.com/api/{{version}}/Illuminate/Cache/Repository.html) | `cache.store` | -| Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/{{version}}/Illuminate/Cache/CacheManager.html) | `cache` | -| Config | [Illuminate\Config\Repository](https://laravel.com/api/{{version}}/Illuminate/Config/Repository.html) | `config` | -| Context | [Illuminate\Log\Context\Repository](https://laravel.com/api/{{version}}/Illuminate/Log/Context/Repository.html) |   | -| Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/{{version}}/Illuminate/Cookie/CookieJar.html) | `cookie` | -| Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/{{version}}/Illuminate/Encryption/Encrypter.html) | `encrypter` | -| Date | [Illuminate\Support\DateFactory](https://laravel.com/api/{{version}}/Illuminate/Support/DateFactory.html) | `date` | -| DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/{{version}}/Illuminate/Database/Connection.html) | `db.connection` | -| DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/{{version}}/Illuminate/Database/DatabaseManager.html) | `db` | -| Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Events/Dispatcher.html) | `events` | -| Exceptions (Instance) | [Illuminate\Contracts\Debug\ExceptionHandler](https://laravel.com/api/{{version}}/Illuminate/Contracts/Debug/ExceptionHandler.html) |   | -| Exceptions | [Illuminate\Foundation\Exceptions\Handler](https://laravel.com/api/{{version}}/Illuminate/Foundation/Exceptions/Handler.html) |   | -| File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Filesystem/Filesystem.html) | `files` | -| Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Access/Gate.html) |   | -| Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Hashing/Hasher.html) | `hash` | -| Http | [Illuminate\Http\Client\Factory](https://laravel.com/api/{{version}}/Illuminate/Http/Client/Factory.html) |   | -| Lang | [Illuminate\Translation\Translator](https://laravel.com/api/{{version}}/Illuminate/Translation/Translator.html) | `translator` | -| Log | [Illuminate\Log\LogManager](https://laravel.com/api/{{version}}/Illuminate/Log/LogManager.html) | `log` | -| Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/{{version}}/Illuminate/Mail/Mailer.html) | `mailer` | -| Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/{{version}}/Illuminate/Notifications/ChannelManager.html) |   | -| Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` | -| Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` | -| Pipeline (Instance) | [Illuminate\Pipeline\Pipeline](https://laravel.com/api/{{version}}/Illuminate/Pipeline/Pipeline.html) |   | -| Process | [Illuminate\Process\Factory](https://laravel.com/api/{{version}}/Illuminate/Process/Factory.html) |   | -| Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) |   | -| Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` | -| Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` | -| RateLimiter | [Illuminate\Cache\RateLimiter](https://laravel.com/api/{{version}}/Illuminate/Cache/RateLimiter.html) |   | -| Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/{{version}}/Illuminate/Routing/Redirector.html) | `redirect` | -| Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/{{version}}/Illuminate/Redis/Connections/Connection.html) | `redis.connection` | -| Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/{{version}}/Illuminate/Redis/RedisManager.html) | `redis` | -| Request | [Illuminate\Http\Request](https://laravel.com/api/{{version}}/Illuminate/Http/Request.html) | `request` | -| Response (Instance) | [Illuminate\Http\Response](https://laravel.com/api/{{version}}/Illuminate/Http/Response.html) |   | -| Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Routing/ResponseFactory.html) |   | -| Route | [Illuminate\Routing\Router](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) | `router` | -| Schedule | [Illuminate\Console\Scheduling\Schedule](https://laravel.com/api/{{version}}/Illuminate/Console/Scheduling/Schedule.html) |   | -| Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/{{version}}/Illuminate/Database/Schema/Builder.html) |   | -| Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/{{version}}/Illuminate/Session/Store.html) | `session.store` | -| Session | [Illuminate\Session\SessionManager](https://laravel.com/api/{{version}}/Illuminate/Session/SessionManager.html) | `session` | -| Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` | -| Storage | [Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/{{version}}/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` | -| URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/{{version}}/Illuminate/Routing/UrlGenerator.html) | `url` | -| Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/{{version}}/Illuminate/Validation/Validator.html) |   | -| Validator | [Illuminate\Validation\Factory](https://laravel.com/api/{{version}}/Illuminate/Validation/Factory.html) | `validator` | -| View (Instance) | [Illuminate\View\View](https://laravel.com/api/{{version}}/Illuminate/View/View.html) |   | -| View | [Illuminate\View\Factory](https://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` | -| Vite | [Illuminate\Foundation\Vite](https://laravel.com/api/{{version}}/Illuminate/Foundation/Vite.html) |   | +| App | [Illuminate\Foundation\Application](https://api.laravel.com/docs/{{version}}/Illuminate/Foundation/Application.html) | `app` | +| Artisan | [Illuminate\Contracts\Console\Kernel](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Console/Kernel.html) | `artisan` | +| Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` | +| Auth | [Illuminate\Auth\AuthManager](https://api.laravel.com/docs/{{version}}/Illuminate/Auth/AuthManager.html) | `auth` | +| Blade | [Illuminate\View\Compilers\BladeCompiler](https://api.laravel.com/docs/{{version}}/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` | +| Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Broadcasting/Broadcaster.html) |   | +| Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Broadcasting/Factory.html) |   | +| Bus | [Illuminate\Contracts\Bus\Dispatcher](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Bus/Dispatcher.html) |   | +| Cache (Instance) | [Illuminate\Cache\Repository](https://api.laravel.com/docs/{{version}}/Illuminate/Cache/Repository.html) | `cache.store` | +| Cache | [Illuminate\Cache\CacheManager](https://api.laravel.com/docs/{{version}}/Illuminate/Cache/CacheManager.html) | `cache` | +| Config | [Illuminate\Config\Repository](https://api.laravel.com/docs/{{version}}/Illuminate/Config/Repository.html) | `config` | +| Context | [Illuminate\Log\Context\Repository](https://api.laravel.com/docs/{{version}}/Illuminate/Log/Context/Repository.html) |   | +| Cookie | [Illuminate\Cookie\CookieJar](https://api.laravel.com/docs/{{version}}/Illuminate/Cookie/CookieJar.html) | `cookie` | +| Crypt | [Illuminate\Encryption\Encrypter](https://api.laravel.com/docs/{{version}}/Illuminate/Encryption/Encrypter.html) | `encrypter` | +| Date | [Illuminate\Support\DateFactory](https://api.laravel.com/docs/{{version}}/Illuminate/Support/DateFactory.html) | `date` | +| DB (Instance) | [Illuminate\Database\Connection](https://api.laravel.com/docs/{{version}}/Illuminate/Database/Connection.html) | `db.connection` | +| DB | [Illuminate\Database\DatabaseManager](https://api.laravel.com/docs/{{version}}/Illuminate/Database/DatabaseManager.html) | `db` | +| Event | [Illuminate\Events\Dispatcher](https://api.laravel.com/docs/{{version}}/Illuminate/Events/Dispatcher.html) | `events` | +| Exceptions (Instance) | [Illuminate\Contracts\Debug\ExceptionHandler](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Debug/ExceptionHandler.html) |   | +| Exceptions | [Illuminate\Foundation\Exceptions\Handler](https://api.laravel.com/docs/{{version}}/Illuminate/Foundation/Exceptions/Handler.html) |   | +| File | [Illuminate\Filesystem\Filesystem](https://api.laravel.com/docs/{{version}}/Illuminate/Filesystem/Filesystem.html) | `files` | +| Gate | [Illuminate\Contracts\Auth\Access\Gate](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Auth/Access/Gate.html) |   | +| Hash | [Illuminate\Contracts\Hashing\Hasher](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Hashing/Hasher.html) | `hash` | +| Http | [Illuminate\Http\Client\Factory](https://api.laravel.com/docs/{{version}}/Illuminate/Http/Client/Factory.html) |   | +| Lang | [Illuminate\Translation\Translator](https://api.laravel.com/docs/{{version}}/Illuminate/Translation/Translator.html) | `translator` | +| Log | [Illuminate\Log\LogManager](https://api.laravel.com/docs/{{version}}/Illuminate/Log/LogManager.html) | `log` | +| Mail | [Illuminate\Mail\Mailer](https://api.laravel.com/docs/{{version}}/Illuminate/Mail/Mailer.html) | `mailer` | +| Notification | [Illuminate\Notifications\ChannelManager](https://api.laravel.com/docs/{{version}}/Illuminate/Notifications/ChannelManager.html) |   | +| Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://api.laravel.com/docs/{{version}}/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` | +| Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://api.laravel.com/docs/{{version}}/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` | +| Pipeline (Instance) | [Illuminate\Pipeline\Pipeline](https://api.laravel.com/docs/{{version}}/Illuminate/Pipeline/Pipeline.html) |   | +| Process | [Illuminate\Process\Factory](https://api.laravel.com/docs/{{version}}/Illuminate/Process/Factory.html) |   | +| Queue (Base Class) | [Illuminate\Queue\Queue](https://api.laravel.com/docs/{{version}}/Illuminate/Queue/Queue.html) |   | +| Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` | +| Queue | [Illuminate\Queue\QueueManager](https://api.laravel.com/docs/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` | +| RateLimiter | [Illuminate\Cache\RateLimiter](https://api.laravel.com/docs/{{version}}/Illuminate/Cache/RateLimiter.html) |   | +| Redirect | [Illuminate\Routing\Redirector](https://api.laravel.com/docs/{{version}}/Illuminate/Routing/Redirector.html) | `redirect` | +| Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://api.laravel.com/docs/{{version}}/Illuminate/Redis/Connections/Connection.html) | `redis.connection` | +| Redis | [Illuminate\Redis\RedisManager](https://api.laravel.com/docs/{{version}}/Illuminate/Redis/RedisManager.html) | `redis` | +| Request | [Illuminate\Http\Request](https://api.laravel.com/docs/{{version}}/Illuminate/Http/Request.html) | `request` | +| Response (Instance) | [Illuminate\Http\Response](https://api.laravel.com/docs/{{version}}/Illuminate/Http/Response.html) |   | +| Response | [Illuminate\Contracts\Routing\ResponseFactory](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Routing/ResponseFactory.html) |   | +| Route | [Illuminate\Routing\Router](https://api.laravel.com/docs/{{version}}/Illuminate/Routing/Router.html) | `router` | +| Schedule | [Illuminate\Console\Scheduling\Schedule](https://api.laravel.com/docs/{{version}}/Illuminate/Console/Scheduling/Schedule.html) |   | +| Schema | [Illuminate\Database\Schema\Builder](https://api.laravel.com/docs/{{version}}/Illuminate/Database/Schema/Builder.html) |   | +| Session (Instance) | [Illuminate\Session\Store](https://api.laravel.com/docs/{{version}}/Illuminate/Session/Store.html) | `session.store` | +| Session | [Illuminate\Session\SessionManager](https://api.laravel.com/docs/{{version}}/Illuminate/Session/SessionManager.html) | `session` | +| Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://api.laravel.com/docs/{{version}}/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` | +| Storage | [Illuminate\Filesystem\FilesystemManager](https://api.laravel.com/docs/{{version}}/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` | +| URL | [Illuminate\Routing\UrlGenerator](https://api.laravel.com/docs/{{version}}/Illuminate/Routing/UrlGenerator.html) | `url` | +| Validator (Instance) | [Illuminate\Validation\Validator](https://api.laravel.com/docs/{{version}}/Illuminate/Validation/Validator.html) |   | +| Validator | [Illuminate\Validation\Factory](https://api.laravel.com/docs/{{version}}/Illuminate/Validation/Factory.html) | `validator` | +| View (Instance) | [Illuminate\View\View](https://api.laravel.com/docs/{{version}}/Illuminate/View/View.html) |   | +| View | [Illuminate\View\Factory](https://api.laravel.com/docs/{{version}}/Illuminate/View/Factory.html) | `view` | +| Vite | [Illuminate\Foundation\Vite](https://api.laravel.com/docs/{{version}}/Illuminate/Foundation/Vite.html) |   | From c5a35be50077dca8b248611f2cfe39b92d03163f Mon Sep 17 00:00:00 2001 From: Matthew Forth <31514505+mattyforth@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:43:26 +0000 Subject: [PATCH 2055/2609] chore: Added note to notifications.md (#10223) Added a note underneath the 'writing the message' heading for using markdown in notifications. --- notifications.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/notifications.md b/notifications.md index fbc5b2914da..d0c5bedc83d 100644 --- a/notifications.md +++ b/notifications.md @@ -831,6 +831,9 @@ Thanks,
    ``` +> [!NOTE] +> Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. + #### Button Component From e099b736871736bb608f0d044f5b6aec43027515 Mon Sep 17 00:00:00 2001 From: Roelof Kallenkoot Date: Mon, 3 Mar 2025 15:44:29 +0100 Subject: [PATCH 2056/2609] Fix 404 to GridFS documentation in mongodb.md (#10221) --- mongodb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongodb.md b/mongodb.md index af992cd0d24..e63872f32f3 100644 --- a/mongodb.md +++ b/mongodb.md @@ -93,7 +93,7 @@ Once your configuration is complete, you can use the `mongodb` package and datab - [Write complex queries](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/query-builder/) using the query builder. - The `mongodb` [cache driver](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/cache/) is optimized to use MongoDB features such as TTL indexes to automatically clear expired cache entries. - [Dispatch and process queued jobs](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/queues/) with the `mongodb` queue driver. -- [Storing files in GridFS](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/gridfs/), via the [GridFS Adapter for Flysystem](https://flysystem.thephpleague.com/docs/adapter/gridfs/). +- [Storing files in GridFS](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/filesystems/), via the [GridFS Adapter for Flysystem](https://flysystem.thephpleague.com/docs/adapter/gridfs/). - Most third party packages using a database connection or Eloquent can be used with MongoDB. To continue learning how to use MongoDB and Laravel, refer to MongoDB's [Quick Start guide](https://www.mongodb.com/docs/drivers/php/laravel-mongodb/current/quick-start/). From 5b8b207487320f081bba4c79b1c740bda8ada6e9 Mon Sep 17 00:00:00 2001 From: "Javed Ahmed (Baloch)" Date: Tue, 4 Mar 2025 19:59:38 +0500 Subject: [PATCH 2057/2609] Fix/remove extra backticks in code snippets in collections.md (#10224) Co-authored-by: = <=> --- collections.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/collections.md b/collections.md index 8037ab297c7..45cb93525a9 100644 --- a/collections.md +++ b/collections.md @@ -859,8 +859,6 @@ $flattened->all(); The `dump` method dumps the collection's items: ```php - -``````php $collection = collect(['John Doe', 'Jane Doe']); $collection->dump(); @@ -883,8 +881,6 @@ If you want to stop executing the script after dumping the collection, use the [ The `duplicates` method retrieves and returns duplicate values from the collection: ```php - -``````php $collection = collect(['a', 'b', 'a', 'c', 'b']); $collection->duplicates(); @@ -895,8 +891,6 @@ $collection->duplicates(); If the collection contains arrays or objects, you can pass the key of the attributes that you wish to check for duplicate values: ```php - -``````php $employees = collect([ ['email' => 'abigail@example.com', 'position' => 'Developer'], ['email' => 'james@example.com', 'position' => 'Designer'], From 4a3b41f7cae8799187abd203faa73dd7c3bc2ad9 Mon Sep 17 00:00:00 2001 From: Austin Carpenter Date: Wed, 5 Mar 2025 01:02:55 +1000 Subject: [PATCH 2058/2609] Clarify ASCII numerals in `alpha_dash` and `alpha_num` validation rules (#10220) * Add mention of 0-9 numerals * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation.md b/validation.md index 891d50c881e..0a18dd6d0e1 100644 --- a/validation.md +++ b/validation.md @@ -1214,7 +1214,7 @@ To restrict this validation rule to characters in the ASCII range (`a-z` and `A- The field under validation must be entirely Unicode alpha-numeric characters contained in [`\p{L}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AL%3A%5D&g=&i=), [`\p{M}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AM%3A%5D&g=&i=), [`\p{N}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AN%3A%5D&g=&i=), as well as ASCII dashes (`-`) and ASCII underscores (`_`). -To restrict this validation rule to characters in the ASCII range (`a-z` and `A-Z`), you may provide the `ascii` option to the validation rule: +To restrict this validation rule to characters in the ASCII range (`a-z`, `A-Z`, and `0-9`), you may provide the `ascii` option to the validation rule: ```php 'username' => 'alpha_dash:ascii', @@ -1225,7 +1225,7 @@ To restrict this validation rule to characters in the ASCII range (`a-z` and `A- The field under validation must be entirely Unicode alpha-numeric characters contained in [`\p{L}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AL%3A%5D&g=&i=), [`\p{M}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AM%3A%5D&g=&i=), and [`\p{N}`](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AN%3A%5D&g=&i=). -To restrict this validation rule to characters in the ASCII range (`a-z` and `A-Z`), you may provide the `ascii` option to the validation rule: +To restrict this validation rule to characters in the ASCII range (`a-z`, `A-Z`, and `0-9`), you may provide the `ascii` option to the validation rule: ```php 'username' => 'alpha_num:ascii', From c589443d18eeeff1a22cb75ead92411dadc0e6fd Mon Sep 17 00:00:00 2001 From: Davendra Naraine Date: Tue, 4 Mar 2025 11:13:01 -0400 Subject: [PATCH 2059/2609] add documentation for traditional livewire components in starter kit (#10219) * add documentation for traditional livewire components in starter kit * update description for livewire section * fix grammar * Update starter-kits.md * Update starter-kits.md --------- Co-authored-by: Taylor Otwell --- starter-kits.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index c475b85147e..a37e14e23e8 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -73,7 +73,7 @@ Our Livewire starter kit provides the perfect starting point for building Larave Livewire is a powerful way of building dynamic, reactive, frontend UIs using just PHP. It's a great fit for teams that primarily use Blade templates and are looking for a simpler alternative to JavaScript-driven SPA frameworks like React and Vue. -The Livewire starter kit utilizes Laravel Volt, Tailwind, and the [Flux UI](https://fluxui.dev) component library. +The Livewire starter kit utilizes Livewire, Tailwind, and the [Flux UI](https://fluxui.dev) component library. ## Starter Kit Customization @@ -221,7 +221,9 @@ import AuthLayout from '@/layouts/auth/AuthSplitLayout.vue'; // [tl! add] ### Livewire -Our Livewire starter kit is built with Livewire 3, Laravel Volt, Tailwind, and [Flux UI](https://fluxui.dev/). As with all of our starter kits, all of the backend and frontend code exists within your application to allow for full customization. +Our Livewire starter kit is built with Livewire 3, Tailwind, and [Flux UI](https://fluxui.dev/). As with all of our starter kits, all of the backend and frontend code exists within your application to allow for full customization. + +#### Livewire and Volt The majority of the frontend code is located in the `resources/views` directory. You are free to modify any of the code to customize the appearance and behavior of your application: @@ -235,6 +237,10 @@ resources/views ├── welcome.blade.php # Guest user welcome page ``` +#### Traditional Livewire Components + +The frontend code is located in the `resouces/views` directory, while the `app/Livewire` directory contains the corresponding backend logic for the Livewire components. + #### Available Layouts From 78ba6edcdf007d74666bf15c37705015b2cc57c4 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:43:29 +0200 Subject: [PATCH 2060/2609] Removed the duplicated use in the validation documentation sentence for clarity (#10226) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 0a18dd6d0e1..45009c23ce8 100644 --- a/validation.md +++ b/validation.md @@ -1427,7 +1427,7 @@ A _ratio_ constraint should be represented as width divided by height. This can 'avatar' => 'dimensions:ratio=3/2' ``` -Since this rule requires several arguments, it is often more convenient to use use the `Rule::dimensions` method to fluently construct the rule: +Since this rule requires several arguments, it is often more convenient to use the `Rule::dimensions` method to fluently construct the rule: ```php use Illuminate\Support\Facades\Validator; From a90e202584719a313b802cdd1f49e2f3c34c5fda Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 4 Mar 2025 15:47:57 -0600 Subject: [PATCH 2061/2609] document event stream customization --- responses.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/responses.md b/responses.md index f031d3e0957..a4201290946 100644 --- a/responses.md +++ b/responses.md @@ -417,7 +417,18 @@ Route::get('/chat', function () { }); ``` -This event stream may be consumed via an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) object by your application's frontend. The `eventStream` method will automatically send a `` update to the event stream when the stream is complete: +If you would like to customize the name of the event, you may yield an instance of the `StreamedEvent` class: + +```php +use Illuminate\Http\StreamedEvent; + +yield new StreamedEvent( + event: 'update', + data: $response->choices[0], +); +``` + +Event streams may be consumed via an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) object by your application's frontend. The `eventStream` method will automatically send a `` update to the event stream when the stream is complete: ```js const source = new EventSource('/chat'); @@ -433,6 +444,14 @@ source.addEventListener('update', (event) => { }) ``` +To customize the final event that is sent to the event stream, you may provide a `StreamedEvent` instance to the `eventStream` method's `endStreamWith` argument: + +```php +return response()->eventStream(function () { + // ... +}, endStreamWith: new StreamedEvent(event: 'update', data: '')); +``` + #### Streamed Downloads From 701d69c6eb07162a584660ee91f0e1726e3f7bd7 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Tue, 4 Mar 2025 16:55:31 -0500 Subject: [PATCH 2062/2609] Add documentation for `Context::scope()` (#10214) * Update context.md * Update context.md * formatting --------- Co-authored-by: Taylor Otwell --- context.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/context.md b/context.md index 8c95bde587a..5311eac076e 100644 --- a/context.md +++ b/context.md @@ -152,6 +152,43 @@ Context::when( ); ``` + +#### Scoped Context + +The `scope` method provides a way to temporarily modify the context during the execution of a given callback and restore the context to its original state when the callback finishes executing. Additionally, you can pass extra data that should be merged into the context (as the second and third arguments) while the closure executes. + +```php +use Illuminate\Support\Facades\Context; +use Illuminate\Support\Facades\Log; + +Context::add('trace_id', 'abc-999'); +Context::addHidden('user_id', 123); + +Context::scope( + function () { + Context::add('action', 'adding_friend'); + + $userId = Context::getHidden('user_id'); + + Log::debug("Adding user [{$userId}] to friends list."); + // Adding user [987] to friends list. {"trace_id":"abc-999","user_name":"taylor_otwell","action":"adding_friend"} + }, + data: ['user_name' => 'taylor_otwell'], + hidden: ['user_id' => 987], +); + +Context::all(); +// [] + +Context::allHidden(); +// [ +// 'user_id' => 123, +// ] +``` + +> [!WARNING] +> If an object within the context is modified inside the scoped closure, that mutation will be reflected outside of the scope. + ### Stacks From 2251911b930ea8344313f10f6d4789b2eea1e473 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Wed, 5 Mar 2025 17:39:38 +0200 Subject: [PATCH 2063/2609] Ensuring logical consistency (#10228) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 45009c23ce8..c3a02d7b2a5 100644 --- a/validation.md +++ b/validation.md @@ -2316,7 +2316,7 @@ use Illuminate\Support\Facades\Validator; $validator = Validator::make($request->all(), [ 'email' => 'required|email', - 'games' => 'required|numeric', + 'games' => 'required|integer|min:0', ]); ``` From 5f3897d26b9f374801d7010ffd4330708a2a8d82 Mon Sep 17 00:00:00 2001 From: Pandi Selvam M <84308225+pandiselvamm@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:15:17 +0530 Subject: [PATCH 2064/2609] Feat: Add Docs for Array Partition (#10227) * Feat: Add Docs for Array Partition * Final updates * PR comments were resolved --------- Co-authored-by: Pandi Selvam --- helpers.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/helpers.md b/helpers.md index 161a54645d4..a4e8c759726 100644 --- a/helpers.md +++ b/helpers.md @@ -60,6 +60,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::mapSpread](#method-array-map-spread) [Arr::mapWithKeys](#method-array-map-with-keys) [Arr::only](#method-array-only) +[Arr::partition](#method-array-partition) [Arr::pluck](#method-array-pluck) [Arr::prepend](#method-array-prepend) [Arr::prependKeysWith](#method-array-prependkeyswith) @@ -681,6 +682,31 @@ $slice = Arr::only($array, ['name', 'price']); // ['name' => 'Desk', 'price' => 100] ``` + +#### `Arr::partition()` {.collection-method} + +The `Arr::partition` method may be combined with PHP array destructuring to separate elements that pass a given truth test from those that do not: + +```php + #### `Arr::pluck()` {.collection-method} From 6a9173a4693675c9d14fbad25bd645d2af1ac7e3 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 6 Mar 2025 00:03:51 +0200 Subject: [PATCH 2065/2609] Missing semi-colon in validation.md (#10230) --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index c3a02d7b2a5..3d2214841f7 100644 --- a/validation.md +++ b/validation.md @@ -2728,7 +2728,7 @@ If necessary, you may provide placeholder replacements and the preferred languag ```php $fail('validation.location')->translate([ 'value' => $this->value, -], 'fr') +], 'fr'); ``` #### Accessing Additional Data From bfcd66a60a6c42145994e3a9cf9e439f8a3a70a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Nicolau=20Domingos?= <100499703+JsExpertCoder@users.noreply.github.com> Date: Thu, 6 Mar 2025 19:53:09 +0100 Subject: [PATCH 2066/2609] fix: remove unnecessary code blocks in file storage documentation (#10233) --- filesystem.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/filesystem.md b/filesystem.md index 0a586523bcf..dd00b56aa24 100644 --- a/filesystem.md +++ b/filesystem.md @@ -687,8 +687,6 @@ $path = $request->file('avatar')->storePubliclyAs( When using the `local` driver, `public` [visibility](#file-visibility) translates to `0755` permissions for directories and `0644` permissions for files. You can modify the permissions mappings in your application's `filesystems` configuration file: ```php - -``````php 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), @@ -712,8 +710,6 @@ When using the `local` driver, `public` [visibility](#file-visibility) translate The `delete` method accepts a single filename or an array of files to delete: ```php - -``````php use Illuminate\Support\Facades\Storage; Storage::delete('file.jpg'); @@ -724,8 +720,6 @@ Storage::delete(['file.jpg', 'file2.jpg']); If necessary, you may specify the disk that the file should be deleted from: ```php - -``````php use Illuminate\Support\Facades\Storage; Storage::disk('s3')->delete('path/file.jpg'); From e4edae63035855c6fb415dd01ca3df7a47627aa1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 6 Mar 2025 13:17:40 -0600 Subject: [PATCH 2067/2609] inertia ssr --- starter-kits.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/starter-kits.md b/starter-kits.md index a37e14e23e8..6320c72432c 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -11,6 +11,7 @@ - [Vue](#vue-customization) - [Livewire](#livewire-customization) - [WorkOS AuthKit Authentication](#workos) +- [Inertia SSR](#inertia-ssr) - [Frequently Asked Questions](#faqs) @@ -305,6 +306,21 @@ When using a WorkOS powered starter kit, we recommend that you disable "Email + In addition, we recommend that you configure your WorkOS AuthKit session inactivity timeout to match your Laravel application's configured session timeout threshold, which is typically two hours. + +### Inertia SSR + +The React and Vue starter kits are compatible with Inertia's [server-side rendering](https://inertiajs.com/server-side-rendering) capabilities. To build an Inertia SSR compatible bundle for your application, run the `build:ssr` command: + +```shell +npm run build:ssr +``` + +For convenience, a `composer dev:ssr` command is also available. This command will start the Laravel development server and Inertia SSR server after building an SSR compatible bundle for your application, allowing you to test your application locally using Inertia's server-side rendering engine: + +```shell +composer dev:ssr +``` + ### Frequently Asked Questions From 87931acc55274d7147fa9113a3b0fea722f908d6 Mon Sep 17 00:00:00 2001 From: mohammadrasoulasghari <112411294+mohammadrasoulasghari@users.noreply.github.com> Date: Fri, 7 Mar 2025 18:43:43 +0330 Subject: [PATCH 2068/2609] Fix API documentation URLs in Routing section (#10235) - Updated URL format from https://laravel.com/api/{{version}}/ to https://api.laravel.com/docs/{{version}}/ - Fixed broken link in "Accessing the Current Route" section that was returning 404 --- routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.md b/routing.md index 8b8935f3d1e..c37c2b11f66 100644 --- a/routing.md +++ b/routing.md @@ -1008,7 +1008,7 @@ $name = Route::currentRouteName(); // string $action = Route::currentRouteAction(); // string ``` -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. +You may refer to the API documentation for both the [underlying class of the Route facade](https://api.laravel.com/docs/{{version}}/Illuminate/Routing/Router.html) and [Route instance](https://api.laravel.com/docs/{{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) From 4dbcf976dfae14c8ee6136772ae5124d52859ea9 Mon Sep 17 00:00:00 2001 From: Kjell Kind <33377566+kachelle@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:19:21 +0100 Subject: [PATCH 2069/2609] Update cache.md (#10234) more explicit info about Cache::lock()->block() --- cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.md b/cache.md index 4bc9aad60b3..d43cd9d3e30 100644 --- a/cache.md +++ b/cache.md @@ -406,7 +406,7 @@ The example above may be simplified by passing a closure to the `block` method. ```php Cache::lock('foo', 10)->block(5, function () { - // Lock acquired after waiting a maximum of 5 seconds... + // Lock acquired for 10 seconds after waiting a maximum of 5 seconds... }); ``` From b9e044e4cc6093a01adf6f3b6de43faf4c7e9b5a Mon Sep 17 00:00:00 2001 From: Davendra Naraine Date: Fri, 7 Mar 2025 11:25:06 -0400 Subject: [PATCH 2070/2609] add documentation for community maintained starter kits (#10236) * add documentation for community maintained starter kits * add anchor tag * update section title * Update starter-kits.md * Update starter-kits.md --------- Co-authored-by: Taylor Otwell --- starter-kits.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/starter-kits.md b/starter-kits.md index 6320c72432c..5f00183204f 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -12,6 +12,7 @@ - [Livewire](#livewire-customization) - [WorkOS AuthKit Authentication](#workos) - [Inertia SSR](#inertia-ssr) +- [Community Maintained Starter Kits](#community-maintained-starter-kits) - [Frequently Asked Questions](#faqs) @@ -321,6 +322,20 @@ For convenience, a `composer dev:ssr` command is also available. This command wi composer dev:ssr ``` + +### Community Maintained Starter Kits + +When creating a new Laravel application using the Laravel installer, you may provide any community maintained starter kit available on Packagist to the `--using` flag: + +```shell +laravel new my-app --using=example/starter-kit +``` + + +#### Creating Starter Kits + +To ensure your starter kit is available to others, you will need to publish it to [Packagist](https://packagist.org). Your starter kit should define its required environment variables in its `.env.example` file, and any necessary post-installation commands should be listed in the `post-create-project-cmd` array of the starter kit's `composer.json` file. + ### Frequently Asked Questions From b9dc15977f7d1b4d43ac0803e57f5b2f7d63af08 Mon Sep 17 00:00:00 2001 From: Hirohisa Kawase Date: Tue, 11 Mar 2025 02:10:42 +0900 Subject: [PATCH 2071/2609] Fix markdown formatting. (#10241) --- dusk.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/dusk.md b/dusk.md index 09c26e14acd..bc05a58997c 100644 --- a/dusk.md +++ b/dusk.md @@ -64,7 +64,7 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome composer require laravel/dusk --dev ``` -> [!WARNING] +> [!WARNING] > If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operating system: @@ -75,7 +75,7 @@ php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. -> [!NOTE] +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -97,7 +97,7 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> [!WARNING] +> [!WARNING] > Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. @@ -185,7 +185,7 @@ class ExampleTest extends DuskTestCase } ``` -> [!WARNING] +> [!WARNING] > SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. @@ -224,7 +224,7 @@ class ExampleTest extends DuskTestCase By default, this trait will truncate all tables except the `migrations` table. If you would like to customize the tables that should be truncated, you may define a `$tablesToTruncate` property on your test class: -> [!NOTE] +> [!NOTE] > If you are using Pest, you should define properties or methods on the base `DuskTestCase` class or on any class your test file extends. ```php @@ -299,7 +299,7 @@ The `dusk` command accepts any argument that is normally accepted by the Pest / php artisan dusk --group=foo ``` -> [!NOTE] +> [!NOTE] > If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). @@ -548,7 +548,7 @@ $this->browse(function (Browser $browser) { }); ``` -> [!WARNING] +> [!WARNING] > After using the `loginAs` method, the user session will be maintained for all tests within the file. @@ -770,9 +770,7 @@ You may select a random option by omitting the second argument: $browser->select('size'); ``` -By providing an array as the second```php - -``` argument to the `select` method, you can instruct the method to select multiple options: +By providing an array as the second argument to the `select` method, you can instruct the method to select multiple options: ```php $browser->select('categories', ['Art', 'Music']); @@ -811,7 +809,7 @@ The `attach` method may be used to attach a file to a `file` input element. Like $browser->attach('photo', __DIR__.'/photos/mountains.png'); ``` -> [!WARNING] +> [!WARNING] > The attach function requires the `Zip` PHP extension to be installed and enabled on your server. @@ -850,7 +848,7 @@ if ($browser->seeLink($linkText)) { } ``` -> [!WARNING] +> [!WARNING] > These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. @@ -868,7 +866,7 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" $browser->keys('.app', ['{command}', 'j']); ``` -> [!NOTE] +> [!NOTE] > All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). @@ -2510,7 +2508,7 @@ class ExampleTest extends DuskTestCase ## Continuous Integration -> [!WARNING] +> [!WARNING] > Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. From bc982a51ed9ee579155837eebc5de1dc8cbf80ac Mon Sep 17 00:00:00 2001 From: Hirohisa Kawase Date: Tue, 11 Mar 2025 02:12:13 +0900 Subject: [PATCH 2072/2609] Fix markdown formatting. (#10241) From f86194af14d4c44df7668e0d455ae1ceefc388f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3zsa=20Zolt=C3=A1n?= <67325669+rozsazoltan@users.noreply.github.com> Date: Mon, 10 Mar 2025 18:19:26 +0100 Subject: [PATCH 2073/2609] [12.x] Mention different Vite integration for Tailwind v3 vs v4 (#10240) * chore: mention different Vite integration for Tailwind v3 vs v4 * chore: added tailwind v3 related url for v3 description * formatting --------- Co-authored-by: Taylor Otwell --- vite.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/vite.md b/vite.md index 7ab83151fd0..4d478f9fe39 100644 --- a/vite.md +++ b/vite.md @@ -445,19 +445,16 @@ The following example demonstrates how Vite will treat relative and absolute URL ## Working With Stylesheets -You can learn more about Vite's CSS support within the [Vite documentation](https://vitejs.dev/guide/features.html#css). If you are using PostCSS plugins such as [Tailwind](https://tailwindcss.com), you may create a `postcss.config.js` file in the root of your project and Vite will automatically apply it: +> [!NOTE] +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Tailwind and Vite configuration. Or, if you would like to use Tailwind and Laravel without using one of our starter kits, check out [Tailwind's installation guide for Laravel](https://tailwindcss.com/docs/guides/laravel). -```js -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; +All Laravel applications already include Tailwind and a properly configured `vite.config.js` file. So, you only need to start the Vite development server or run the `dev` Composer command, which will start both the Laravel and Vite development servers: + +```shell +composer run dev ``` -> [!NOTE] -> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Tailwind, PostCSS, and Vite configuration. Or, if you would like to use Tailwind and Laravel without using one of our starter kits, check out [Tailwind's installation guide for Laravel](https://tailwindcss.com/docs/guides/laravel). +Your application's CSS may be placed within the `resources/css/app.css` file. ## Working With Blade and Routes From 87fe8fc2d9fd82d74b6385564ace459a63226f10 Mon Sep 17 00:00:00 2001 From: Mario Guillen Date: Mon, 10 Mar 2025 11:23:03 -0600 Subject: [PATCH 2074/2609] Remove unnecessary fenced code blocks in strings.md (#10239) --- strings.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/strings.md b/strings.md index 2a11cf35708..962cd12bbbf 100644 --- a/strings.md +++ b/strings.md @@ -1717,8 +1717,6 @@ Str::wordCount('Hello, world!'); // 2 The `Str::wordWrap` method wraps a string to a given number of characters: ```php - -``````php use Illuminate\Support\Str; $text = "The quick brown fox jumped over the lazy dog." @@ -1738,8 +1736,6 @@ dog. The `Str::words` method limits the number of words in a string. An additional string may be passed to this method via its third argument to specify which string should be appended to the end of the truncated string: ```php - -``````php use Illuminate\Support\Str; return Str::words('Perfectly balanced, as all things should be.', 3, ' >>>'); @@ -1753,8 +1749,6 @@ return Str::words('Perfectly balanced, as all things should be.', 3, ' >>>'); The `Str::wrap` method wraps the given string with an additional string or pair of strings: ```php - -``````php use Illuminate\Support\Str; Str::wrap('Laravel', '"'); @@ -1772,8 +1766,6 @@ Str::wrap('is', before: 'This ', after: ' Laravel!'); The `str` function returns a new `Illuminate\Support\Stringable` instance of the given string. This function is equivalent to the `Str::of` method: ```php - -``````php $string = str('Taylor')->append(' Otwell'); // 'Taylor Otwell' @@ -1782,8 +1774,6 @@ $string = str('Taylor')->append(' Otwell'); If no argument is provided to the `str` function, the function returns an instance of `Illuminate\Support\Str`: ```php - -``````php $snake = str()->snake('FooBar'); // 'foo_bar' @@ -1795,8 +1785,6 @@ $snake = str()->snake('FooBar'); The `trans` function translates the given translation key using your [language files](/docs/{{version}}/localization): ```php - -``````php echo trans('messages.welcome'); ``` From 607adcf48240987e6190c347df2d1b3b60ca915b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3zsa=20Zolt=C3=A1n?= <67325669+rozsazoltan@users.noreply.github.com> Date: Mon, 10 Mar 2025 18:26:49 +0100 Subject: [PATCH 2075/2609] [12.x] Rework pagination Tailwind JIT description; add v4 syntax (#10238) * chore: rework pagination Tailwind JIT description; add Tailwind v4 syntax In Tailwind CSS v3, JIT mode is enabled by default (which was introduced in v2). I believe that Laravel 12.x is far enough from Tailwind CSS v2 that we can drop this naming convention and simply refer to it as v3. * https://v3.tailwindcss.com/docs/upgrade-guide#migrating-to-the-jit-engine The release of Tailwind CSS v4 introduced many breaking changes. Automatic source detection was introduced, but unfortunately, the vendor folder was excluded due to the .gitignore file. Adding the source now works with a different syntax compared to v3. * https://tailwindcss.com/docs/detecting-classes-in-source-files#which-files-are-scanned * https://tailwindcss.com/docs/functions-and-directives#source-directive * wip: use " instead of ' * Update pagination.md --------- Co-authored-by: Taylor Otwell --- pagination.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/pagination.md b/pagination.md index cf83aa612de..63af7e5176c 100644 --- a/pagination.md +++ b/pagination.md @@ -22,18 +22,15 @@ In other frameworks, pagination can be very painful. We hope Laravel's approach 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. - -#### Tailwind JIT - -If you are using Laravel's default Tailwind pagination views and the Tailwind JIT engine, you should ensure your application's `tailwind.config.js` file's `content` key references Laravel's pagination views so that their Tailwind classes are not purged: - -```js -content: [ - './resources/**/*.blade.php', - './resources/**/*.js', - './resources/**/*.vue', - './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', -], + +#### Tailwind + +If you are using Laravel's default Tailwind pagination views with Tailwind 4.x, your application's `resources/css/app.css` file will already be properly configured to `@source` Laravel's pagination views: + +```css +@import "https://github.com/tailwindcss"; + +@source "./../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views"; ``` From 2fd7197e878eabd66544c86e26049142a8a4e7f2 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Wed, 12 Mar 2025 16:21:35 +0200 Subject: [PATCH 2076/2609] Update Logging Documentation to Use handler_with (#10242) --- logging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logging.md b/logging.md index e9bd266c85d..67d9e25643c 100644 --- a/logging.md +++ b/logging.md @@ -422,7 +422,7 @@ When using the `monolog` driver, the `handler` configuration option is used to s 'logentries' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\SyslogUdpHandler::class, - 'with' => [ + 'handler_with' => [ 'host' => 'my.logentries.internal.datahubhost.company.com', 'port' => '10000', ], @@ -466,7 +466,7 @@ If you would like to customize the processors for a `monolog` driver, add a `pro 'memory' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\StreamHandler::class, - 'with' => [ + 'handler_with' => [ 'stream' => 'php://stderr', ], 'processors' => [ From e181b1ca8d3b04d8a198c3af8bfa69cab752c80a Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 12 Mar 2025 15:40:56 +0100 Subject: [PATCH 2077/2609] [12.x] Document `ensureNotTimedOut` (#10231) * [12.x] Document `ensureNotTimedOut` * formatting --------- Co-authored-by: Taylor Otwell --- processes.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/processes.md b/processes.md index eefc6acf766..a6565b79a52 100644 --- a/processes.md +++ b/processes.md @@ -8,6 +8,7 @@ - [Asynchronous Processes](#asynchronous-processes) - [Process IDs and Signals](#process-ids-and-signals) - [Asynchronous Process Output](#asynchronous-process-output) + - [Asynchronous Process Timeouts](#asynchronous-process-timeouts) - [Concurrent Processes](#concurrent-processes) - [Naming Pool Processes](#naming-pool-processes) - [Pool Process IDs and Signals](#pool-process-ids-and-signals) @@ -301,6 +302,23 @@ $process->waitUntil(function (string $type, string $output) { }); ``` + +### Asynchronous Process Timeouts + +While an asynchronous process is running, you may verify that the process has not timed out using the `ensureNotTimedOut` method. This method will throw a [timeout exception](#timeouts) if the process has timed out: + +```php +$process = Process::timeout(120)->start('bash import.sh'); + +while ($process->running()) { + $process->ensureNotTimedOut(); + + // ... + + sleep(1); +} +``` + ## Concurrent Processes From 0c27677d57f7b565e4ddca146257d71f30378a0c Mon Sep 17 00:00:00 2001 From: Caleb White Date: Thu, 13 Mar 2025 09:39:20 -0500 Subject: [PATCH 2078/2609] [12.x] docs: document container respecting property default values (#10250) * docs: document container respecting property default values See: https://github.com/laravel/framework/pull/53522 * formatting * Formatting --------- Co-authored-by: Taylor Otwell --- upgrade.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/upgrade.md b/upgrade.md index a7aa7694292..0e609d947ab 100644 --- a/upgrade.md +++ b/upgrade.md @@ -28,6 +28,7 @@ - [Carbon 3](#carbon-3) - [Concurrency Result Index Mapping](#concurrency-result-index-mapping) +- [Container Class Dependency Resolution](#container-class-dependency-resolution) - [Image Validation Now Excludes SVGs](#image-validation) - [Multi-Schema Database Inspecting](#multi-schema-database-inspecting) - [Nested Array Request Merging](#nested-array-request-merging) @@ -109,6 +110,31 @@ $result = Concurrency::run([ // ['task-1' => 2, 'task-2' => 4] ``` + +### Container + + +#### Container Class Dependency Resolution + +**Likelihood Of Impact: Low** + +The dependency injection container now respects the default value of class properties when resolving a class instance. If you were previously relying on the container to resolve a class instance without the default value, you may need to adjust your application to account for this new behavior: + +```php +class Example +{ + public function __construct(public ?Carbon $date = null) {} +} + +$example = resolve(Example::class); + +// <= 11.x +$example->date instanceof Carbon; + +// >= 12.x +$example->date === null; +``` + ### Database From d914fa3bf7683117b068484cdb5d94d56d24438b Mon Sep 17 00:00:00 2001 From: Robert Boes <2871897+RobertBoes@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:49:14 +0100 Subject: [PATCH 2079/2609] Fix strings whenEmpty code example (#10249) --- strings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings.md b/strings.md index 962cd12bbbf..43dc92adba9 100644 --- a/strings.md +++ b/strings.md @@ -3393,8 +3393,8 @@ The `whenEmpty` method invokes the given closure if the string is empty. If the use Illuminate\Support\Str; use Illuminate\Support\Stringable; -$string = Str::of(' ')->whenEmpty(function (Stringable $string) { - return $string->trim()->prepend('Laravel'); +$string = Str::of(' ')->trim()->whenEmpty(function (Stringable $string) { + return $string->prepend('Laravel'); }); // 'Laravel' From 16ff3475279f2aaef773373a4b159de6ac195ffd Mon Sep 17 00:00:00 2001 From: inmanturbo <47095624+inmanturbo@users.noreply.github.com> Date: Thu, 13 Mar 2025 10:53:51 -0400 Subject: [PATCH 2080/2609] Document 'only' and 'except' for blade attributes (#10248) * Document 'only' and 'except' for blade attributes * Update blade.md --------- Co-authored-by: Taylor Otwell --- blade.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/blade.md b/blade.md index 6d95e614428..082402b9986 100644 --- a/blade.md +++ b/blade.md @@ -1156,6 +1156,18 @@ You may retrieve a specific attribute's value using the `get` method: {{ $attributes->get('class') }} ``` +The `only` method may be used to retrieve only the attributes with the given keys: + +```blade +{{ $attributes->only(['class']) }} +``` + +The `except` method may be used to retrieve all attributes except those with the given keys: + +```blade +{{ $attributes->except(['class']) }} +``` + ### Reserved Keywords From 5253514d16075cc11710d2fe2e088555082ce223 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 13 Mar 2025 17:09:43 +0200 Subject: [PATCH 2081/2609] Update Logging Documentation to Use handler_with (#10247) --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index 67d9e25643c..970cd83bb5a 100644 --- a/logging.md +++ b/logging.md @@ -416,7 +416,7 @@ class CustomizeFormatter Monolog has a variety of [available handlers](https://github.com/Seldaek/monolog/tree/main/src/Monolog/Handler) and Laravel does not include a built-in channel for each one. In some cases, you may wish to create a custom channel that is merely an instance of a specific Monolog handler that does not have a corresponding Laravel log driver. These channels can be easily created using the `monolog` driver. -When using the `monolog` driver, the `handler` configuration option is used to specify which handler will be instantiated. Optionally, any constructor parameters the handler needs may be specified using the `with` configuration option: +When using the `monolog` driver, the `handler` configuration option is used to specify which handler will be instantiated. Optionally, any constructor parameters the handler needs may be specified using the `handler_with` configuration option: ```php 'logentries' => [ From 0cc3f9385d9155faf496d5b4e569915780468cbd Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 13 Mar 2025 16:16:23 +0100 Subject: [PATCH 2082/2609] [12.x] Document `increment` and `decrement` (#10245) * [12.x] Document `increment` and `decrement` * formatting * Update context.md --------- Co-authored-by: Taylor Otwell --- context.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/context.md b/context.md index 5311eac076e..c20297aaa1a 100644 --- a/context.md +++ b/context.md @@ -136,6 +136,16 @@ Context::get('key'); // "first" ``` +Context also provides convenient methods for incrementing or decrementing a given key. Both of these methods accept at least one argument: the key to track. A second argument may be provided to specify the amount by which the key should be incremented or decremented: + +```php +Context::increment('records_added'); +Context::increment('records_added', 5); + +Context::decrement('records_added'); +Context::decrement('records_added', 5); +``` + #### Conditional Context From 8837a4b5cd43de5cb25895d6fc91308051fc99b4 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 13 Mar 2025 17:31:21 +0200 Subject: [PATCH 2083/2609] Update the pagination docs to reflect the actual implementation (#10251) --- pagination.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pagination.md b/pagination.md index 63af7e5176c..9ec61d5f255 100644 --- a/pagination.md +++ b/pagination.md @@ -28,9 +28,9 @@ By default, the HTML generated by the paginator is compatible with the [Tailwind If you are using Laravel's default Tailwind pagination views with Tailwind 4.x, your application's `resources/css/app.css` file will already be properly configured to `@source` Laravel's pagination views: ```css -@import "https://github.com/tailwindcss"; +@import 'https://github.com/tailwindcss'; -@source "./../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views"; +@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; ``` From 9a84c2f1d643f40c655d7d6bc5f65935e35494a4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 13 Mar 2025 10:44:36 -0500 Subject: [PATCH 2084/2609] document has one through --- eloquent-relationships.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index f42fb93d84a..1f539a87722 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -468,6 +468,15 @@ public function largestOrder(): HasOne } ``` +You may also use the `one` method to convert `HasManyThrough` relationships to `HasOneThrough` relationships: + +```php +public function latestDeployment(): HasOneThrough +{ + return $this->deployments()->one()->latestOfMany(); +} +``` + #### Advanced Has One of Many Relationships From 9b39f6e1b17658b10555e56f258131fff514727d Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 14 Mar 2025 19:24:31 +0200 Subject: [PATCH 2085/2609] Updating the docs to reflect the actual generated comment (#10252) --- errors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/errors.md b/errors.md index 29210a34d41..64f7c9f84d6 100644 --- a/errors.md +++ b/errors.md @@ -310,7 +310,7 @@ class InvalidOrderException extends Exception } /** - * Render the exception into an HTTP response. + * Render the exception as an HTTP response. */ public function render(Request $request): Response { @@ -323,7 +323,7 @@ If your exception extends an exception that is already renderable, such as a bui ```php /** - * Render the exception into an HTTP response. + * Render the exception as an HTTP response. */ public function render(Request $request): Response|bool { From 2ae2817441800fe98c95349fd036499031539795 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 16 Mar 2025 14:06:37 -0500 Subject: [PATCH 2086/2609] wip --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 53cc96bd39e..f01fae8f3e0 100644 --- a/releases.md +++ b/releases.md @@ -9,7 +9,7 @@ Laravel and its other first-party packages follow [Semantic Versioning](https://semver.org). Major framework releases are released every year (~Q1), while minor and patch releases may be released as often as every week. Minor and patch releases should **never** contain breaking changes. -When referencing the Laravel framework or its components from your application or package, you should always use a version constraint such as `^11.0`, since major releases of Laravel do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less. +When referencing the Laravel framework or its components from your application or package, you should always use a version constraint such as `^12.0`, since major releases of Laravel do include breaking changes. However, we strive to always ensure you may update to a new major release in one day or less. #### Named Arguments From 594fe1b68b8a1f8c4ac26d4133d93bacac6d651d Mon Sep 17 00:00:00 2001 From: John Rivs Date: Mon, 17 Mar 2025 00:51:15 +0100 Subject: [PATCH 2087/2609] Fix typo in a Precognition for Alpine.js example (#10255) Co-authored-by: John Rivs --- precognition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precognition.md b/precognition.md index 1af5cfa8110..062602f4cb9 100644 --- a/precognition.md +++ b/precognition.md @@ -580,7 +580,7 @@ Alternatively, if you would like to submit the form via XHR you may use the form submit() { this.form.submit() .then(response => { - form.reset(); + this.form.reset(); alert('User created.') }) From 690f920c064a205e7d2bc5c57034c0ca896700ae Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 17 Mar 2025 01:56:41 +0200 Subject: [PATCH 2088/2609] Consistency with Laravel's best practices (#10254) --- authorization.md | 4 ---- controllers.md | 1 - database.md | 1 - eloquent.md | 1 - events.md | 1 - facades.md | 1 - filesystem.md | 1 - frontend.md | 1 - logging.md | 1 - mail.md | 1 - pagination.md | 1 - queues.md | 5 ----- redis.md | 1 - 13 files changed, 20 deletions(-) diff --git a/authorization.md b/authorization.md index beb771c748e..0e498b06823 100644 --- a/authorization.md +++ b/authorization.md @@ -87,7 +87,6 @@ To authorize an action using gates, you should use the `allows` or `denies` meth namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -542,7 +541,6 @@ The `App\Models\User` model that is included with your Laravel application inclu namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -577,7 +575,6 @@ Remember, some actions may correspond to policy methods like `create` that do no namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -612,7 +609,6 @@ Like the `can` method, this method accepts the name of the action you wish to au namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; diff --git a/controllers.md b/controllers.md index 6c42ab10b66..ee927f8d5de 100644 --- a/controllers.md +++ b/controllers.md @@ -125,7 +125,6 @@ Or, you may find it convenient to specify middleware within your controller clas namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use Illuminate\Routing\Controllers\HasMiddleware; use Illuminate\Routing\Controllers\Middleware; diff --git a/database.md b/database.md index 37a0ce161aa..a12621c9ce9 100644 --- a/database.md +++ b/database.md @@ -141,7 +141,6 @@ To run a basic SELECT query, you may use the `select` method on the `DB` facade: namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\DB; use Illuminate\View\View; diff --git a/eloquent.md b/eloquent.md index 643b2d421d8..ac745a2d3fe 100644 --- a/eloquent.md +++ b/eloquent.md @@ -730,7 +730,6 @@ Of course, when using Eloquent, we don't only need to retrieve models from the d namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\Flight; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; diff --git a/events.md b/events.md index 4f98b892cef..2c6d6f34ce7 100644 --- a/events.md +++ b/events.md @@ -586,7 +586,6 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th namespace App\Http\Controllers; use App\Events\OrderShipped; -use App\Http\Controllers\Controller; use App\Models\Order; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; diff --git a/facades.md b/facades.md index 09382f5257f..9e73818a789 100644 --- a/facades.md +++ b/facades.md @@ -158,7 +158,6 @@ The `Facade` base class makes use of the `__callStatic()` magic-method to defer namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Cache; use Illuminate\View\View; diff --git a/filesystem.md b/filesystem.md index dd00b56aa24..d95f3bb5583 100644 --- a/filesystem.md +++ b/filesystem.md @@ -559,7 +559,6 @@ In web applications, one of the most common use-cases for storing files is stori namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use Illuminate\Http\Request; class UserAvatarController extends Controller diff --git a/frontend.md b/frontend.md index 104a6698971..7656f0e741d 100644 --- a/frontend.md +++ b/frontend.md @@ -125,7 +125,6 @@ After installing Inertia into your Laravel application, you will write routes an namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\User; use Inertia\Inertia; use Inertia\Response; diff --git a/logging.md b/logging.md index 970cd83bb5a..d434041ebed 100644 --- a/logging.md +++ b/logging.md @@ -213,7 +213,6 @@ You may call any of these methods to log a message for the corresponding level. namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Support\Facades\Log; use Illuminate\View\View; diff --git a/mail.md b/mail.md index b70b444e831..d8ce3a7c8cf 100644 --- a/mail.md +++ b/mail.md @@ -920,7 +920,6 @@ To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/ namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Mail\OrderShipped; use App\Models\Order; use Illuminate\Http\RedirectResponse; diff --git a/pagination.md b/pagination.md index 9ec61d5f255..fba4710e17c 100644 --- a/pagination.md +++ b/pagination.md @@ -48,7 +48,6 @@ In this example, the only argument passed to the `paginate` method is the number namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\DB; use Illuminate\View\View; diff --git a/queues.md b/queues.md index 49897fb118e..5763e9f888d 100644 --- a/queues.md +++ b/queues.md @@ -793,7 +793,6 @@ Once you have written your job class, you may dispatch it using the `dispatch` m namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\RedirectResponse; @@ -837,7 +836,6 @@ If you would like to specify that a job should not be immediately available for namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\RedirectResponse; @@ -903,7 +901,6 @@ If you would like to dispatch a job immediately (synchronously), you may use the namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\RedirectResponse; @@ -1067,7 +1064,6 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\RedirectResponse; @@ -1125,7 +1121,6 @@ If your application interacts with multiple queue connections, you may specify w namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; use App\Models\Podcast; use Illuminate\Http\RedirectResponse; diff --git a/redis.md b/redis.md index ecd3fde4c57..dc31256ae7f 100644 --- a/redis.md +++ b/redis.md @@ -245,7 +245,6 @@ You may interact with Redis by calling various methods on the `Redis` [facade](/ namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Redis; use Illuminate\View\View; From 4223ed3fb9af969df6803cc81a21d5e803236ae0 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 17 Mar 2025 22:15:14 +0200 Subject: [PATCH 2089/2609] Fix markdown formatting (#10257) --- artisan.md | 14 ++++----- authentication.md | 12 ++++---- authorization.md | 6 ++-- billing.md | 70 +++++++++++++++++++++---------------------- blade.md | 26 ++++++++-------- broadcasting.md | 24 +++++++-------- cache.md | 10 +++---- cashier-paddle.md | 30 +++++++++---------- collections.md | 40 ++++++++++++------------- concurrency.md | 2 +- configuration.md | 14 ++++----- context.md | 6 ++-- controllers.md | 8 ++--- csrf.md | 4 +-- database-testing.md | 2 +- database.md | 6 ++-- deployment.md | 4 +-- eloquent-factories.md | 4 +-- 18 files changed, 141 insertions(+), 141 deletions(-) diff --git a/artisan.md b/artisan.md index f2c40fc96a3..41b080c86be 100644 --- a/artisan.md +++ b/artisan.md @@ -62,7 +62,7 @@ All Laravel applications include Tinker by default. However, you may install Tin composer require laravel/tinker ``` -> [!NOTE] +> [!NOTE] > Looking for hot reloading, multiline code editing, and autocompletion when interacting with your Laravel application? Check out [Tinkerwell](https://tinkerwell.app)! @@ -80,7 +80,7 @@ You can publish Tinker's configuration file using the `vendor:publish` command: php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider" ``` -> [!WARNING] +> [!WARNING] > 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. @@ -161,7 +161,7 @@ class SendEmails extends Command } ``` -> [!NOTE] +> [!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. @@ -224,7 +224,7 @@ Artisan::command('mail:send {user}', function (string $user) { ### Isolatable Commands -> [!WARNING] +> [!WARNING] > To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. Sometimes you may wish to ensure that only one instance of a command can run at a time. To accomplish this, you may implement the `Illuminate\Contracts\Console\Isolatable` interface on your command class: @@ -499,7 +499,7 @@ return [ ]; ``` -> [!NOTE] +> [!NOTE] The comprehensive [Laravel Prompts](/docs/{{version}}/prompts) documentation includes additional information on the available prompts and their usage. If you wish to prompt the user to select or enter [options](#options), you may include prompts in your command's `handle` method. However, if you only wish to prompt the user when they have also been automatically prompted for missing arguments, then you may implement the `afterPromptingForMissingArguments` method: @@ -560,7 +560,7 @@ $options = $this->options(); ### Prompting for Input -> [!NOTE] +> [!NOTE] > [Laravel Prompts](/docs/{{version}}/prompts) is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. In addition to displaying output, you may also ask the user to provide input during the execution of your command. The `ask` method will prompt the user with the given question, accept their input, and then return the user's input back to your command: @@ -734,7 +734,7 @@ foreach ($users as $user) { $bar->finish(); ``` -> [!NOTE] +> [!NOTE] > For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/7.0/components/console/helpers/progressbar.html). diff --git a/authentication.md b/authentication.md index a06f281b202..cea323fada4 100644 --- a/authentication.md +++ b/authentication.md @@ -40,7 +40,7 @@ Providers define how users are retrieved from your persistent storage. Laravel s 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. -> [!NOTE] +> [!NOTE] > Guards and providers should not be confused with "roles" and "permissions". To learn more about authorizing user actions via permissions, please refer to the [authorization](/docs/{{version}}/authorization) documentation. @@ -110,7 +110,7 @@ And, if you would like to get started quickly, we are pleased to recommend [our ## Authentication Quickstart -> [!WARNING] +> [!WARNING] > 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). @@ -172,7 +172,7 @@ if (Auth::check()) { } ``` -> [!NOTE] +> [!NOTE] > Even though it is possible to determine if a user is authenticated using the `check` method, you will typically use a middleware to verify that the user is authenticated before allowing the user access to certain routes / controllers. To learn more about this, check out the documentation on [protecting routes](/docs/{{version}}/authentication#protecting-routes). @@ -218,7 +218,7 @@ Route::get('/flights', function () { If you are using one of our [application 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. -> [!NOTE] +> [!NOTE] > If you would like to rate limit other routes in your application, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). @@ -295,7 +295,7 @@ if (Auth::attempt([ } ``` -> [!WARNING] +> [!WARNING] > 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. The `attemptWhen` method, which receives a closure as its second argument, may be used to perform more extensive inspection of the potential user before actually authenticating the user. The closure receives the potential user and should return `true` or `false` to indicate if the user may be authenticated: @@ -519,7 +519,7 @@ When the `logoutOtherDevices` method is invoked, the user's other sessions will 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. -> [!NOTE] +> [!NOTE] > 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! diff --git a/authorization.md b/authorization.md index 0e498b06823..fe87667dea6 100644 --- a/authorization.md +++ b/authorization.md @@ -39,7 +39,7 @@ You do not need to choose between exclusively using gates or exclusively using p ### Writing Gates -> [!WARNING] +> [!WARNING] > 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\AppServiceProvider` 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. @@ -377,7 +377,7 @@ You may continue to define additional methods on the policy as needed for the va 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. -> [!NOTE] +> [!NOTE] > 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. @@ -525,7 +525,7 @@ public function before(User $user, string $ability): bool|null 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. -> [!WARNING] +> [!WARNING] > 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. diff --git a/billing.md b/billing.md index 162272322d3..5f66343a023 100644 --- a/billing.md +++ b/billing.md @@ -81,7 +81,7 @@ When upgrading to a new version of Cashier, it's important that you carefully review [the upgrade guide](https://github.com/laravel/cashier-stripe/blob/master/UPGRADE.md). -> [!WARNING] +> [!WARNING] > To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 15 utilizes Stripe API version `2023-10-16`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. @@ -115,7 +115,7 @@ php artisan vendor:publish --tag="cashier-config" Lastly, to ensure Cashier properly handles all Stripe events, remember to [configure Cashier's webhook handling](#handling-stripe-webhooks). -> [!WARNING] +> [!WARNING] > 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). @@ -150,7 +150,7 @@ public function boot(): void } ``` -> [!WARNING] +> [!WARNING] > 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. @@ -164,7 +164,7 @@ STRIPE_SECRET=your-stripe-secret STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret ``` -> [!WARNING] +> [!WARNING] > You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. @@ -182,7 +182,7 @@ In addition to configuring Cashier's currency, you may also specify a locale to CASHIER_CURRENCY_LOCALE=nl_BE ``` -> [!WARNING] +> [!WARNING] > In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. @@ -253,7 +253,7 @@ public function boot(): void ### Selling Products -> [!NOTE] +> [!NOTE] > Before utilizing Stripe Checkout, you should define Products with fixed prices in your Stripe dashboard. In addition, you should [configure Cashier's webhook handling](#handling-stripe-webhooks). Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Stripe Checkout](https://stripe.com/payments/checkout), you can easily build modern, robust payment integrations. @@ -346,7 +346,7 @@ Please refer to Stripe's documentation for more information on the [data contain ### Selling Subscriptions -> [!NOTE] +> [!NOTE] > Before utilizing Stripe Checkout, you should define Products with fixed prices in your Stripe dashboard. In addition, you should [configure Cashier's webhook handling](#handling-stripe-webhooks). Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Stripe Checkout](https://stripe.com/payments/checkout), you can easily build modern, robust payment integrations. @@ -456,7 +456,7 @@ Route::get('/billing', function (Request $request) { })->middleware(['auth'])->name('billing'); ``` -> [!NOTE] +> [!NOTE] > As long as you have configured Cashier's webhook handling, Cashier will automatically keep your application's Cashier-related database tables in sync by inspecting the incoming webhooks from Stripe. So, for example, when a user cancels their subscription via Stripe's Customer Billing Portal, Cashier will receive the corresponding webhook and mark the subscription as "canceled" in your application's database. @@ -717,7 +717,7 @@ cardButton.addEventListener('click', async (e) => { 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). -> [!NOTE] +> [!NOTE] > If you would like more information about Setup Intents and gathering customer payment details please [review this overview provided by Stripe](https://stripe.com/docs/payments/save-and-reuse#php). @@ -843,7 +843,7 @@ To sync your default payment method information with the customer's default paym $user->updateDefaultPaymentMethodFromStripe(); ``` -> [!WARNING] +> [!WARNING] > 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. @@ -855,7 +855,7 @@ To add a new payment method, you may call the `addPaymentMethod` method on the b $user->addPaymentMethod($paymentMethod); ``` -> [!NOTE] +> [!NOTE] > To learn how to retrieve payment method identifiers please review the [payment method storage documentation](#storing-payment-methods). @@ -885,7 +885,7 @@ By default, this method will delete payment methods of every type. To delete pay $user->deletePaymentMethods('sepa_debit'); ``` -> [!WARNING] +> [!WARNING] > If a user has an active subscription, your application should not allow them to delete their default payment method. @@ -914,7 +914,7 @@ The first argument passed to the `newSubscription` method should be the internal 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. -> [!WARNING] +> [!WARNING] > Passing a payment method identifier directly to the `create` subscription method will also automatically add it to the user's stored payment methods. @@ -1134,7 +1134,7 @@ if ($user->subscription('default')->recurring()) { } ``` -> [!WARNING] +> [!WARNING] > If a user has two subscriptions with the same type, the most recent subscription will always be returned by the `subscription` method. For example, a user might have two subscription records with the type of `default`; however, one of the subscriptions may be an old, expired subscription, while the other is the current, active subscription. The most recent subscription will always be returned while older subscriptions are kept in the database for historical review. @@ -1204,7 +1204,7 @@ public function register(): void } ``` -> [!WARNING] +> [!WARNING] > 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. @@ -1278,7 +1278,7 @@ $user->subscription('default')->noProrate()->swap('price_yearly'); For more information on subscription proration, consult the [Stripe documentation](https://stripe.com/docs/billing/subscriptions/prorations). -> [!WARNING] +> [!WARNING] > Executing the `noProrate` method before the `swapAndInvoice` method will have no effect on proration. An invoice will always be issued. @@ -1383,7 +1383,7 @@ You may remove prices from subscriptions using the `removePrice` method: $user->subscription('default')->removePrice('price_chat'); ``` -> [!WARNING] +> [!WARNING] > You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. @@ -1446,7 +1446,7 @@ $user->subscription('default')->decrementQuantity(3, 'price_chat'); $user->subscription('default')->updateQuantity(10, 'price_chat'); ``` -> [!WARNING] +> [!WARNING] > When a subscription has multiple prices the `stripe_price` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual price attributes, you should use the `items` relationship available on the `Subscription` model. @@ -1579,7 +1579,7 @@ $user->meters(); ### Subscription Taxes -> [!WARNING] +> [!WARNING] > Instead of calculating Tax Rates manually, you can [automatically calculate taxes using Stripe Tax](#tax-configuration) 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): @@ -1614,7 +1614,7 @@ public function priceTaxRates(): array } ``` -> [!WARNING] +> [!WARNING] > The `taxRates` method only applies to subscription charges. If you use Cashier to make "one-off" charges, you will need to manually specify the tax rate at that time. @@ -1643,7 +1643,7 @@ $user->isNotTaxExempt(); $user->reverseChargeApplies(); ``` -> [!WARNING] +> [!WARNING] > 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. @@ -1749,7 +1749,7 @@ Route::post('/user/subscribe', function (Request $request) { 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 price in Stripe. -> [!WARNING] +> [!WARNING] > If the customer's subscription is not canceled 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 that specifies when the trial period should end: @@ -1811,7 +1811,7 @@ $user = User::create([ ]); ``` -> [!WARNING] +> [!WARNING] > 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 billable model instance will return `true` if the current date is not past the value of `trial_ends_at`: @@ -1870,7 +1870,7 @@ $subscription->extendTrial( ## Handling Stripe Webhooks -> [!NOTE] +> [!NOTE] > 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 automatically registered by the Cashier service provider. This controller will handle all incoming webhook requests. @@ -1912,7 +1912,7 @@ After creation, the webhook will be immediately active. If you wish to create th php artisan cashier:webhook --disabled ``` -> [!WARNING] +> [!WARNING] > Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](#verifying-webhook-signatures) middleware. @@ -2012,7 +2012,7 @@ try { } ``` -> [!WARNING] +> [!WARNING] > The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. @@ -2052,7 +2052,7 @@ $user->invoiceFor('One Time Fee', 500); Although the `invoiceFor` method is available for you to use, it is recommended that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. -> [!WARNING] +> [!WARNING] > The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. @@ -2088,7 +2088,7 @@ Route::post('/pay', function (Request $request) { }); ``` -> [!WARNING] +> [!WARNING] > The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. @@ -2329,13 +2329,13 @@ Route::get('/charge-checkout', function (Request $request) { }); ``` -> [!WARNING] +> [!WARNING] > When using the `checkoutCharge` method, Stripe will always create a new product and price in your Stripe dashboard. Therefore, we recommend that you create the products up front in your Stripe dashboard and use the `checkout` method instead. ### Subscription Checkouts -> [!WARNING] +> [!WARNING] > Using Stripe Checkout for subscriptions requires you to enable the `customer.subscription.created` webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items. You may also use Stripe Checkout to initiate subscriptions. After defining your subscription with Cashier's subscription builder methods, you may call the `checkout `method. When a customer visits this route they will be redirected to Stripe's Checkout page: @@ -2378,7 +2378,7 @@ Route::get('/subscription-checkout', function (Request $request) { }); ``` -> [!WARNING] +> [!WARNING] > Unfortunately Stripe Checkout does not support all subscription billing options when starting subscriptions. Using the `anchorBillingCycleOn` method on the subscription builder, setting proration behavior, or setting payment behavior will not have any effect during Stripe Checkout sessions. Please consult [the Stripe Checkout Session API documentation](https://stripe.com/docs/api/checkout/sessions/create) to review which parameters are available. @@ -2410,7 +2410,7 @@ $checkout = $user->collectTaxIds()->checkout('price_tshirt'); When this method is invoked, a new checkbox will be available to the customer that allows them to indicate if they're purchasing as a company. If so, they will have the opportunity to provide their Tax ID number. -> [!WARNING] +> [!WARNING] > If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. @@ -2538,7 +2538,7 @@ You may consult the [Stripe API documentation](https://stripe.com/docs/api/payme If your business or one of your customers 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. -> [!WARNING] +> [!WARNING] > 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). @@ -2566,7 +2566,7 @@ CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment To ensure that off-session payment confirmation notifications are delivered, verify that [Stripe webhooks are configured](#handling-stripe-webhooks) for your application and the `invoice.payment_action_required` webhook is enabled in your Stripe dashboard. In addition, your `Billable` model should also use Laravel's `Illuminate\Notifications\Notifiable` trait. -> [!WARNING] +> [!WARNING] > Notifications will be sent even when customers are manually making a payment that requires additional confirmation. Unfortunately, there is no way for Stripe to know that the payment was done manually or "off-session". But, a customer will simply see a "Payment Successful" message if they visit the payment page after already confirming their payment. The customer will not be allowed to accidentally confirm the same payment twice and incur an accidental second charge. @@ -2611,5 +2611,5 @@ 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 / prices that you may use during testing. -> [!NOTE] +> [!NOTE] > 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 082402b9986..30389aed49e 100644 --- a/blade.md +++ b/blade.md @@ -81,7 +81,7 @@ You may display the contents of the `name` variable like so: Hello, {{ $name }}. ``` -> [!NOTE] +> [!NOTE] > 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: @@ -124,7 +124,7 @@ By default, Blade `{{ }}` statements are automatically sent through PHP's `htmls Hello, {!! $name !!}. ``` -> [!WARNING] +> [!WARNING] > 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. @@ -177,7 +177,7 @@ The latest versions of the Laravel application skeleton include a `Js` facade, w ``` -> [!WARNING] +> [!WARNING] > You should only use the `Js::from` method 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. @@ -366,7 +366,7 @@ In addition to conditional statements, Blade provides simple directives for work @endwhile ``` -> [!NOTE] +> [!NOTE] > While iterating through a `foreach` loop, 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 skip the current iteration or end the loop using the `@continue` and `@break` directives: @@ -540,7 +540,7 @@ In addition, the `@required` directive may be used to indicate if a given elemen ### Including Subviews -> [!NOTE] +> [!NOTE] > 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. 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: @@ -581,7 +581,7 @@ To include the first view that exists from a given array of views, you may use t @includeFirst(['custom.admin', 'admin'], ['status' => 'complete']) ``` -> [!WARNING] +> [!WARNING] > 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. @@ -601,7 +601,7 @@ You may also pass a fourth argument to the `@each` directive. This argument dete @each('view.name', $jobs, 'job', 'view.empty') ``` -> [!WARNING] +> [!WARNING] > 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. @@ -1014,7 +1014,7 @@ All of the attributes that are not part of the component's constructor will auto ``` -> [!WARNING] +> [!WARNING] > Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled. @@ -1061,7 +1061,7 @@ If you need to merge other attributes onto your component, you can chain the `me ``` -> [!NOTE] +> [!NOTE] > If you need to conditionally compile classes on other HTML elements that shouldn't receive merged attributes, you can use the [`@class` directive](#conditional-classes). @@ -1349,7 +1349,7 @@ Sometimes you may need to render a component but not know which component should ### Manually Registering Components -> [!WARNING] +> [!WARNING] > 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. When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. @@ -1503,7 +1503,7 @@ Because the `color` prop was only passed into the parent (``), it won't ``` -> [!WARNING] +> [!WARNING] > The `@aware` directive cannot access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component cannot be accessed by the `@aware` directive. @@ -1668,7 +1668,7 @@ When defining a child view, use the `@extends` Blade directive to specify which 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. -> [!NOTE] +> [!NOTE] > 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: @@ -1920,7 +1920,7 @@ As you can see, we will chain the `format` method onto whatever expression is pa format('m/d/Y H:i'); ?> ``` -> [!WARNING] +> [!WARNING] > After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command. diff --git a/broadcasting.md b/broadcasting.md index a0bd1ab3e08..88869cd41ba 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -55,7 +55,7 @@ The core concepts behind broadcasting are simple: clients connect to named chann By default, Laravel includes three server-side broadcasting drivers for you to choose from: [Laravel Reverb](https://reverb.laravel.com), [Pusher Channels](https://pusher.com/channels), and [Ably](https://ably.com). -> [!NOTE] +> [!NOTE] > Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). @@ -139,7 +139,7 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst ### Ably -> [!NOTE] +> [!NOTE] > The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). If you plan to broadcast your events using [Ably](https://ably.com), you should install the Ably PHP SDK using the Composer package manager: @@ -199,7 +199,7 @@ Next, you should compile your application's assets: npm run build ``` -> [!WARNING] +> [!WARNING] > The Laravel Echo `reverb` broadcaster requires laravel-echo v1.16.0+. @@ -254,7 +254,7 @@ Once you have adjusted the Echo configuration according to your application's ne npm run build ``` -> [!NOTE] +> [!NOTE] > To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). @@ -280,7 +280,7 @@ window.Echo = new Echo({ ### Ably -> [!NOTE] +> [!NOTE] > The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please [consult Ably's Laravel broadcaster documentation](https://github.com/ably/laravel-broadcaster). [Laravel Echo](https://github.com/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. Echo also leverages the `pusher-js` NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages. @@ -319,7 +319,7 @@ Once you have adjusted the Echo configuration according to your needs, you may c npm run dev ``` -> [!NOTE] +> [!NOTE] > To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). @@ -616,7 +616,7 @@ class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit } ``` -> [!NOTE] +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -663,7 +663,7 @@ Broadcast::channel('orders.{order}', function (User $user, Order $order) { }); ``` -> [!WARNING] +> [!WARNING] > 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. @@ -721,7 +721,7 @@ class OrderChannel } ``` -> [!NOTE] +> [!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. @@ -757,7 +757,7 @@ axios.post('/task', task) 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. -> [!WARNING] +> [!WARNING] > Your event must use the `Illuminate\Broadcasting\InteractsWithSockets` trait in order to call the `toOthers` method. @@ -1027,7 +1027,7 @@ Echo.join(`chat.${roomId}`) ## Model Broadcasting -> [!WARNING] +> [!WARNING] > Before reading the following documentation about model broadcasting, we recommend you become familiar with the general concepts of Laravel's model broadcasting services as well as how to manually create and listen to broadcast events. It is common to broadcast events when your application's [Eloquent models](/docs/{{version}}/eloquent) are created, updated, or deleted. Of course, this can easily be accomplished by manually [defining custom events for Eloquent model state changes](/docs/{{version}}/eloquent#events) and marking those events with the `ShouldBroadcast` interface. @@ -1216,7 +1216,7 @@ Echo.private(`App.Models.User.${this.user.id}`) ## Client Events -> [!NOTE] +> [!NOTE] > When using [Pusher Channels](https://pusher.com/channels), you must enable the "Client Events" option in the "App Settings" section of your [application dashboard](https://dashboard.pusher.com/) in order to send client events. Sometimes you may wish to broadcast an event to other connected clients without hitting your Laravel application at all. This can be particularly useful for things like "typing" notifications, where you want to alert users of your application that another user is typing a message on a given screen. diff --git a/cache.md b/cache.md index d43cd9d3e30..b5a9d2a455f 100644 --- a/cache.md +++ b/cache.md @@ -296,7 +296,7 @@ The `forever` method may be used to store an item in the cache permanently. Sinc Cache::forever('key', 'value'); ``` -> [!NOTE] +> [!NOTE] > If you are using the Memcached driver, items that are stored "forever" may be removed when the cache reaches its size limit. @@ -322,7 +322,7 @@ You may clear the entire cache using the `flush` method: Cache::flush(); ``` -> [!WARNING] +> [!WARNING] > 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. @@ -350,13 +350,13 @@ cache()->remember('users', $seconds, function () { }); ``` -> [!NOTE] +> [!NOTE] > 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). ## Atomic Locks -> [!WARNING] +> [!WARNING] > To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. @@ -477,7 +477,7 @@ Cache::extend('mongo', function (Application $app) { }); ``` -> [!NOTE] +> [!NOTE] > If you're wondering where to put your custom cache driver code, you could create an `Extensions` namespace within your `app` directory. However, keep in mind that Laravel does not have a rigid application structure and you are free to organize your application according to your preferences. diff --git a/cashier-paddle.md b/cashier-paddle.md index e3aa5aba613..6f2cba862a0 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -53,7 +53,7 @@ ## Introduction -> [!WARNING] +> [!WARNING] > This documentation is for Cashier Paddle 2.x's integration with Paddle Billing. If you're still using Paddle Classic, you should use [Cashier Paddle 1.x](https://github.com/laravel/cashier-paddle/tree/1.x). [Laravel Cashier Paddle](https://github.com/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: swapping subscriptions, subscription "quantities", subscription pausing, cancelation grace periods, and more. @@ -86,7 +86,7 @@ Then, you should run your application's database migrations. The Cashier migrati php artisan migrate ``` -> [!WARNING] +> [!WARNING] > To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks). @@ -170,7 +170,7 @@ You can specify a locale to be used when formatting money values for display on CASHIER_CURRENCY_LOCALE=nl_BE ``` -> [!WARNING] +> [!WARNING] > In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. @@ -209,7 +209,7 @@ public function boot(): void ### Selling Products -> [!NOTE] +> [!NOTE] > Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should [configure Paddle's webhook handling](#handling-paddle-webhooks). Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://www.paddle.com/billing/checkout), you can easily build modern, robust payment integrations. @@ -315,7 +315,7 @@ Please refer to Paddle's documentation for more information on the [data contain ### Selling Subscriptions -> [!NOTE] +> [!NOTE] > Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should [configure Paddle's webhook handling](#handling-paddle-webhooks). Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://www.paddle.com/billing/checkout), you can easily build modern, robust payment integrations. @@ -435,7 +435,7 @@ Route::put('/subscription/cancel', function (Request $request, $price) { And now your subscription will get canceled at the end of its billing period. -> [!NOTE] +> [!NOTE] > As long as you have configured Cashier's webhook handling, Cashier will automatically keep your application's Cashier-related database tables in sync by inspecting the incoming webhooks from Paddle. So, for example, when you cancel a customer's subscription via Paddle's dashboard, Cashier will receive the corresponding webhook and mark the subscription as "canceled" in your application's database. @@ -479,7 +479,7 @@ By default, this will display the widget using Paddle's default styling. You can The Paddle checkout widget is asynchronous. Once the user creates a subscription within the widget, Paddle will send your application a webhook so that you may properly update the subscription state in your application's database. Therefore, it's important that you properly [set up webhooks](#handling-paddle-webhooks) to accommodate for state changes from Paddle. -> [!WARNING] +> [!WARNING] > 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. @@ -894,7 +894,7 @@ public function register(): void } ``` -> [!WARNING] +> [!WARNING] > 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. @@ -1098,7 +1098,7 @@ You may remove prices from subscriptions using the `swap` method and omitting th $user->subscription()->swap(['price_original' => 2]); ``` -> [!WARNING] +> [!WARNING] > You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. @@ -1173,7 +1173,7 @@ To resume a paused subscription, you may invoke the `resume` method on the subsc $user->subscription()->resume(); ``` -> [!WARNING] +> [!WARNING] > A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. @@ -1207,7 +1207,7 @@ To stop a subscription on its grace period from canceling, you may invoke the `s $user->subscription()->stopCancelation(); ``` -> [!WARNING] +> [!WARNING] > Paddle's subscriptions cannot be resumed after cancelation. If your customer wishes to resume their subscription, they will have to create a new subscription. @@ -1232,7 +1232,7 @@ Route::get('/user/subscribe', function (Request $request) { When your application receives the `subscription_created` event, Cashier 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. -> [!WARNING] +> [!WARNING] > If the customer's subscription is not canceled 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: @@ -1350,7 +1350,7 @@ To ensure your application can handle Paddle webhooks, be sure to [configure the - Subscription Paused - Subscription Canceled -> [!WARNING] +> [!WARNING] > Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. @@ -1492,7 +1492,7 @@ $response = $transaction->refund('Accidental charge'); For more information on refunds, please consult [Paddle's refund documentation](https://developer.paddle.com/build/transactions/create-transaction-adjustments). -> [!WARNING] +> [!WARNING] > Refunds must always be approved by Paddle before fully processing. @@ -1509,7 +1509,7 @@ $response = $transaction->credit('Compensation', 'pri_123'); For more info, [see Paddle's documentation on crediting](https://developer.paddle.com/build/transactions/create-transaction-adjustments). -> [!WARNING] +> [!WARNING] > Credits can only be applied for manually-collected transactions. Automatically-collected transactions are credited by Paddle themselves. diff --git a/collections.md b/collections.md index 45cb93525a9..5fd6ff659de 100644 --- a/collections.md +++ b/collections.md @@ -35,7 +35,7 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle $collection = collect([1, 2, 3]); ``` -> [!NOTE] +> [!NOTE] > The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. @@ -488,7 +488,7 @@ $collection->all(); // [1, 2, 3] ``` -> [!NOTE] +> [!NOTE] > The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. @@ -593,7 +593,7 @@ collect(['1', '2'])->containsOneItem(); This method has the same signature as the [`contains`](#method-contains) method; however, all values are compared using "strict" comparisons. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). @@ -716,7 +716,7 @@ $diff->all(); // [1, 3, 5] ``` -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). @@ -968,7 +968,7 @@ Primitive types such as `string`, `int`, `float`, `bool`, and `array` may also b return $collection->ensure('int'); ``` -> [!WARNING] +> [!WARNING] > The `ensure` method does not guarantee that elements of different types will not be added to the collection at a later time. @@ -1013,7 +1013,7 @@ $filtered->all(); For the inverse of `except`, see the [only](#method-only) method. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). @@ -1228,7 +1228,7 @@ $collection->forget(['name', 'framework']); // [] ``` -> [!WARNING] +> [!WARNING] > Unlike most other collection methods, `forget` does not return a new modified collection; it modifies and returns the collection it is called on. @@ -1457,7 +1457,7 @@ $intersect->all(); // [0 => 'Desk', 2 => 'Chair'] ``` -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). @@ -1714,7 +1714,7 @@ $multiplied->all(); // [2, 4, 6, 8, 10] ``` -> [!WARNING] +> [!WARNING] > Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. @@ -2022,7 +2022,7 @@ $filtered->all(); For the inverse of `only`, see the [except](#method-except) method. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). @@ -2668,7 +2668,7 @@ $subset->all(); // [3, 4] ``` -> [!WARNING] +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. @@ -2688,7 +2688,7 @@ $subset->all(); // [4] ``` -> [!WARNING] +> [!WARNING] > If the callback never returns `false`, the `skipWhile` method will return an empty collection. @@ -2815,7 +2815,7 @@ $sorted->values()->all(); If your sorting needs are more advanced, you may pass a callback to `sort` with your own algorithm. Refer to the PHP documentation on [`uasort`](https://secure.php.net/manual/en/function.uasort.php#refsect1-function.uasort-parameters), which is what the collection's `sort` method calls utilizes internally. -> [!NOTE] +> [!NOTE] > If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. @@ -3199,7 +3199,7 @@ $subset->all(); // [1, 2] ``` -> [!WARNING] +> [!WARNING] > If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. @@ -3219,7 +3219,7 @@ $subset->all(); // [1, 2] ``` -> [!WARNING] +> [!WARNING] > If the callback never returns `false`, the `takeWhile` method will return all items in the collection. @@ -3270,7 +3270,7 @@ $collection->toArray(); */ ``` -> [!WARNING] +> [!WARNING] > `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw array underlying the collection, use the [`all`](#method-all) method instead. @@ -3303,7 +3303,7 @@ $collection->all(); // [2, 4, 6, 8, 10] ``` -> [!WARNING] +> [!WARNING] > Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. @@ -3417,7 +3417,7 @@ $unique->values()->all(); The `unique` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`uniqueStrict`](#method-uniquestrict) method to filter using "strict" comparisons. -> [!NOTE] +> [!NOTE] > This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). @@ -3971,7 +3971,7 @@ return $users->sum->votes; ### Introduction -> [!WARNING] +> [!WARNING] > Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). To supplement the already powerful `Collection` class, the `LazyCollection` class leverages PHP's [generators](https://www.php.net/manual/en/language.generators.overview.php) to allow you to work with very large datasets while keeping memory usage low. @@ -4170,7 +4170,7 @@ Almost all methods available on the `Collection` class are also available on the -> [!WARNING] +> [!WARNING] > Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. diff --git a/concurrency.md b/concurrency.md index 78eebdae9e9..e29cd916710 100644 --- a/concurrency.md +++ b/concurrency.md @@ -40,7 +40,7 @@ If you upgraded to Laravel 11.x from a Laravel 10.x application, you may need to Laravel achieves concurrency by serializing the given closures and dispatching them to a hidden Artisan CLI command, which unserializes the closures and invokes it within its own PHP process. After the closure has been invoked, the resulting value is serialized back to the parent process. -The `Concurrency` facade supports three drivers: `process` (the default), `fork`, and `sync`. +The `Concurrency` facade supports three drivers: `process` (the default), `fork`, and `sync`. The `fork` driver offers improved performance compared to the default `process` driver, but it may only be used within PHP's CLI context, as PHP does not support forking during web requests. Before using the `fork` driver, you need to install the `spatie/fork` package: diff --git a/configuration.md b/configuration.md index 15a870b2d81..8869b27a18d 100644 --- a/configuration.md +++ b/configuration.md @@ -51,7 +51,7 @@ Laravel's default `.env` file contains some common configuration values that may If you are developing with a team, you may wish to continue including and updating the `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. -> [!NOTE] +> [!NOTE] > Any variable in your `.env` file can be overridden by external environment variables such as server-level or system-level environment variables. @@ -126,7 +126,7 @@ if (App::environment(['local', 'staging'])) { } ``` -> [!NOTE] +> [!NOTE] > The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. @@ -149,7 +149,7 @@ Running the `env:encrypt` command will encrypt your `.env` file and place the en php artisan env:encrypt --key=3UVsEgGVK36XN82KKeyLFMhvosbZN1aF ``` -> [!NOTE] +> [!NOTE] > The length of the key provided should match the key length required by the encryption cipher being used. By default, Laravel will use the `AES-256-CBC` cipher which requires a 32 character key. You are free to use any cipher supported by Laravel's [encrypter](/docs/{{version}}/encryption) by passing the `--cipher` option when invoking the command. If your application has multiple environment files, such as `.env` and `.env.staging`, you may specify the environment file that should be encrypted by providing the environment name via the `--env` option: @@ -244,7 +244,7 @@ The `config:clear` command may be used to purge the cached configuration: php artisan config:clear ``` -> [!WARNING] +> [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded; therefore, the `env` function will only return external, system level environment variables. @@ -265,7 +265,7 @@ php artisan config:publish --all The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. -> [!WARNING] +> [!WARNING] > For local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** @@ -314,7 +314,7 @@ php artisan down --with-secret When accessing this hidden route, you will then be redirected to the `/` route of the application. Once the cookie has been issued to your browser, you will be able to browse the application normally as if it was not in maintenance mode. -> [!NOTE] +> [!NOTE] > Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?` or `&`. @@ -358,7 +358,7 @@ To disable maintenance mode, use the `up` command: php artisan up ``` -> [!NOTE] +> [!NOTE] > You may customize the default maintenance mode template by defining your own template at `resources/views/errors/503.blade.php`. diff --git a/context.md b/context.md index c20297aaa1a..39e60dfba91 100644 --- a/context.md +++ b/context.md @@ -285,7 +285,7 @@ Context::pop('breadcrumbs') // second_value Context::get('breadcrumbs'); -// ['first_value'] +// ['first_value'] ``` If you would like to retrieve all of the information stored in the context, you may invoke the `all` method: @@ -405,7 +405,7 @@ public function boot(): void } ``` -> [!NOTE] +> [!NOTE] > You should not use the `Context` facade within the `dehydrating` callback, as that will change the context of the current process. Ensure you only make changes to the repository passed to the callback. @@ -433,5 +433,5 @@ public function boot(): void } ``` -> [!NOTE] +> [!NOTE] > You should not use the `Context` facade within the `hydrated` callback and instead ensure you only make changes to the repository passed to the callback. diff --git a/controllers.md b/controllers.md index ee927f8d5de..262c2b9e07b 100644 --- a/controllers.md +++ b/controllers.md @@ -67,7 +67,7 @@ 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. -> [!NOTE] +> [!NOTE] > Controllers are not **required** to extend a base class. However, it is sometimes convenient to extend a base controller class that contains methods that should be shared across all of your controllers. @@ -106,7 +106,7 @@ You may generate an invokable controller by using the `--invokable` option of th php artisan make:controller ProvisionServer --invokable ``` -> [!NOTE] +> [!NOTE] > Controller stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). @@ -165,7 +165,7 @@ public static function middleware(): array } ``` -> [!WARNING] +> [!WARNING] > Controllers implementing `Illuminate\Routing\Controllers\HasMiddleware` should not extend `Illuminate\Routing\Controller`. @@ -452,7 +452,7 @@ Route::get('/photos/popular', [PhotoController::class, 'popular']); Route::resource('photos', PhotoController::class); ``` -> [!NOTE] +> [!NOTE] > Remember to keep your controllers focused. If you find yourself routinely needing methods outside of the typical set of resource actions, consider splitting your controller into two, smaller controllers. diff --git a/csrf.md b/csrf.md index 4a2cd8ee6b4..a60b53edccb 100644 --- a/csrf.md +++ b/csrf.md @@ -86,7 +86,7 @@ Typically, you should place these kinds of routes outside of the `web` middlewar }) ``` -> [!NOTE] +> [!NOTE] > For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). @@ -115,5 +115,5 @@ Laravel stores the current CSRF token in an encrypted `XSRF-TOKEN` cookie that i This cookie is primarily sent as a developer convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the `X-XSRF-TOKEN` header on same-origin requests. -> [!NOTE] +> [!NOTE] > By default, the `resources/js/bootstrap.js` file includes the Axios HTTP library which will automatically send the `X-XSRF-TOKEN` header for you. diff --git a/database-testing.md b/database-testing.md index e2f7bbaaec9..a2525eb2d03 100644 --- a/database-testing.md +++ b/database-testing.md @@ -210,7 +210,7 @@ Assert that a table in the database contains no records: ```php $this->assertDatabaseEmpty('users'); ``` - + #### assertDatabaseHas diff --git a/database.md b/database.md index a12621c9ce9..14c9c68adb5 100644 --- a/database.md +++ b/database.md @@ -52,7 +52,7 @@ By default, foreign key constraints are enabled for SQLite connections. If you w DB_FOREIGN_KEYS=false ``` -> [!NOTE] +> [!NOTE] > If you use the [Laravel installer](/docs/{{version}}/installation#creating-a-laravel-project) to create your Laravel application and select SQLite as your database, Laravel will automatically create a `database/database.sqlite` file and run the default [database migrations](/docs/{{version}}/migrations) for you. @@ -257,7 +257,7 @@ Sometimes you may want to execute an SQL statement without binding any values. Y DB::unprepared('update users set votes = 100 where name = "Dries"'); ``` -> [!WARNING] +> [!WARNING] > Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement. @@ -417,7 +417,7 @@ Lastly, you can commit a transaction via the `commit` method: DB::commit(); ``` -> [!NOTE] +> [!NOTE] > The `DB` facade's transaction methods control the transactions for both the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent). diff --git a/deployment.md b/deployment.md index 158ce5a2fc3..292e0be0221 100644 --- a/deployment.md +++ b/deployment.md @@ -134,7 +134,7 @@ php artisan config:cache This command will combine all of Laravel's configuration files into a single, cached file, which greatly reduces the number of trips the framework must make to the filesystem when loading your configuration values. -> [!WARNING] +> [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function for `.env` variables will return `null`. @@ -173,7 +173,7 @@ This command precompiles all your Blade views so they are not compiled on demand The debug option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your application's `.env` file. -> [!WARNING] +> [!WARNING] > **In your production environment, this value should always be `false`. If the `APP_DEBUG` variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** diff --git a/eloquent-factories.md b/eloquent-factories.md index a95cfa08096..afb6383f027 100644 --- a/eloquent-factories.md +++ b/eloquent-factories.md @@ -73,7 +73,7 @@ As you can see, in their most basic form, factories are classes that extend Lara Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing and seeding. -> [!NOTE] +> [!NOTE] > You can change your application's Faker locale by updating the `faker_locale` option in your `config/app.php` configuration file. @@ -260,7 +260,7 @@ $user = User::factory()->state([ ])->make(); ``` -> [!NOTE] +> [!NOTE] > [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. From 092db4e880eb9de7f39e953bed43a8fe3202884c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20H=2E?= <36476318+Tamas-hi@users.noreply.github.com> Date: Mon, 17 Mar 2025 21:39:02 +0100 Subject: [PATCH 2090/2609] Add documentation for getChanges (#10258) * Add documentation for getChanges The eloquent method getOriginal is already documented, and we should do the same with getChanges. * Update eloquent.md --------- Co-authored-by: Taylor Otwell --- eloquent.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index ac745a2d3fe..963482fdf2c 100644 --- a/eloquent.md +++ b/eloquent.md @@ -872,13 +872,36 @@ $user = User::find(1); $user->name; // John $user->email; // john@example.com -$user->name = "Jack"; +$user->name = 'Jack'; $user->name; // Jack $user->getOriginal('name'); // John $user->getOriginal(); // Array of original attributes... ``` +The `getChanges` method returns an array containing the attributes that changed when the model was last saved: + +```php +$user = User::find(1); + +$user->name; // John +$user->email; // john@example.com + +$user->update([ + 'name' => 'Jack', + 'email' => 'jack@example.com', +]); + +$user->getChanges(); + +/* + [ + 'name' => 'Jack', + 'email' => 'jack@example.com', + ] +*/ +``` + ### Mass Assignment From 73056479a60a5509881e47a847dd606d2984ac93 Mon Sep 17 00:00:00 2001 From: Samuel Date: Mon, 17 Mar 2025 21:47:53 +0100 Subject: [PATCH 2091/2609] Explain redirectUsersTo functionality (#10253) * Explain redirectUsersTo functionality * formatting --------- Co-authored-by: Taylor Otwell --- authentication.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index cea323fada4..a103d158d7d 100644 --- a/authentication.md +++ b/authentication.md @@ -189,7 +189,7 @@ Route::get('/flights', function () { #### Redirecting Unauthenticated Users -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 using the method `redirectGuestsTo` of your application's `bootstrap/app.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 using the `redirectGuestsTo` method within your application's `bootstrap/app.php` file: ```php use Illuminate\Http\Request; @@ -202,6 +202,22 @@ use Illuminate\Http\Request; }) ``` + +#### Redirecting Authenticated Users + +When the `guest` middleware detects an authenticated user, it will redirect the user to the `dashboard` or `home` named route. You may modify this behavior using the `redirectUsersTo` method within your application's `bootstrap/app.php` file: + +```php +use Illuminate\Http\Request; + +->withMiddleware(function (Middleware $middleware) { + $middleware->redirectUsersTo('/panel'); + + // Using a closure... + $middleware->redirectUsersTo(fn (Request $request) => route('panel')); +}) +``` + #### Specifying a Guard From a89581b628e5fce26aa0085771e4877c3a650a71 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 17 Mar 2025 23:10:43 +0200 Subject: [PATCH 2092/2609] Fix markdown formatting (#10259) --- eloquent-mutators.md | 6 +++--- eloquent-relationships.md | 22 +++++++++++----------- eloquent-resources.md | 10 +++++----- eloquent-serialization.md | 4 ++-- eloquent.md | 26 +++++++++++++------------- errors.md | 4 ++-- events.md | 10 +++++----- filesystem.md | 12 ++++++------ fortify.md | 4 ++-- frontend.md | 2 +- hashing.md | 2 +- helpers.md | 8 ++++---- homestead.md | 22 +++++++++++----------- horizon.md | 16 ++++++++-------- http-client.md | 2 +- http-tests.md | 12 ++++++------ localization.md | 6 +++--- logging.md | 8 ++++---- 18 files changed, 88 insertions(+), 88 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index c7909cc12f5..6c1c015c893 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -66,7 +66,7 @@ $user = User::find(1); $firstName = $user->first_name; ``` -> [!NOTE] +> [!NOTE] > If you would like these computed values to be added to the array / JSON representations of your model, [you will need to append them](/docs/{{version}}/eloquent-serialization#appending-values-to-json). @@ -280,7 +280,7 @@ $user->mergeCasts([ ]); ``` -> [!WARNING] +> [!WARNING] > Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship or assign a cast to the model's primary key. @@ -710,7 +710,7 @@ $user->address->lineOne = 'Updated Address Value'; $user->save(); ``` -> [!NOTE] +> [!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. diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 1f539a87722..c4f96f062fb 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -442,7 +442,7 @@ public function largestOrder(): HasOne } ``` -> [!WARNING] +> [!WARNING] > Because PostgreSQL does not support executing the `MAX` function against UUID columns, it is not currently possible to use one-of-many relationships in combination with PostgreSQL UUID columns. @@ -870,7 +870,7 @@ If you would like your intermediate table to have `created_at` and `updated_at` return $this->belongsToMany(Role::class)->withTimestamps(); ``` -> [!WARNING] +> [!WARNING] > Intermediate tables that utilize Eloquent's automatically maintained timestamps are required to have both `created_at` and `updated_at` timestamp columns. @@ -988,7 +988,7 @@ class RoleUser extends Pivot } ``` -> [!WARNING] +> [!WARNING] > Pivot models may not use the `SoftDeletes` trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model. @@ -1318,7 +1318,7 @@ public function bestImage(): MorphOne } ``` -> [!NOTE] +> [!NOTE] > It is possible to construct more advanced "one of many" relationships. For more information, please consult the [has one of many documentation](#advanced-has-one-of-many-relationships). @@ -1348,7 +1348,7 @@ taggables taggable_type - string ``` -> [!NOTE] +> [!NOTE] > Before diving into polymorphic many-to-many relationships, you may benefit from reading the documentation on typical [many-to-many relationships](#many-to-many). @@ -1472,7 +1472,7 @@ $alias = $post->getMorphClass(); $class = Relation::getMorphedModel($alias); ``` -> [!WARNING] +> [!WARNING] > When adding a "morph map" to your existing application, every morphable `*_type` column value in your database that still contains a fully-qualified class will need to be converted to its "map" name. @@ -1491,7 +1491,7 @@ Order::resolveRelationUsing('customer', function (Order $orderModel) { }); ``` -> [!WARNING] +> [!WARNING] > When defining dynamic relationships, always provide explicit key name arguments to the Eloquent relationship methods. @@ -1633,7 +1633,7 @@ $posts = Post::whereHas('comments', function (Builder $query) { }, '>=', 10)->get(); ``` -> [!WARNING] +> [!WARNING] > Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. @@ -2041,7 +2041,7 @@ You may not always need every column from the relationships you are retrieving. $books = Book::with('author:id,name,book_id')->get(); ``` -> [!WARNING] +> [!WARNING] > When using this feature, you should always include the `id` column and any relevant foreign key columns in the list of columns you wish to retrieve. @@ -2358,7 +2358,7 @@ $user->posts()->createManyQuietly([ You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods to [create and update models on relationships](/docs/{{version}}/eloquent#upserts). -> [!NOTE] +> [!NOTE] > Before using the `create` method, be sure to review the [mass assignment](/docs/{{version}}/eloquent#mass-assignment) documentation. @@ -2521,5 +2521,5 @@ class Comment extends Model } ``` -> [!WARNING] +> [!WARNING] > Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. diff --git a/eloquent-resources.md b/eloquent-resources.md index 065ca219ecc..638d5576a20 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -44,7 +44,7 @@ php artisan make:resource UserCollection ## Concept Overview -> [!NOTE] +> [!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: @@ -212,7 +212,7 @@ class UserCollection extends ResourceCollection ## Writing Resources -> [!NOTE] +> [!NOTE] > If you have not read the [concept overview](#concept-overview), you are highly encouraged to do so before proceeding with this documentation. Resources only need to transform a given model into an array. So, each resource contains a `toArray` method which translates your model's attributes into an API friendly array that can be returned from your application's routes or controllers: @@ -283,7 +283,7 @@ public function toArray(Request $request): array } ``` -> [!NOTE] +> [!NOTE] > If you would like to include relationships only when they have already been loaded, check out the documentation on [conditional relationships](#conditional-relationships). @@ -392,7 +392,7 @@ class AppServiceProvider extends ServiceProvider } ``` -> [!WARNING] +> [!WARNING] > The `withoutWrapping` method only affects the outermost response and will not remove `data` keys that you manually add to your own resource collections. @@ -605,7 +605,7 @@ public function toArray(Request $request): array Again, if the given condition is `false`, these attributes will be removed from the resource response before it is sent to the client. -> [!WARNING] +> [!WARNING] > The `mergeWhen` method should not be used within arrays that mix string and numeric keys. Furthermore, it should not be used within arrays with numeric keys that are not ordered sequentially. diff --git a/eloquent-serialization.md b/eloquent-serialization.md index 5488da39372..2b66c798473 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -13,7 +13,7 @@ When building APIs using Laravel, you will often need to convert your models and relationships to arrays or JSON. Eloquent includes convenient methods for making these conversions, as well as controlling which attributes are included in the serialized representation of your models. -> [!NOTE] +> [!NOTE] > For an even more robust way of handling Eloquent model and collection JSON serialization, check out the documentation on [Eloquent API resources](/docs/{{version}}/eloquent-resources). @@ -105,7 +105,7 @@ class User extends Model } ``` -> [!NOTE] +> [!NOTE] > To hide relationships, add the relationship's method name to your Eloquent model's `$hidden` property. Alternatively, you may use the `visible` property to define an "allow list" of attributes that should be included in your model's array and JSON representation. All attributes that are not present in the `$visible` array will be hidden when the model is converted to an array or JSON: diff --git a/eloquent.md b/eloquent.md index 963482fdf2c..5e07d101a6f 100644 --- a/eloquent.md +++ b/eloquent.md @@ -44,7 +44,7 @@ Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Eloquent, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well. -> [!NOTE] +> [!NOTE] > Before getting started, be sure to configure a database connection in your application's `config/database.php` configuration file. For more information on configuring your database, check out [the database configuration documentation](/docs/{{version}}/database#configuration). @@ -435,7 +435,7 @@ $flights = Flight::where('active', 1) ->get(); ``` -> [!NOTE] +> [!NOTE] > Since Eloquent models are query builders, you should review all of the methods provided by Laravel's [query builder](/docs/{{version}}/queries). You may use any of these methods when writing your Eloquent queries. @@ -558,7 +558,7 @@ Similar to the `lazy` method, the `cursor` method may be used to significantly r The `cursor` method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor. -> [!WARNING] +> [!WARNING] > Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality: @@ -807,7 +807,7 @@ Flight::where('active', 1) The `update` method expects an array of column and value pairs representing the columns that should be updated. The `update` method returns the number of affected rows. -> [!WARNING] +> [!WARNING] > When issuing a mass update via Eloquent, the `saving`, `saved`, `updating`, and `updated` model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. @@ -1012,7 +1012,7 @@ Flight::upsert([ ], uniqueBy: ['departure', 'destination'], update: ['price']); ``` -> [!WARNING] +> [!WARNING] > All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MariaDB and MySQL database drivers ignore the second argument of the `upsert` method and always use the "primary" and "unique" indexes of the table to detect existing records. @@ -1049,7 +1049,7 @@ If you are utilizing [soft deleting models](#soft-deleting), you may permanently Flight::forceDestroy(1); ``` -> [!WARNING] +> [!WARNING] > The `destroy` method loads each model individually and calls the `delete` method so that the `deleting` and `deleted` events are properly dispatched for each model. @@ -1067,7 +1067,7 @@ To delete all models in a table, you should execute a query without adding any c $deleted = Flight::query()->delete(); ``` -> [!WARNING] +> [!WARNING] > When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement. @@ -1089,7 +1089,7 @@ class Flight extends Model } ``` -> [!NOTE] +> [!NOTE] > The `SoftDeletes` trait will automatically cast the `deleted_at` attribute to a `DateTime` / `Carbon` instance for you. You should also add the `deleted_at` column to your database table. The Laravel [schema builder](/docs/{{version}}/migrations) contains a helper method to create this column: @@ -1258,7 +1258,7 @@ You may test your `prunable` query by executing the `model:prune` command with t php artisan model:prune --pretend ``` -> [!WARNING] +> [!WARNING] > Soft deleting models will be permanently deleted (`forceDelete`) if they match the prunable query. @@ -1371,7 +1371,7 @@ class AncientScope implements Scope } ``` -> [!NOTE] +> [!NOTE] > If your global scope is adding columns to the select clause of the query, you should use the `addSelect` method instead of `select`. This will prevent the unintentional replacement of the query's existing select clause. @@ -1628,7 +1628,7 @@ if ($post->author()->is($user)) { ## Events -> [!NOTE] +> [!NOTE] > Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `trashed`, `forceDeleting`, `forceDeleted`, `restoring`, `restored`, and `replicating`. @@ -1665,7 +1665,7 @@ class User extends Authenticatable After defining and mapping your Eloquent events, you may use [event listeners](/docs/{{version}}/events#defining-listeners) to handle the events. -> [!WARNING] +> [!WARNING] > When issuing a mass update or delete query via Eloquent, the `saved`, `updated`, `deleting`, and `deleted` model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing mass updates or deletes. @@ -1797,7 +1797,7 @@ public function boot(): void } ``` -> [!NOTE] +> [!NOTE] > There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. diff --git a/errors.md b/errors.md index 64f7c9f84d6..8a08a0e64c0 100644 --- a/errors.md +++ b/errors.md @@ -58,7 +58,7 @@ When you register a custom exception reporting callback using the `report` metho }) ``` -> [!NOTE] +> [!NOTE] > To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). @@ -355,7 +355,7 @@ public function report(): bool } ``` -> [!NOTE] +> [!NOTE] > You may type-hint any required dependencies of the `report` method and they will automatically be injected into the method by Laravel's [service container](/docs/{{version}}/container). diff --git a/events.md b/events.md index 2c6d6f34ce7..33b8726c05f 100644 --- a/events.md +++ b/events.md @@ -271,7 +271,7 @@ class SendShipmentNotification } ``` -> [!NOTE] +> [!NOTE] > Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. @@ -452,7 +452,7 @@ class SendShipmentNotification implements ShouldQueueAfterCommit } ``` -> [!NOTE] +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). @@ -539,7 +539,7 @@ public function retryUntil(): DateTime #### Specifying Queued Listener Backoff If you would like to configure how many seconds Laravel should wait before retrying a listener that has encountered an exception, you may do so by defining a `backoff` property on your listener class: - + ```php /** * The number of seconds to wait before retrying the queued listener. @@ -616,7 +616,7 @@ OrderShipped::dispatchIf($condition, $order); OrderShipped::dispatchUnless($condition, $order); ``` -> [!NOTE] +> [!NOTE] > When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](#testing) make it a cinch. @@ -847,7 +847,7 @@ Event::assertListening( ); ``` -> [!WARNING] +> [!WARNING] > 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. diff --git a/filesystem.md b/filesystem.md index d95f3bb5583..d93c7f56fbb 100644 --- a/filesystem.md +++ b/filesystem.md @@ -37,7 +37,7 @@ Laravel's filesystem configuration file is located at `config/filesystems.php`. The `local` driver interacts with files stored locally on the server running the Laravel application while the `s3` driver is used to write to Amazon's S3 cloud storage service. -> [!NOTE] +> [!NOTE] > You may configure as many disks as you like and may even have multiple disks that use the same driver. @@ -230,7 +230,7 @@ In order for Laravel's Flysystem integration to generate proper URLs when using AWS_URL=http://localhost:9000/local ``` -> [!WARNING] +> [!WARNING] > Generating temporary storage URLs via the `temporaryUrl` method may not work when using MinIO if the `endpoint` is not accessible by the client. @@ -321,7 +321,7 @@ $url = Storage::url('https://github.com/file.jpg'); When using the `local` driver, all files that should be publicly accessible should be placed in the `storage/app/public` directory. Furthermore, you should [create a symbolic link](#the-public-disk) at `public/storage` which points to the `storage/app/public` directory. -> [!WARNING] +> [!WARNING] > When using the `local` driver, the return value of `url` is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs. @@ -420,7 +420,7 @@ class AppServiceProvider extends ServiceProvider #### Temporary Upload URLs -> [!WARNING] +> [!WARNING] > The ability to generate temporary upload URLs is only supported by the `s3` driver. If you need to generate a temporary URL that can be used to upload a file directly from your client-side application, you may use the `temporaryUploadUrl` method. This method accepts a path and a `DateTime` instance specifying when the URL should expire. The `temporaryUploadUrl` method returns an associative array which may be destructured into the upload URL and the headers that should be included with the upload request: @@ -602,7 +602,7 @@ $path = Storage::putFileAs( ); ``` -> [!WARNING] +> [!WARNING] > Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. @@ -843,7 +843,7 @@ class ExampleTest extends TestCase 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. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). -> [!WARNING] +> [!WARNING] > The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). diff --git a/fortify.md b/fortify.md index 4df2abef52e..109733cb170 100644 --- a/fortify.md +++ b/fortify.md @@ -31,7 +31,7 @@ Since Fortify does not provide its own user interface, it is meant to be paired with your own user interface which makes requests to the routes it registers. We will discuss exactly how to make requests to these routes in the remainder of this documentation. -> [!NOTE] +> [!NOTE] > Remember, Fortify is a package that is meant to give you a head start implementing Laravel's authentication features. **You are not required to use it.** You are always free to manually interact with Laravel's authentication services by following the documentation available in the [authentication](/docs/{{version}}/authentication), [password reset](/docs/{{version}}/passwords), and [email verification](/docs/{{version}}/verification) documentation. @@ -216,7 +216,7 @@ By default, Fortify will throttle authentication attempts using the `EnsureLogin Some applications may require a different approach to throttling authentication attempts, such as throttling by IP address alone. Therefore, Fortify allows you to specify your own [rate limiter](/docs/{{version}}/routing#rate-limiting) via the `fortify.limiters.login` configuration option. Of course, this configuration option is located in your application's `config/fortify.php` configuration file. -> [!NOTE] +> [!NOTE] > Utilizing a mixture of throttling, [two factor authentication](/docs/{{version}}/fortify#two-factor-authentication), and an external web application firewall (WAF) will provide the most robust defense for your legitimate application users. diff --git a/frontend.md b/frontend.md index 7656f0e741d..2c8d9802cda 100644 --- a/frontend.md +++ b/frontend.md @@ -180,5 +180,5 @@ By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. V The fastest way to get started with Laravel and Vite is by beginning your application's development using [our application starter kits](/docs/{{version}}/starter-kits), which jump-starts your application by providing frontend and backend authentication scaffolding. -> [!NOTE] +> [!NOTE] > For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). diff --git a/hashing.md b/hashing.md index 8663e751066..ff5fe2c2dfd 100644 --- a/hashing.md +++ b/hashing.md @@ -85,7 +85,7 @@ $hashed = Hash::make('password', [ ]); ``` -> [!NOTE] +> [!NOTE] > For more information on these options, please refer to the [official PHP documentation regarding Argon hashing](https://secure.php.net/manual/en/function.password-hash.php). diff --git a/helpers.md b/helpers.md index a4e8c759726..ae1d78e1acb 100644 --- a/helpers.md +++ b/helpers.md @@ -1766,7 +1766,7 @@ $path = lang_path(); $path = lang_path('en/messages.php'); ``` -> [!NOTE] +> [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. @@ -2221,7 +2221,7 @@ $env = env('APP_ENV'); $env = env('APP_ENV', 'production'); ``` -> [!WARNING] +> [!WARNING] > If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. @@ -3026,12 +3026,12 @@ $user = Pipeline::send($user) ->through([ function (User $user, Closure $next) { // ... - + return $next($user); }, function (User $user, Closure $next) { // ... - + return $next($user); }, ]) diff --git a/homestead.md b/homestead.md index aeea06fd25d..bd282bc9d3d 100644 --- a/homestead.md +++ b/homestead.md @@ -42,7 +42,7 @@ Laravel strives to make the entire PHP development experience delightful, includ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, MySQL, PostgreSQL, Redis, Memcached, Node, and all of the other software you need to develop amazing Laravel applications. -> [!WARNING] +> [!WARNING] > If you are using Windows, you may need to enable hardware virtualization (VT-x). It can usually be enabled via your BIOS. If you are using Hyper-V on a UEFI system you may additionally need to disable Hyper-V in order to access VT-x. @@ -189,7 +189,7 @@ The `provider` key in your `Homestead.yaml` file indicates which Vagrant provide provider: virtualbox -> [!WARNING] +> [!WARNING] > If you are using Apple Silicon the Parallels provider is required. @@ -203,7 +203,7 @@ folders: to: /home/vagrant/project1 ``` -> [!WARNING] +> [!WARNING] > Windows users should not use the `~/` path syntax and instead should use the full path to their project, such as `C:\Users\user\Code\project1`. You should always map individual applications to their own folder mapping instead of mapping a single large directory that contains all of your applications. When you map a folder, the virtual machine must keep track of all disk IO for *every* file in the folder. You may experience reduced performance if you have a large number of files in a folder: @@ -216,7 +216,7 @@ folders: to: /home/vagrant/project2 ``` -> [!WARNING] +> [!WARNING] > You should never mount `.` (the current directory) when using Homestead. This causes Vagrant to not map the current folder to `/vagrant` and will break optional features and cause unexpected results while provisioning. To enable [NFS](https://developer.hashicorp.com/vagrant/docs/synced-folders/nfs), you may add a `type` option to your folder mapping: @@ -228,7 +228,7 @@ folders: type: "nfs" ``` -> [!WARNING] +> [!WARNING] > When using NFS on Windows, you should consider installing the [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd) plug-in. This plug-in will maintain the correct user / group permissions for files and directories within the Homestead virtual machine. You may also pass any options supported by Vagrant's [Synced Folders](https://developer.hashicorp.com/vagrant/docs/synced-folders/basic_usage) by listing them under the `options` key: @@ -256,7 +256,7 @@ sites: If you change the `sites` property after provisioning the Homestead virtual machine, you should execute the `vagrant reload --provision` command in your terminal to update the Nginx configuration on the virtual machine. -> [!WARNING] +> [!WARNING] > Homestead scripts are built to be as idempotent as possible. However, if you are experiencing issues while provisioning you should destroy and rebuild the machine by executing the `vagrant destroy && vagrant up` command. @@ -372,7 +372,7 @@ features: You may specify a supported version of Elasticsearch, which must be an exact version number (major.minor.patch). The default installation will create a cluster named 'homestead'. You should never give Elasticsearch more than half of the operating system's memory, so make sure your Homestead virtual machine has at least twice the Elasticsearch allocation. -> [!NOTE] +> [!NOTE] > Check out the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current) to learn how to customize your configuration. @@ -470,7 +470,7 @@ sites: to: /home/vagrant/project2/public ``` -> [!WARNING] +> [!WARNING] > You should ensure that you have configured a [folder mapping](#configuring-shared-folders) for the project's directory before adding the site. If Vagrant is not automatically managing your "hosts" file, you may need to add the new site to that file as well. On macOS and Linux, this file is located at `/etc/hosts`. On Windows, it is located at `C:\Windows\System32\drivers\etc\hosts`: @@ -612,7 +612,7 @@ php83 A `homestead` database is configured for both MySQL and PostgreSQL out of the box. To connect to your MySQL or PostgreSQL database from your host machine's database client, you should connect to `127.0.0.1` on port `33060` (MySQL) or `54320` (PostgreSQL). The username and password for both databases is `homestead` / `secret`. -> [!WARNING] +> [!WARNING] > You should only use these non-standard ports when connecting to the databases from your host machine. You will use the default 3306 and 5432 ports in your Laravel application's `database` configuration file since Laravel is running _within_ the virtual machine. @@ -720,7 +720,7 @@ share homestead.test -region=eu -subdomain=laravel If you need to share content over HTTPS rather than HTTP, using the `sshare` command instead of `share` will enable you to do so. -> [!WARNING] +> [!WARNING] > Remember, Vagrant is inherently insecure and you are exposing your virtual machine to the Internet when running the `share` command. @@ -733,7 +733,7 @@ Homestead includes support for step debugging using [Xdebug](https://xdebug.org) By default, Xdebug is already running and ready to accept connections. If you need to enable Xdebug on the CLI, execute the `sudo phpenmod xdebug` command within your Homestead virtual machine. Next, follow your IDE's instructions to enable debugging. Finally, configure your browser to trigger Xdebug with an extension or [bookmarklet](https://www.jetbrains.com/phpstorm/marklets/). -> [!WARNING] +> [!WARNING] > Xdebug causes PHP to run significantly slower. To disable Xdebug, run `sudo phpdismod xdebug` within your Homestead virtual machine and restart the FPM service. diff --git a/horizon.md b/horizon.md index 3fa78e39a56..e64536b70b0 100644 --- a/horizon.md +++ b/horizon.md @@ -18,7 +18,7 @@ ## Introduction -> [!NOTE] +> [!NOTE] > Before digging into Laravel Horizon, you should familiarize yourself with Laravel's base [queue services](/docs/{{version}}/queues). Horizon augments Laravel's queue with additional features that may be confusing if you are not already familiar with the basic queue features offered by Laravel. [Laravel Horizon](https://github.com/laravel/horizon) provides a beautiful dashboard and code-driven configuration for your Laravel powered [Redis queues](/docs/{{version}}/queues). Horizon allows you to easily monitor key metrics of your queue system such as job throughput, runtime, and job failures. @@ -30,7 +30,7 @@ When using Horizon, all of your queue worker configuration is stored in a single ## Installation -> [!WARNING] +> [!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: @@ -50,7 +50,7 @@ php artisan horizon:install After publishing Horizon's assets, its primary configuration file will be located at `config/horizon.php`. This configuration file allows you to configure the queue worker options for your application. Each configuration option includes a description of its purpose, so be sure to thoroughly explore this file. -> [!WARNING] +> [!WARNING] > Horizon uses a Redis connection named `horizon` internally. This Redis connection name is reserved and should not be assigned to another Redis connection in the `database.php` configuration file or as the value of the `use` option in the `horizon.php` configuration file. @@ -92,7 +92,7 @@ You may also define a wildcard environment (`*`) which will be used when no othe When you start Horizon, it will use the worker process configuration options for the environment that your application is running on. Typically, the environment is determined by the value of the `APP_ENV` [environment variable](/docs/{{version}}/configuration#determining-the-current-environment). For example, the default `local` Horizon environment is configured to start three worker processes and automatically balance the number of worker processes assigned to each queue. The default `production` environment is configured to start a maximum of 10 worker processes and automatically balance the number of worker processes assigned to each queue. -> [!WARNING] +> [!WARNING] > You should ensure that the `environments` portion of your `horizon` configuration file contains an entry for each [environment](/docs/{{version}}/configuration#environment-configuration) on which you plan to run Horizon. @@ -276,7 +276,7 @@ Supervisor is a process monitor for the Linux operating system and will automati sudo apt-get install supervisor ``` -> [!NOTE] +> [!NOTE] > If configuring Supervisor yourself sounds overwhelming, consider using [Laravel Cloud](https://cloud.laravel.com), which can manage background processes for your Laravel applications. @@ -298,7 +298,7 @@ stopwaitsecs=3600 When defining your Supervisor configuration, 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. -> [!WARNING] +> [!WARNING] > While the examples above are valid for Ubuntu based servers, the location and file extension expected of Supervisor configuration files may vary between other server operating systems. Please consult your server's documentation for more information. @@ -314,7 +314,7 @@ sudo supervisorctl update sudo supervisorctl start horizon ``` -> [!NOTE] +> [!NOTE] > For more information on running Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html). @@ -406,7 +406,7 @@ class SendRenderNotifications implements ShouldQueue ## Notifications -> [!WARNING] +> [!WARNING] > When configuring Horizon to send Slack or SMS notifications, you should review the [prerequisites for the relevant notification channel](/docs/{{version}}/notifications). If you would like to be notified when one of your queues has a long wait time, you may use the `Horizon::routeMailNotificationsTo`, `Horizon::routeSlackNotificationsTo`, and `Horizon::routeSmsNotificationsTo` methods. You may call these methods from the `boot` method of your application's `App\Providers\HorizonServiceProvider`: diff --git a/http-client.md b/http-client.md index 312c6362713..9e884014cd7 100644 --- a/http-client.md +++ b/http-client.md @@ -317,7 +317,7 @@ If all of the requests fail, an instance of `Illuminate\Http\Client\RequestExcep $response = Http::retry(3, 100, throw: false)->post(/* ... */); ``` -> [!WARNING] +> [!WARNING] > If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. diff --git a/http-tests.md b/http-tests.md index ee4c342284a..94614457b1d 100644 --- a/http-tests.md +++ b/http-tests.md @@ -95,7 +95,7 @@ class ExampleTest extends TestCase In general, each of your tests should only make one request to your application. Unexpected behavior may occur if multiple requests are executed within a single test method. -> [!NOTE] +> [!NOTE] > For convenience, the CSRF middleware is automatically disabled when running tests. @@ -494,7 +494,7 @@ expect($response['created'])->toBeTrue(); $this->assertTrue($response['created']); ``` -> [!NOTE] +> [!NOTE] > The `assertJson` method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. @@ -1292,7 +1292,7 @@ Assert that the response has no JSON validation errors for the given keys: $response->assertJsonMissingValidationErrors($keys); ``` -> [!NOTE] +> [!NOTE] > The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. @@ -1416,7 +1416,7 @@ Assert that the response has the given JSON validation errors for the given keys $response->assertJsonValidationErrors(array $data, $responseKey = 'errors'); ``` -> [!NOTE] +> [!NOTE] > The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. @@ -1703,7 +1703,7 @@ $response->assertSessionHasErrors([ ]); ``` -> [!NOTE] +> [!NOTE] > The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. @@ -1733,7 +1733,7 @@ Assert that the session has no validation errors for the given keys: $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); ``` -> [!NOTE] +> [!NOTE] > The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. diff --git a/localization.md b/localization.md index 7022bffe5b9..03125e743b1 100644 --- a/localization.md +++ b/localization.md @@ -15,7 +15,7 @@ ## Introduction -> [!NOTE] +> [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. Laravel's localization features provide a convenient way to retrieve strings in various languages, allowing you to easily support multiple languages within your application. @@ -106,7 +106,7 @@ public function boot(): void } ``` -> [!WARNING] +> [!WARNING] > If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). @@ -137,7 +137,7 @@ return [ ]; ``` -> [!WARNING] +> [!WARNING] > For languages that differ by territory, you should name the language directories according to the ISO 15897. For example, "en_GB" should be used for British English rather than "en-gb". diff --git a/logging.md b/logging.md index d434041ebed..8ec10a49b8e 100644 --- a/logging.md +++ b/logging.md @@ -55,7 +55,7 @@ Each log channel is powered by a "driver". The driver determines how and where t -> [!NOTE] +> [!NOTE] > Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. @@ -314,7 +314,7 @@ class AssignRequestId } ``` -> [!NOTE] +> [!NOTE] > If you need to share log context while processing queued jobs, you may utilize [job middleware](/docs/{{version}}/queues#job-middleware). @@ -407,7 +407,7 @@ class CustomizeFormatter } ``` -> [!NOTE] +> [!NOTE] > All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected. @@ -528,7 +528,7 @@ Laravel Pail is a package that allows you to easily dive into your Laravel appli ### Installation -> [!WARNING] +> [!WARNING] > Laravel Pail requires [PHP 8.2+](https://php.net/releases/) and the [PCNTL](https://www.php.net/manual/en/book.pcntl.php) extension. To get started, install Pail into your project using the Composer package manager: From 6a0eec994bc3e9dfd4a6d2d81abc3a5261b7e79d Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 18 Mar 2025 00:21:33 +0200 Subject: [PATCH 2093/2609] Fix markdown formatting (#10260) --- mail.md | 8 +++--- middleware.md | 4 +-- migrations.md | 16 ++++++------ mix.md | 2 +- mocking.md | 2 +- mongodb.md | 2 +- notifications.md | 2 +- octane.md | 20 +++++++-------- packages.md | 4 +-- pagination.md | 4 +-- passport.md | 28 ++++++++++----------- passwords.md | 6 ++--- pennant.md | 8 +++--- precognition.md | 14 +++++------ prompts.md | 6 ++--- providers.md | 2 +- pulse.md | 22 ++++++++--------- queries.md | 28 ++++++++++----------- queues.md | 64 ++++++++++++++++++++++++------------------------ rate-limiting.md | 2 +- redis.md | 4 +-- 21 files changed, 124 insertions(+), 124 deletions(-) diff --git a/mail.md b/mail.md index d8ce3a7c8cf..eb7c9c55915 100644 --- a/mail.md +++ b/mail.md @@ -379,7 +379,7 @@ public function content(): Content } ``` -> [!NOTE] +> [!NOTE] > You may wish to create a `resources/views/emails` directory to house all of your email templates; however, you are free to place them wherever you wish within your `resources/views` directory. @@ -637,7 +637,7 @@ Embedding inline images into your emails is typically cumbersome; however, Larav ``` -> [!WARNING] +> [!WARNING] > The `$message` variable is not available in plain-text message templates since plain-text messages do not utilize inline attachments. @@ -851,7 +851,7 @@ Thanks,
    ``` -> [!NOTE] +> [!NOTE] > Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. @@ -1073,7 +1073,7 @@ class OrderShipped extends Mailable implements ShouldQueue } ``` -> [!NOTE] +> [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). diff --git a/middleware.md b/middleware.md index c696ea8597b..4b7139a539b 100644 --- a/middleware.md +++ b/middleware.md @@ -60,7 +60,7 @@ As you can see, if the given `token` does not match our secret token, the middle It's best to envision middleware as a series of "layers" HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely. -> [!NOTE] +> [!NOTE] > All middleware are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a middleware's constructor. @@ -325,7 +325,7 @@ If you would like to manually manage all of the middleware within Laravel's defa }) ``` -> [!NOTE] +> [!NOTE] > By default, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `bootstrap/app.php` file. diff --git a/migrations.md b/migrations.md index 0a44d45fbd1..34499bb1c23 100644 --- a/migrations.md +++ b/migrations.md @@ -44,7 +44,7 @@ Laravel will use the name of the migration to attempt to guess the name of the t If you would like to specify a custom path for the generated migration, you may use the `--path` option when executing the `make:migration` command. The given path should be relative to your application's base path. -> [!NOTE] +> [!NOTE] > Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). @@ -70,7 +70,7 @@ php artisan schema:dump --database=testing --prune You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure. -> [!WARNING] +> [!WARNING] > Migration squashing is only available for the MariaDB, MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. @@ -165,7 +165,7 @@ When the `isolated` option is provided, Laravel will acquire an atomic lock usin php artisan migrate --isolated ``` -> [!WARNING] +> [!WARNING] > To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. @@ -245,7 +245,7 @@ By default, the `migrate:fresh` command only drops tables from the default datab php artisan migrate:fresh --database=admin ``` -> [!WARNING] +> [!WARNING] > The `migrate:fresh` command will drop all database tables regardless of their prefix. This command should be used with caution when developing on a database that is shared with other applications. @@ -722,7 +722,7 @@ The `geography` method creates a `GEOGRAPHY` equivalent column with the given sp $table->geography('coordinates', subtype: 'point', srid: 4326); ``` -> [!NOTE] +> [!NOTE] > Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the [PostGIS](https://postgis.net) extension before the `geography` method may be used. @@ -734,7 +734,7 @@ The `geometry` method creates a `GEOMETRY` equivalent column with the given spat $table->geometry('positions', subtype: 'point', srid: 0); ``` -> [!NOTE] +> [!NOTE] > Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the [PostGIS](https://postgis.net) extension before the `geometry` method may be used. @@ -1229,7 +1229,7 @@ return new class extends Migration }; ``` -> [!WARNING] +> [!WARNING] > Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. @@ -1508,7 +1508,7 @@ Schema::withoutForeignKeyConstraints(function () { }); ``` -> [!WARNING] +> [!WARNING] > SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. diff --git a/mix.md b/mix.md index 91cf5c66506..59ec23ea1ff 100644 --- a/mix.md +++ b/mix.md @@ -16,5 +16,5 @@ mix.js('resources/js/app.js', 'public/js') 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. -> [!NOTE] +> [!NOTE] > Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [official Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [Vite migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). diff --git a/mocking.md b/mocking.md index dd9673b91dd..7b707a649b3 100644 --- a/mocking.md +++ b/mocking.md @@ -154,7 +154,7 @@ class UserControllerTest extends TestCase } ``` -> [!WARNING] +> [!WARNING] > 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. diff --git a/mongodb.md b/mongodb.md index e63872f32f3..5aaf4927959 100644 --- a/mongodb.md +++ b/mongodb.md @@ -66,7 +66,7 @@ Finally, use Composer to install the Laravel MongoDB package: composer require mongodb/laravel-mongodb ``` -> [!NOTE] +> [!NOTE] > This installation of the package will fail if the `mongodb` PHP extension is not installed. The PHP configuration can differ between the CLI and the web server, so ensure the extension is enabled in both configurations. diff --git a/notifications.md b/notifications.md index d0c5bedc83d..65ddc93f75d 100644 --- a/notifications.md +++ b/notifications.md @@ -831,7 +831,7 @@ Thanks,
    ``` -> [!NOTE] +> [!NOTE] > Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. diff --git a/octane.md b/octane.md index 3149aa86c92..7e5fe5eee14 100644 --- a/octane.md +++ b/octane.md @@ -47,7 +47,7 @@ php artisan octane:install ## Server Prerequisites -> [!WARNING] +> [!WARNING] > Laravel Octane requires [PHP 8.1+](https://php.net/releases/). @@ -204,7 +204,7 @@ Using Laravel Octane with Open Swoole grants the same functionality provided by #### Swoole via Laravel Sail -> [!WARNING] +> [!WARNING] > Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. Alternatively, you may develop your Swoole based Octane application using [Laravel Sail](/docs/{{version}}/sail), the official Docker based development environment for Laravel. Laravel Sail includes the Swoole extension by default. However, you will still need to adjust the `docker-compose.yml` file used by Sail. @@ -261,7 +261,7 @@ By default, applications running via Octane generate links prefixed with `http:/ ### Serving Your Application via Nginx -> [!NOTE] +> [!NOTE] > If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Cloud](https://cloud.laravel.com), which offers fully-managed Laravel Octane support. In production environments, you should serve your Octane application behind a traditional web server such as Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. @@ -482,7 +482,7 @@ $service->method($request->input('name')); The global `request` helper will always return the request the application is currently handling and is therefore safe to use within your application. -> [!WARNING] +> [!WARNING] > It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. @@ -553,7 +553,7 @@ While building your application, you should take special care to avoid creating ## Concurrent Tasks -> [!WARNING] +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's `concurrently` method. You may combine this method with PHP array destructuring to retrieve the results of each operation: @@ -580,7 +580,7 @@ When invoking the `concurrently` method, you should not provide more than 1024 t ## Ticks and Intervals -> [!WARNING] +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may register "tick" operations that will be executed every specified number of seconds. You may register "tick" callbacks via the `tick` method. The first argument provided to the `tick` method should be a string that represents the name of the ticker. The second argument should be a callable that will be invoked at the specified interval. @@ -603,7 +603,7 @@ Octane::tick('simple-ticker', fn () => ray('Ticking...')) ## The Octane Cache -> [!WARNING] +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. Therefore, this cache driver is an excellent choice for applications that need extreme read / write speeds from their caching layer. @@ -614,7 +614,7 @@ This cache driver is powered by [Swoole tables](https://www.swoole.co.uk/docs/mo Cache::store('octane')->put('framework', 'Laravel', 30); ``` -> [!NOTE] +> [!NOTE] > The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. @@ -633,7 +633,7 @@ Cache::store('octane')->interval('random', function () { ## Tables -> [!WARNING] +> [!WARNING] > This feature requires [Swoole](#swoole). When using Swoole, you may define and interact with your own arbitrary [Swoole tables](https://www.swoole.co.uk/docs/modules/swoole-table). Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server. However, the data within them will be lost when the server is restarted. @@ -662,5 +662,5 @@ Octane::table('example')->set('uuid', [ return Octane::table('example')->get('uuid'); ``` -> [!WARNING] +> [!WARNING] > The column types supported by Swoole tables are: `string`, `int`, and `float`. diff --git a/packages.md b/packages.md index 17b55832261..d5bdb223d70 100644 --- a/packages.md +++ b/packages.md @@ -111,7 +111,7 @@ Now, when users of your package execute Laravel's `vendor:publish` command, your $value = config('courier.option'); ``` -> [!WARNING] +> [!WARNING] > You should not define closures in your configuration files. They cannot be serialized correctly when users execute the `config:cache` Artisan command. @@ -133,7 +133,7 @@ public function register(): void } ``` -> [!WARNING] +> [!WARNING] > This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. diff --git a/pagination.md b/pagination.md index fba4710e17c..42c6ac57ada 100644 --- a/pagination.md +++ b/pagination.md @@ -137,7 +137,7 @@ $users = DB::table('users')->orderBy('id')->cursorPaginate(15); Once you have retrieved a cursor paginator instance, you may [display the pagination results](#displaying-pagination-results) as you typically would when using the `paginate` and `simplePaginate` methods. For more information on the instance methods offered by the cursor paginator, please consult the [cursor paginator instance method documentation](#cursor-paginator-instance-methods). -> [!WARNING] +> [!WARNING] > Your query must contain an "order by" clause in order to take advantage of cursor pagination. In addition, the columns that the query are ordered by must belong to the table you are paginating. @@ -174,7 +174,7 @@ The `Paginator` and `CursorPaginator` classes do not need to know the total numb In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, the `CursorPaginator` corresponds to the `cursorPaginate` method, and the `LengthAwarePaginator` corresponds to the `paginate` method. -> [!WARNING] +> [!WARNING] > 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. diff --git a/passport.md b/passport.md index 952d5a5fb24..9c0fc9ece12 100644 --- a/passport.md +++ b/passport.md @@ -48,7 +48,7 @@ [Laravel Passport](https://github.com/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. -> [!WARNING] +> [!WARNING] > 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. @@ -185,7 +185,7 @@ public function boot(): void } ``` -> [!WARNING] +> [!WARNING] > 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). @@ -389,7 +389,7 @@ If the `prompt` value is `none`, Passport will always throw an authentication er If no `prompt` value is provided, the user will be prompted for authorization only if they have not previously authorized access to the consuming application for the requested scopes. -> [!NOTE] +> [!NOTE] > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. @@ -456,7 +456,7 @@ Route::get('/callback', function (Request $request) { 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. -> [!NOTE] +> [!NOTE] > Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. @@ -655,7 +655,7 @@ Route::get('/callback', function (Request $request) { ## Password Grant Tokens -> [!WARNING] +> [!WARNING] > We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). 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. @@ -701,7 +701,7 @@ $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ return $response->json(); ``` -> [!NOTE] +> [!NOTE] > Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. @@ -787,7 +787,7 @@ class User extends Authenticatable ## Implicit Grant Tokens -> [!WARNING] +> [!WARNING] > We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). 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 application's `App\Providers\AppServiceProvider` class: @@ -823,7 +823,7 @@ Route::get('/redirect', function (Request $request) { }); ``` -> [!NOTE] +> [!NOTE] > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. @@ -888,7 +888,7 @@ return $response->json()['access_token']; Sometimes, your users may want to issue access tokens to themselves without going through the typical authorization code redirect flow. Allowing users to issue tokens to themselves via your application's UI can be useful for allowing users to experiment with your API or may serve as a simpler approach to issuing access tokens in general. -> [!NOTE] +> [!NOTE] > If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. @@ -998,7 +998,7 @@ Route::get('/user', function () { })->middleware('auth:api'); ``` -> [!WARNING] +> [!WARNING] > If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. @@ -1026,7 +1026,7 @@ Route::get('/customer', function () { })->middleware('auth:api-customers'); ``` -> [!NOTE] +> [!NOTE] > For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). @@ -1087,7 +1087,7 @@ Passport::setDefaultScope([ ]); ``` -> [!NOTE] +> [!NOTE] > Passport's default scopes do not apply to personal access tokens that are generated by the user. @@ -1220,7 +1220,7 @@ use Laravel\Passport\Http\Middleware\CreateFreshApiToken; }) ``` -> [!WARNING] +> [!WARNING] > You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. 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: @@ -1252,7 +1252,7 @@ public function boot(): void When using this method of authentication, you will need to ensure a valid CSRF token header is included in your requests. The default Laravel JavaScript scaffolding includes an Axios instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. -> [!NOTE] +> [!NOTE] > If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. diff --git a/passwords.md b/passwords.md index e9682f30634..d12655a9a15 100644 --- a/passwords.md +++ b/passwords.md @@ -15,7 +15,7 @@ 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. -> [!NOTE] +> [!NOTE] > 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. @@ -86,12 +86,12 @@ Before moving on, let's examine this route in more detail. First, the request's 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 `lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. -> [!NOTE] +> [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. You may be 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). -> [!NOTE] +> [!NOTE] > 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). diff --git a/pennant.md b/pennant.md index d85ee7781aa..ba84e245950 100644 --- a/pennant.md +++ b/pennant.md @@ -152,7 +152,7 @@ use Illuminate\Support\Facades\Feature; $instance = Feature::instance(NewApi::class); ``` -> [!NOTE] +> [!NOTE] > Feature classes are resolved via the [container](/docs/{{version}}/container), so you may inject dependencies into the feature class's constructor when needed. #### Customizing the Stored Feature Name @@ -234,7 +234,7 @@ Feature::allAreInactive(['new-api', 'site-redesign']); Feature::someAreInactive(['new-api', 'site-redesign']); ``` -> [!NOTE] +> [!NOTE] > When using Pennant outside of an HTTP context, such as in an Artisan command or a queued job, you should typically [explicitly specify the feature's scope](#specifying-the-scope). Alternatively, you may define a [default scope](#default-scope) that accounts for both authenticated HTTP contexts and unauthenticated contexts. @@ -699,7 +699,7 @@ Pennant's included Blade directive also makes it easy to conditionally render co @endfeature ``` -> [!NOTE] +> [!NOTE] > When using rich values, it is important to know that a feature is considered "active" when it has any value other than `false`. When calling the [conditional `when`](#conditional-execution) method, the feature's rich value will be provided to the first closure: @@ -880,7 +880,7 @@ Alternatively, you may deactivate the feature for all users: Feature::deactivateForEveryone('new-api'); ``` -> [!NOTE] +> [!NOTE] > This will only update the resolved feature values that have been stored by Pennant's storage driver. You will also need to update the feature definition in your application. diff --git a/precognition.md b/precognition.md index 062602f4cb9..5de23b8018b 100644 --- a/precognition.md +++ b/precognition.md @@ -133,7 +133,7 @@ You may also determine if an input has passed or failed validation by passing th ``` -> [!WARNING] +> [!WARNING] > A form input will only appear as valid or invalid once it has changed and a validation response has been received. If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's `forgetError` function to achieve this: @@ -156,7 +156,7 @@ To do this with Precognition, you should call the `validate` method passing the ```html + + ); +} +``` + +```vue tab=Vue + + + +``` + +When sending data back to the stream via `send`, the active connection to the stream is canceled before sending the new data. All requests are sent as JSON `POST` requests. + +The second argument given to `useStream` is an options object that you may use to customize the stream consumption behavior. The default values for this object are shown below: + +```tsx tab=React +import { useStream } from "@laravel/stream-react"; + +function App() { + const { data } = useStream("chat", { + id: undefined, + initialInput: undefined, + headers: undefined, + csrfToken: undefined, + onResponse: (response: Response) => void, + onData: (data: string) => void, + onCancel: () => void, + onFinish: () => void, + onError: (error: Error) => void, + }); + + return
    {data}
    ; +} +``` + +```vue tab=Vue + + + +``` + +`onResponse` is triggered after a successful initial response from the stream and the raw [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) is passed to the callback. `onData` is called as each chunk is received - the current chunk is passed to the callback. `onFinish` is called when a stream has finished and when an error is thrown during the fetch / read cycle. + +By default, a request is not made to the stream on initialization. You may pass an initial payload to the stream by using the `initialInput` option: + +```tsx tab=React +import { useStream } from "@laravel/stream-react"; + +function App() { + const { data } = useStream("chat", { + initialInput: { + message: "Introduce yourself.", + }, + }); + + return
    {data}
    ; +} +``` + +```vue tab=Vue + + + +``` + +To cancel a stream manually, you may use the `cancel` method returned from the hook: + +```tsx tab=React +import { useStream } from "@laravel/stream-react"; + +function App() { + const { data, cancel } = useStream("chat"); + + return ( +
    +
    {data}
    + +
    + ); +} +``` + +```vue tab=Vue + + + +``` + +Each time the `useStream` hook is used, a random `id` is generated to identify the stream. This is sent back to the server with each request in the `X-STREAM-ID` header. When consuming the same stream from multiple components, you can read and write to the stream by providing your own `id`: + +```tsx tab=React +// App.tsx +import { useStream } from "@laravel/stream-react"; + +function App() { + const { data, id } = useStream("chat"); + + return ( +
    +
    {data}
    + +
    + ); +} + +// StreamStatus.tsx +import { useStream } from "@laravel/stream-react"; + +function StreamStatus({ id }) { + const { isFetching, isStreaming } = useStream("chat", { id }); + + return ( +
    + {isFetching &&
    Connecting...
    } + {isStreaming &&
    Generating...
    } +
    + ); +} +``` + +```vue tab=Vue + + + + + + + + + +``` -#### Streamed JSON Responses +### Streamed JSON Responses If you need to stream JSON data incrementally, you may utilize the `streamJson` method. This method is especially useful for large datasets that need to be sent progressively to the browser in a format that can be easily parsed by JavaScript: @@ -400,8 +650,74 @@ Route::get('/users.json', function () { }); ``` +The `useJsonStream` hook is identical to the [`useStream` hook](#consuming-streamed-responses) except that it will attempt to parse the data as JSON once it has finished streaming: + +```tsx tab=React +import { useJsonStream } from "@laravel/stream-react"; + +type User = { + id: number; + name: string; + email: string; +}; + +function App() { + const { data, send } = useJsonStream<{ users: User[] }>("users"); + + const loadUsers = () => { + send({ + query: "taylor", + }); + }; + + return ( +
    +
      + {data?.users.map((user) => ( +
    • + {user.id}: {user.name} +
    • + ))} +
    + +
    + ); +} +``` + +```vue tab=Vue + + + +``` + -#### Event Streams +### Event Streams (SSE) The `eventStream` method may be used to return a server-sent events (SSE) streamed response using the `text/event-stream` content type. The `eventStream` method accepts a closure which should [yield](https://www.php.net/manual/en/language.generators.overview.php) responses to the stream as the responses become available: @@ -428,6 +744,9 @@ yield new StreamedEvent( ); ``` + +#### Consuming Event Streams + Event streams may be consumed using Laravel's `stream` npm package, which provides a convenient API for interacting with Laravel event streams. To get started, install the `@laravel/stream-react` or `@laravel/stream-vue` package: ```shell tab=React @@ -533,7 +852,7 @@ return response()->eventStream(function () { ``` -#### Streamed Downloads +### 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, filename, and an optional array of headers as its arguments: From 3d227ed6a7101a9a03fe37ff67812d73b593e57d Mon Sep 17 00:00:00 2001 From: Giga <36007921+gigabites19@users.noreply.github.com> Date: Thu, 22 May 2025 18:52:55 +0400 Subject: [PATCH 2241/2609] Update default node version installed by laravel/sail (#10429) --- sail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sail.md b/sail.md index 5f4d5cfc32d..5466f0fe76f 100644 --- a/sail.md +++ b/sail.md @@ -442,7 +442,7 @@ sail up ## Node Versions -Sail installs Node 20 by default. To change the Node version that is installed when building your images, you may update the `build.args` definition of the `laravel.test` service in your application's `docker-compose.yml` file: +Sail installs Node 22 by default. To change the Node version that is installed when building your images, you may update the `build.args` definition of the `laravel.test` service in your application's `docker-compose.yml` file: ```yaml build: From b5bc9a47096955e048575bd6f5b2ed9a9602d9ca Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 22 May 2025 17:56:51 +0300 Subject: [PATCH 2242/2609] Clarify retryUntil() precedence over tries (#10427) --- events.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/events.md b/events.md index 076d88f6974..dbacb51542f 100644 --- a/events.md +++ b/events.md @@ -535,6 +535,8 @@ public function retryUntil(): DateTime } ``` +If both `retryUntil` and `tries` are defined, Laravel gives precedence to the `retryUntil` method. + #### Specifying Queued Listener Backoff From ca629f9a9bcdfa50a48d695cae2642e9b43410eb Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 22 May 2025 17:57:32 +0300 Subject: [PATCH 2243/2609] Is the defer function still in beta? (#10426) --- helpers.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/helpers.md b/helpers.md index e6c31d8648b..7d7df1fb605 100644 --- a/helpers.md +++ b/helpers.md @@ -502,9 +502,9 @@ use Illuminate\Support\Arr; Arr::from((object) ['foo' => 'bar']); // ['foo' => 'bar'] -class TestJsonableObject implements Jsonable +class TestJsonableObject implements Jsonable { - public function toJson($options = 0) + public function toJson($options = 0) { return json_encode(['foo' => 'bar']); } @@ -3067,9 +3067,6 @@ For a thorough discussion of Carbon and its features, please consult the [offici ### Deferred Functions -> [!WARNING] -> Deferred functions are currently in beta while we gather community feedback. - While Laravel's [queued jobs](/docs/{{version}}/queues) allow you to queue tasks for background processing, sometimes you may have simple tasks you would like to defer without configuring or maintaining a long-running queue worker. Deferred functions allow you to defer the execution of a closure until after the HTTP response has been sent to the user, keeping your application feeling fast and responsive. To defer the execution of a closure, simply pass the closure to the `Illuminate\Support\defer` function: From 9995b1a85d09f1fdeccc1e9dbd43d32be8e0789c Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 22 May 2025 17:57:46 +0300 Subject: [PATCH 2244/2609] Is the Concurrency facade still in beta? (#10424) --- concurrency.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/concurrency.md b/concurrency.md index e29cd916710..95559041448 100644 --- a/concurrency.md +++ b/concurrency.md @@ -7,9 +7,6 @@ ## Introduction -> [!WARNING] -> Laravel's `Concurrency` facade is currently in beta while we gather community feedback. - Sometimes you may need to execute several slow tasks which do not depend on one another. In many cases, significant performance improvements can be realized by executing the tasks concurrently. Laravel's `Concurrency` facade provides a simple, convenient API for executing closures concurrently. From 518e070aaeb0aba93b57a38609de12abb6fedbe6 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 22 May 2025 17:59:33 +0300 Subject: [PATCH 2245/2609] Update the output of `dd` and `dump` methods (#10422) --- collections.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/collections.md b/collections.md index 9bbcf2a15e7..8e010c260da 100644 --- a/collections.md +++ b/collections.md @@ -697,12 +697,10 @@ $collection = collect(['John Doe', 'Jane Doe']); $collection->dd(); /* - Collection { - #items: array:2 [ - 0 => "John Doe" - 1 => "Jane Doe" - ] - } + array:2 [ + 0 => "John Doe" + 1 => "Jane Doe" + ] */ ``` @@ -871,12 +869,10 @@ $collection = collect(['John Doe', 'Jane Doe']); $collection->dump(); /* - Collection { - #items: array:2 [ - 0 => "John Doe" - 1 => "Jane Doe" - ] - } + array:2 [ + 0 => "John Doe" + 1 => "Jane Doe" + ] */ ``` From c5f1f472c66dbf36844bf3461a23b3ce16753004 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 22 May 2025 18:01:06 +0300 Subject: [PATCH 2246/2609] [12.x] Clarify where the code should be placed (#10423) * Clarify where the code should be placed * Update context.md --------- Co-authored-by: Taylor Otwell --- context.md | 1 + 1 file changed, 1 insertion(+) diff --git a/context.md b/context.md index 7d90914d33a..51c119cce3b 100644 --- a/context.md +++ b/context.md @@ -227,6 +227,7 @@ Stacks can be useful to capture historical information about a request, such as use Illuminate\Support\Facades\Context; use Illuminate\Support\Facades\DB; +// In AppServiceProvider.php... DB::listen(function ($event) { Context::push('queries', [$event->time, $event->sql]); }); From eac0713425b308ab774271836a0130d357cc6131 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 23 May 2025 17:33:20 +0300 Subject: [PATCH 2247/2609] Remove outdated upgrade note from Laravel 12 docs (#10434) --- helpers.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/helpers.md b/helpers.md index 7d7df1fb605..819322de99d 100644 --- a/helpers.md +++ b/helpers.md @@ -3103,19 +3103,6 @@ defer(fn () => Metrics::report(), 'reportMetrics'); defer()->forget('reportMetrics'); ``` - -#### Deferred Function Compatibility - -If you upgraded to Laravel 11.x from a Laravel 10.x application and your application's skeleton still contains an `app/Http/Kernel.php` file, you should add the `InvokeDeferredCallbacks` middleware to the beginning of the kernel's `$middleware` property: - -```php -protected $middleware = [ - \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, // [tl! add] - \App\Http\Middleware\TrustProxies::class, - // ... -]; -``` - #### Disabling Deferred Functions in Tests From a15a6384c2e4a74f448bca9ef9cece49bc168547 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 23 May 2025 17:33:32 +0300 Subject: [PATCH 2248/2609] Remove outdated upgrade note from Laravel 12 docs (#10433) --- concurrency.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/concurrency.md b/concurrency.md index 95559041448..9a412ee2b60 100644 --- a/concurrency.md +++ b/concurrency.md @@ -9,29 +9,6 @@ Sometimes you may need to execute several slow tasks which do not depend on one another. In many cases, significant performance improvements can be realized by executing the tasks concurrently. Laravel's `Concurrency` facade provides a simple, convenient API for executing closures concurrently. - -#### Concurrency Compatibility - -If you upgraded to Laravel 11.x from a Laravel 10.x application, you may need to add the `ConcurrencyServiceProvider` to the `providers` array in your application's `config/app.php` configuration file: - -```php -'providers' => ServiceProvider::defaultProviders()->merge([ - /* - * Package Service Providers... - */ - Illuminate\Concurrency\ConcurrencyServiceProvider::class, // [tl! add] - - /* - * Application Service Providers... - */ - App\Providers\AppServiceProvider::class, - App\Providers\AuthServiceProvider::class, - // App\Providers\BroadcastServiceProvider::class, - App\Providers\EventServiceProvider::class, - App\Providers\RouteServiceProvider::class, -])->toArray(), -``` - #### How it Works From 3d31241b90cf1cd91e8dd8b9559d51631d944e9a Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 23 May 2025 17:33:45 +0300 Subject: [PATCH 2249/2609] Format the code with Pint (#10431) --- container.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/container.md b/container.md index d60dd934da6..cf5ee304ced 100644 --- a/container.md +++ b/container.md @@ -323,8 +323,7 @@ class PhotoController extends Controller #[Log('daily')] protected LoggerInterface $log, #[RouteParameter('photo')] protected Photo $photo, #[Tag('reports')] protected iterable $reports, - ) - { + ) { // ... } } From cdcc447d88cedebae3fbcf7d36332b694a9a64e2 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 23 May 2025 17:34:04 +0300 Subject: [PATCH 2250/2609] [12.x] Update version support table (#10435) * Update version support table * wip --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index f01fae8f3e0..7a8fe607679 100644 --- a/releases.md +++ b/releases.md @@ -25,10 +25,10 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | | --- | --- | --- | --- | --- | -| 9 | 8.0 - 8.2 | February 8th, 2022 | August 8th, 2023 | February 6th, 2024 | | 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | | 11 | 8.2 - 8.4 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | | 12 | 8.2 - 8.4 | February 24th, 2025 | August 13th, 2026 | February 24th, 2027 | +| 13 | 8.3 - 8.4 | Q1 2026 | Q3 2027 | Q1 2028 | From b8f6edf6564191b9c3cddcbaa17b898c98a2b995 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 23 May 2025 17:34:18 +0300 Subject: [PATCH 2251/2609] Clarify behavior of pop method when the collection is empty (#10436) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index 8e010c260da..bd7a951f10e 100644 --- a/collections.md +++ b/collections.md @@ -2254,7 +2254,7 @@ $plucked->all(); #### `pop()` {.collection-method} -The `pop` method removes and returns the last item from the collection: +The `pop` method removes and returns the last item from the collection. If the collection is empty, `null` will be returned: ```php $collection = collect([1, 2, 3, 4, 5]); From 93c218dd4bb6928144923a0e2b651bcc17abb24d Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 26 May 2025 20:15:24 +0300 Subject: [PATCH 2252/2609] Should we remove Lumen from the docks? (#10449) --- releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releases.md b/releases.md index 7a8fe607679..5287340d392 100644 --- a/releases.md +++ b/releases.md @@ -19,7 +19,7 @@ When referencing the Laravel framework or its components from your application o ## Support Policy -For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest major release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). +For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, only the latest major release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction).
    From 94f048488b1c148bc0159ed184c26aa62f444133 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 26 May 2025 20:18:48 +0300 Subject: [PATCH 2253/2609] Fix typo in artisan.md (#10446) --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index 9aea15abe34..b53097ba190 100644 --- a/artisan.md +++ b/artisan.md @@ -387,7 +387,7 @@ If you would like to define arguments or options to expect multiple input values 'mail: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 `1` and `2` as its values: +When running this command, 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 `1` and `2` as its values: ```shell php artisan mail:send 1 2 From e565e299fb6e6d2e021f1a36a951e5278dc2c27f Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 26 May 2025 20:20:34 +0300 Subject: [PATCH 2254/2609] Use environment variable (#10444) --- broadcasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index 36ebd35e1bb..7b6fe751ab6 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -368,7 +368,7 @@ import Pusher from 'pusher-js'; const options = { broadcaster: 'pusher', - key: 'your-pusher-channels-key' + key: import.meta.env.VITE_PUSHER_APP_KEY } window.Echo = new Echo({ From 5dbcf7abb81f3dee09d0f03af124f8aa4ea6e42f Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 26 May 2025 20:20:53 +0300 Subject: [PATCH 2255/2609] Improve clarity in event subscriber docs (#10443) --- events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.md b/events.md index dbacb51542f..f2d8de7b885 100644 --- a/events.md +++ b/events.md @@ -658,7 +658,7 @@ class OrderShipped implements ShouldDispatchAfterCommit ### Writing Event Subscribers -Event subscribers are classes that may subscribe to multiple events from within the subscriber class itself, allowing you to define several event handlers within a single class. Subscribers should define a `subscribe` method, which will be passed an event dispatcher instance. You may call the `listen` method on the given dispatcher to register event listeners: +Event subscribers are classes that may subscribe to multiple events from within the subscriber class itself, allowing you to define several event handlers within a single class. Subscribers should define a `subscribe` method, which receives an event dispatcher instance. You may call the `listen` method on the given dispatcher to register event listeners: ```php Date: Mon, 26 May 2025 20:21:30 +0300 Subject: [PATCH 2256/2609] Refine replaceRecursive method description for consistency (#10442) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index bd7a951f10e..949f7f76f62 100644 --- a/collections.md +++ b/collections.md @@ -2509,7 +2509,7 @@ $replaced->all(); #### `replaceRecursive()` {.collection-method} -This method works like `replace`, but it will recur into arrays and apply the same replacement process to the inner values: +The `replaceRecursive` method behaves similarly to `replace`, but it will recur into arrays and apply the same replacement process to the inner values: ```php $collection = collect([ From 00a4c0fd8f241e519a8c72d056121b1cbcd6a79a Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 26 May 2025 20:53:57 +0330 Subject: [PATCH 2257/2609] [12.x] Add `Arr::hasAll` method to helpers (#10441) * add `Arr::hasAll` method to helpers * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/helpers.md b/helpers.md index 819322de99d..a797121cdd2 100644 --- a/helpers.md +++ b/helpers.md @@ -55,6 +55,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::from](#method-array-from) [Arr::get](#method-array-get) [Arr::has](#method-array-has) +[Arr::hasAll](#method-array-hasall) [Arr::hasAny](#method-array-hasany) [Arr::integer](#method-array-integer) [Arr::isAssoc](#method-array-isassoc) @@ -557,6 +558,21 @@ $contains = Arr::has($array, ['product.price', 'product.discount']); // false ``` + +#### `Arr::hasAll()` {.collection-method} + +The `Arr::hasAll` method determines if all of the specified keys exist in the given array using "dot" notation: + +```php +use Illuminate\Support\Arr; + +$array = ['name' => 'Taylor', 'language' => 'php']; + +Arr::hasAll($array, ['name']); // true +Arr::hasAll($array, ['name', 'language']); // true +Arr::hasAll($array, ['name', 'ide']); // false +``` + #### `Arr::hasAny()` {.collection-method} From 10ca661ad531827702383dac4a4fa77e5975d7dd Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 26 May 2025 20:55:32 +0330 Subject: [PATCH 2258/2609] [12.x] Update Passport documentation (#10439) * fix a typo * formatting * Update passport.md --------- Co-authored-by: Taylor Otwell --- passport.md | 41 +++++++++++++++++++++++++---------------- upgrade.md | 8 ++++---- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/passport.md b/passport.md index cb0c3484180..94ab791d215 100644 --- a/passport.md +++ b/passport.md @@ -255,6 +255,7 @@ To get started, we need to instruct Passport how to return our "authorization" v All the authorization view's rendering logic may be customized using the appropriate methods available via the `Laravel\Passport\Passport` class. Typically, you should call this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class: ```php +use Inertia\Inertia; use Laravel\Passport\Passport; /** @@ -638,6 +639,7 @@ To get started, we need to instruct Passport how to return our "user code" and " All the authorization view's rendering logic may be customized using the appropriate methods available via the `Laravel\Passport\Passport` class. Typically, you should call this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class. ```php +use Inertia\Inertia; use Laravel\Passport\Passport; /** @@ -650,15 +652,19 @@ public function boot(): void Passport::deviceAuthorizationView('auth.oauth.device.authorize'); // By providing a closure... - Passport::deviceUserCodeView(fn ($parameters) => Inertia::render('Auth/OAuth/Device/UserCode')); + Passport::deviceUserCodeView( + fn ($parameters) => Inertia::render('Auth/OAuth/Device/UserCode') + ); - Passport::deviceAuthorizationView(fn ($parameters) => Inertia::render('Auth/OAuth/Device/Authorize', [ - 'request' => $parameters['request'], - 'authToken' => $parameters['authToken'], - 'client' => $parameters['client'], - 'user' => $parameters['user'], - 'scopes' => $parameters['scopes'], - ])); + Passport::deviceAuthorizationView( + fn ($parameters) => Inertia::render('Auth/OAuth/Device/Authorize', [ + 'request' => $parameters['request'], + 'authToken' => $parameters['authToken'], + 'client' => $parameters['client'], + 'user' => $parameters['user'], + 'scopes' => $parameters['scopes'], + ]) + ); // ... } @@ -738,7 +744,7 @@ do { $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', 'client_id' => 'your-client-id', - 'client_secret' => 'your-client-secret', // required for confidential clients only + 'client_secret' => 'your-client-secret', // Required for confidential clients only... 'device_code' => 'the-device-code', ]); @@ -792,7 +798,7 @@ use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', - 'client_secret' => 'your-client-secret', // required for confidential clients only + 'client_secret' => 'your-client-secret', // Required for confidential clients only... 'username' => 'taylor@laravel.com', 'password' => 'my-password', 'scope' => 'user:read orders:create', @@ -815,7 +821,7 @@ use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', - 'client_secret' => 'your-client-secret', // required for confidential clients only + 'client_secret' => 'your-client-secret', // Required for confidential clients only... 'username' => 'taylor@laravel.com', 'password' => 'my-password', 'scope' => '*', @@ -839,9 +845,10 @@ namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Passport\Contracts\OAuthenticatable; use Laravel\Passport\HasApiTokens; -class User extends Authenticatable +class User extends Authenticatable implements OAuthenticatable { use HasApiTokens, Notifiable; @@ -868,9 +875,10 @@ namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Facades\Hash; +use Laravel\Passport\Contracts\OAuthenticatable; use Laravel\Passport\HasApiTokens; -class User extends Authenticatable +class User extends Authenticatable implements OAuthenticatable { use HasApiTokens, Notifiable; @@ -1127,9 +1135,9 @@ If a client does not request any specific scopes, you may configure your Passpor use Laravel\Passport\Passport; Passport::tokensCan([ - 'user:read' => 'Retrieve the user info', - 'orders:create' => 'Place orders', - 'orders:read:status' => 'Check order status', + 'user:read' => 'Retrieve the user info', + 'orders:create' => 'Place orders', + 'orders:read:status' => 'Check order status', ]); Passport::defaultScopes([ @@ -1305,6 +1313,7 @@ Passport raises events when issuing access tokens and refresh tokens. You may [l | Event Name | | --- | | `Laravel\Passport\Events\AccessTokenCreated` | +| `Laravel\Passport\Events\AccessTokenRevoked` | | `Laravel\Passport\Events\RefreshTokenCreated` |
    diff --git a/upgrade.md b/upgrade.md index 2c3ddb3e309..88af28d3c19 100644 --- a/upgrade.md +++ b/upgrade.md @@ -160,10 +160,10 @@ The `Schema::getTables()`, `Schema::getViews()`, and `Schema::getTypes()` method $tables = Schema::getTables(); // All tables on the 'main' schema... -$table = Schema::getTables(schema: 'main'); +$tables = Schema::getTables(schema: 'main'); // All tables on the 'main' and 'blog' schemas... -$table = Schema::getTables(schema: ['main', 'blog']); +$tables = Schema::getTables(schema: ['main', 'blog']); ``` The `Schema::getTableListing()` method now returns schema-qualified table names by default. You may pass the `schemaQualified` argument to change the behavior as desired: @@ -172,10 +172,10 @@ The `Schema::getTableListing()` method now returns schema-qualified table names $tables = Schema::getTableListing(); // ['main.migrations', 'main.users', 'blog.posts'] -$table = Schema::getTableListing(schema: 'main'); +$tables = Schema::getTableListing(schema: 'main'); // ['main.migrations', 'main.users'] -$table = Schema::getTableListing(schema: 'main', schemaQualified: false); +$tables = Schema::getTableListing(schema: 'main', schemaQualified: false); // ['migrations', 'users'] ``` From d62aa21a3514ea379fab16382aa963ae31d570ef Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 27 May 2025 18:31:06 +0300 Subject: [PATCH 2259/2609] Improving the return type annotation (#10453) --- events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events.md b/events.md index f2d8de7b885..7ad8548f8bd 100644 --- a/events.md +++ b/events.md @@ -569,7 +569,7 @@ You may easily configure "exponential" backoffs by returning an array of backoff /** * Calculate the number of seconds to wait before retrying the queued listener. * - * @return array + * @return list */ public function backoff(): array { From 17a836491765b8f892ae68fb477bff7079ec1b5c Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 27 May 2025 18:31:51 +0300 Subject: [PATCH 2260/2609] Fix capitalization of proper names and nouns (#10451) --- collections.md | 36 ++++++++++++++++++------------------ helpers.md | 8 ++++---- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/collections.md b/collections.md index 949f7f76f62..dafbc1de412 100644 --- a/collections.md +++ b/collections.md @@ -17,7 +17,7 @@ The `Illuminate\Support\Collection` class provides a fluent, convenient wrapper for working with arrays of data. For example, check out the following code. We'll use the `collect` helper to create a new collection instance from the array, run the `strtoupper` function on each element, and then remove all empty elements: ```php -$collection = collect(['taylor', 'abigail', null])->map(function (?string $name) { +$collection = collect(['Taylor', 'Abigail', null])->map(function (?string $name) { return strtoupper($name); })->reject(function (string $name) { return empty($name); @@ -1152,9 +1152,9 @@ The `flatten` method flattens a multi-dimensional collection into a single dimen ```php $collection = collect([ - 'name' => 'taylor', + 'name' => 'Taylor', 'languages' => [ - 'php', 'javascript' + 'PHP', 'JavaScript' ] ]); @@ -1162,7 +1162,7 @@ $flattened = $collection->flatten(); $flattened->all(); -// ['taylor', 'php', 'javascript']; +// ['Taylor', 'PHP', 'JavaScript']; ``` If necessary, you may pass the `flatten` method a "depth" argument: @@ -1203,13 +1203,13 @@ In this example, calling `flatten` without providing the depth would have also f The `flip` method swaps the collection's keys with their corresponding values: ```php -$collection = collect(['name' => 'taylor', 'framework' => 'laravel']); +$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']); $flipped = $collection->flip(); $flipped->all(); -// ['taylor' => 'name', 'laravel' => 'framework'] +// ['Taylor' => 'name', 'Laravel' => 'framework'] ``` @@ -1218,12 +1218,12 @@ $flipped->all(); The `forget` method removes an item from the collection by its key: ```php -$collection = collect(['name' => 'taylor', 'framework' => 'laravel']); +$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']); // Forget a single key... $collection->forget('name'); -// ['framework' => 'laravel'] +// ['framework' => 'Laravel'] // Forget multiple keys... $collection->forget(['name', 'framework']); @@ -1272,17 +1272,17 @@ $collection = Collection::fromJson($json); The `get` method returns the item at a given key. If the key does not exist, `null` is returned: ```php -$collection = collect(['name' => 'taylor', 'framework' => 'laravel']); +$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']); $value = $collection->get('name'); -// taylor +// Taylor ``` You may optionally pass a default value as the second argument: ```php -$collection = collect(['name' => 'taylor', 'framework' => 'laravel']); +$collection = collect(['name' => 'Taylor', 'framework' => 'Laravel']); $value = $collection->get('age', 34); @@ -3651,20 +3651,20 @@ For the inverse of `whenEmpty`, see the [whenNotEmpty](#method-whennotempty) met The `whenNotEmpty` method will execute the given callback when the collection is not empty: ```php -$collection = collect(['michael', 'tom']); +$collection = collect(['Michael', 'Tom']); $collection->whenNotEmpty(function (Collection $collection) { - return $collection->push('adam'); + return $collection->push('Adam'); }); $collection->all(); -// ['michael', 'tom', 'adam'] +// ['Michael', 'Tom', 'Adam'] $collection = collect(); $collection->whenNotEmpty(function (Collection $collection) { - return $collection->push('adam'); + return $collection->push('Adam'); }); $collection->all(); @@ -3678,14 +3678,14 @@ A second closure may be passed to the `whenNotEmpty` method that will be execute $collection = collect(); $collection->whenNotEmpty(function (Collection $collection) { - return $collection->push('adam'); + return $collection->push('Adam'); }, function (Collection $collection) { - return $collection->push('taylor'); + return $collection->push('Taylor'); }); $collection->all(); -// ['taylor'] +// ['Taylor'] ``` For the inverse of `whenNotEmpty`, see the [whenEmpty](#method-whenempty) method. diff --git a/helpers.md b/helpers.md index a797121cdd2..a37f15f9852 100644 --- a/helpers.md +++ b/helpers.md @@ -566,11 +566,11 @@ The `Arr::hasAll` method determines if all of the specified keys exist in the gi ```php use Illuminate\Support\Arr; -$array = ['name' => 'Taylor', 'language' => 'php']; +$array = ['name' => 'Taylor', 'language' => 'PHP']; Arr::hasAll($array, ['name']); // true Arr::hasAll($array, ['name', 'language']); // true -Arr::hasAll($array, ['name', 'ide']); // false +Arr::hasAll($array, ['name', 'IDE']); // false ``` @@ -2298,7 +2298,7 @@ $traits = class_uses_recursive(App\Models\User::class); The `collect` function creates a [collection](/docs/{{version}}/collections) instance from the given value: ```php -$collection = collect(['taylor', 'abigail']); +$collection = collect(['Taylor', 'Abigail']); ``` @@ -2844,7 +2844,7 @@ The `tap` function accepts two arguments: an arbitrary `$value` and a closure. T ```php $user = tap(User::first(), function (User $user) { - $user->name = 'taylor'; + $user->name = 'Taylor'; $user->save(); }); From cdd572dd913fa0ab05c6b8c1b3b39f1028ae594d Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 27 May 2025 18:41:13 +0300 Subject: [PATCH 2261/2609] [12.x] Clarify custom Artisan command discovery outside default directory (#10454) * Clarify custom Artisan command discovery outside default directory * Update artisan.md --------- Co-authored-by: Taylor Otwell --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index b53097ba190..0e62cbed108 100644 --- a/artisan.md +++ b/artisan.md @@ -108,7 +108,7 @@ Typically, Tinker automatically aliases classes as you interact with them in Tin ## Writing Commands -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. +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 you instruct Laravel to [scan other directories for Artisan commands](#registering-commands). ### Generating Commands From 749f980905661156d686084d95ed0d131695f439 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 27 May 2025 18:44:48 +0300 Subject: [PATCH 2262/2609] Add array method in Request (#10452) --- requests.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/requests.md b/requests.md index 78154a81662..e7476fa7129 100644 --- a/requests.md +++ b/requests.md @@ -394,6 +394,15 @@ When dealing with HTML elements like checkboxes, your application may receive "t $archived = $request->boolean('archived'); ``` + +#### Retrieving Array Input Values + +Input values containing arrays may be retrieved using the `array` method. This method will always cast the input value to an array. If the request does not contain an input value with the given name, an empty array will be returned: + +```php +$versions = $request->array('versions'); +``` + #### Retrieving Date Input Values From fef99d747dbf03b291f6f6393180cee787643678 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 27 May 2025 18:45:08 +0300 Subject: [PATCH 2263/2609] Update Passport documentation (#10450) --- passport.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/passport.md b/passport.md index 94ab791d215..fc0d76c636c 100644 --- a/passport.md +++ b/passport.md @@ -265,15 +265,17 @@ public function boot(): void { // By providing a view name... Passport::authorizationView('auth.oauth.authorize'); - + // By providing a closure... - Passport::authorizationView(fn ($parameters) => Inertia::render('Auth/OAuth/Authorize', [ - 'request' => $parameters['request'], - 'authToken' => $parameters['authToken'], - 'client' => $parameters['client'], - 'user' => $parameters['user'], - 'scopes' => $parameters['scopes'], - ])); + Passport::authorizationView( + fn ($parameters) => Inertia::render('Auth/OAuth/Authorize', [ + 'request' => $parameters['request'], + 'authToken' => $parameters['authToken'], + 'client' => $parameters['client'], + 'user' => $parameters['user'], + 'scopes' => $parameters['scopes'], + ]) + ); } ``` @@ -382,7 +384,7 @@ class Client extends BaseClient /** * Determine if the client should skip the authorization prompt. * - * @param \Laravel\Passport\Scope[] $scopes + * @param \Laravel\Passport\Scope[] $scopes */ public function skipsAuthorization(Authenticatable $user, array $scopes): bool { @@ -650,7 +652,7 @@ public function boot(): void // By providing a view name... Passport::deviceUserCodeView('auth.oauth.device.user-code'); Passport::deviceAuthorizationView('auth.oauth.device.authorize'); - + // By providing a closure... Passport::deviceUserCodeView( fn ($parameters) => Inertia::render('Auth/OAuth/Device/UserCode') @@ -719,7 +721,7 @@ return $response->json(); This will return a JSON response containing `device_code`, `user_code`, `verification_uri`, `interval`, and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the device code expires. The `interval` attribute contains the number of seconds the consuming device should wait between requests when polling `/oauth/token` route to avoid rate limit errors. -> [!NOTE] +> [!NOTE] > Remember, the `/oauth/device/code` route is already defined by Passport. You do not need to manually define this route. @@ -747,7 +749,7 @@ do { 'client_secret' => 'your-client-secret', // Required for confidential clients only... 'device_code' => 'the-device-code', ]); - + if ($response->json('error') === 'slow_down') { $interval += 5; } From f25bb47710f0632a087cd1f4d1921b9d5c999b24 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 May 2025 13:53:21 -0500 Subject: [PATCH 2264/2609] wip --- validation.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/validation.md b/validation.md index ef18ae43883..931b79513e5 100644 --- a/validation.md +++ b/validation.md @@ -1044,6 +1044,7 @@ Below is a list of all available validation rules and their function: [Contains](#rule-contains) [Distinct](#rule-distinct) [In Array](#rule-in-array) +[In Array Keys](#rule-in-array-keys) [List](#rule-list) [Max](#rule-max) [Min](#rule-min) @@ -1771,6 +1772,15 @@ Validator::make($input, [ The field under validation must exist in _anotherfield_'s values. + +#### in_array_keys:_value_.* + +The field under validation must be an array having at least one of the given _values_ as a key within the array: + +```php +'config' => 'array|in_array_keys:timezone' +``` + #### integer From 0272b7874a61a4df12cda7520e0fc7c25e7871e6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 May 2025 13:55:41 -0500 Subject: [PATCH 2265/2609] wip --- strings.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/strings.md b/strings.md index 43dc92adba9..30b1c320938 100644 --- a/strings.md +++ b/strings.md @@ -206,6 +206,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [title](#method-fluent-str-title) [toBase64](#method-fluent-str-to-base64) [toHtmlString](#method-fluent-str-to-html-string) +[toUri](#method-fluent-str-to-uri) [transliterate](#method-fluent-str-transliterate) [trim](#method-fluent-str-trim) [ltrim](#method-fluent-str-ltrim) @@ -3193,6 +3194,17 @@ use Illuminate\Support\Str; $htmlString = Str::of('Nuno Maduro')->toHtmlString(); ``` + +#### `toUri` {.collection-method} + +The `toUri` method converts the given string to an instance of [Illuminate\Support\Uri](/docs/{{version}}/helpers#uri): + +```php +use Illuminate\Support\Str; + +$uri = Str::of('https://example.com/')->toUri(); +``` + #### `transliterate` {.collection-method} From 8c05067e5f899d150e0c91e69dea289b7ea1407b Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Tue, 27 May 2025 17:44:04 -0400 Subject: [PATCH 2266/2609] [12.x] Add documentation for "Rule::contains" (#10457) * Add documentation for new "contains" validation rule * Update validation.md --------- Co-authored-by: Taylor Otwell --- validation.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/validation.md b/validation.md index 931b79513e5..1d4d4df2b28 100644 --- a/validation.md +++ b/validation.md @@ -1356,7 +1356,20 @@ You may also pass a custom confirmation field name. For example, `confirmed:repe #### contains:_foo_,_bar_,... -The field under validation must be an array that contains all of the given parameter values. +The field under validation must be an array that contains all of the given parameter values. Since this rule often requires you to `implode` an array, the `Rule::contains` method may be used to fluently construct the rule: + +```php +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; + +Validator::make($data, [ + 'roles' => [ + 'required', + 'array', + Rule::contains(['admin', 'editor']), + ], +]); +``` #### current_password From b83932c0a3c7b0b3eee78893b9409c2d8af1a7eb Mon Sep 17 00:00:00 2001 From: Caleb White Date: Mon, 2 Jun 2025 09:22:59 -0500 Subject: [PATCH 2267/2609] fix: broken code block (#10465) --- notifications.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notifications.md b/notifications.md index 3d350b58bf5..a2085774ef5 100644 --- a/notifications.md +++ b/notifications.md @@ -944,9 +944,9 @@ public function toArray(object $notifiable): array When a notification is stored in your application's database, the `type` column will be set to the notification's class name by default, and the `read_at` column will be `null`. However, you can customize this behavior by defining the `databaseType` and `initialDatabaseReadAtValue` methods in your notification class: - use Illuminate\Support\Carbon; - ```php +use Illuminate\Support\Carbon; + /** * Get the notification's database type. */ From 9078091ac9e46bfd1746d5ae9932dde19452791e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Jun 2025 09:53:02 -0500 Subject: [PATCH 2268/2609] wip --- eloquent-mutators.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 8f606173431..95b80d4fbe9 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -213,6 +213,7 @@ The `casts` method should return an array where the key is the name of the attri
    - `array` +- `AsUri::class` - `AsStringable::class` - `boolean` - `collection` From 3dbdf1782036f82db0f538c5c02a151ec156852d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Jun 2025 09:57:21 -0500 Subject: [PATCH 2269/2609] wip --- container.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/container.md b/container.md index cf5ee304ced..c85b278b492 100644 --- a/container.md +++ b/container.md @@ -291,7 +291,7 @@ class PhotoController extends Controller } ``` -In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `Context`, `DB`, `Log`, `RouteParameter`, and [Tag](#tagging) attributes: +In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config`, `Context`, `DB`, `Give`, `Log`, `RouteParameter`, and [Tag](#tagging) attributes: ```php Date: Wed, 4 Jun 2025 08:59:05 -0500 Subject: [PATCH 2270/2609] Update responses.md to make easier for new users to use stream package. (#10466) * Update responses.md to make easier for new users to use stream package. Totally understand if these changes don't get merged. Spent a day trying to understand the new streaming docs and had to go to the source of the package to see why I was having issues. The Laravel stream package is hardcoded to use POST requests yet the second example is a GET request. Updating to POST to make it easier to onboard new developers. The CSRF token is not alway present in the head of a layout. Make sure to call this out otherwise new users see 419 without a good error message on how to address. * Update responses.md --------- Co-authored-by: Taylor Otwell --- responses.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/responses.md b/responses.md index 3af163f4040..43235a628d3 100644 --- a/responses.md +++ b/responses.md @@ -384,7 +384,7 @@ Route::get('/stream', function () { For convenience, if the closure you provide to the `stream` method returns a [Generator](https://www.php.net/manual/en/language.generators.overview.php), Laravel will automatically flush the output buffer between strings returned by the generator, as well as disable Nginx output buffering: ```php -Route::get('/chat', function () { +Route::post('/chat', function () { return response()->stream(function (): void { $stream = OpenAI::client()->chat()->createStreamed(...); @@ -458,6 +458,9 @@ const sendMessage = () => { When sending data back to the stream via `send`, the active connection to the stream is canceled before sending the new data. All requests are sent as JSON `POST` requests. +> [!WARNING] +> Since the `useStream` hook makes a `POST` request to your application, a valid CSRF token is required. The easiest way to provide the CSRF token is to [include it in your application layout's `head` tag](/docs/{{version}}/csrf#csrf-x-csrf-token). + The second argument given to `useStream` is an options object that you may use to customize the stream consumption behavior. The default values for this object are shown below: ```tsx tab=React From d37c75d30814473cda3a5737f056fe0527287d26 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 4 Jun 2025 09:00:57 -0500 Subject: [PATCH 2271/2609] wip --- responses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/responses.md b/responses.md index 43235a628d3..35c0a0621f0 100644 --- a/responses.md +++ b/responses.md @@ -459,7 +459,7 @@ const sendMessage = () => { When sending data back to the stream via `send`, the active connection to the stream is canceled before sending the new data. All requests are sent as JSON `POST` requests. > [!WARNING] -> Since the `useStream` hook makes a `POST` request to your application, a valid CSRF token is required. The easiest way to provide the CSRF token is to [include it in your application layout's `head` tag](/docs/{{version}}/csrf#csrf-x-csrf-token). +> Since the `useStream` hook makes a `POST` request to your application, a valid CSRF token is required. The easiest way to provide the CSRF token is to [include it via a `meta` tag in your application layout's `head`](/docs/{{version}}/csrf#csrf-x-csrf-token). The second argument given to `useStream` is an options object that you may use to customize the stream consumption behavior. The default values for this object are shown below: From 8f8374ffe17babb2218ea0faec68d135f47e83de Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 4 Jun 2025 10:46:31 -0400 Subject: [PATCH 2272/2609] useEchoNotification (#10467) * Update notifications.md * Update notifications.md * formatting --------- Co-authored-by: Taylor Otwell --- notifications.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/notifications.md b/notifications.md index a2085774ef5..38813e0b743 100644 --- a/notifications.md +++ b/notifications.md @@ -1094,6 +1094,82 @@ Echo.private('App.Models.User.' + userId) }); ``` + +#### Using React or Vue + +Laravel Echo includes React and Vue hooks that make it painless to listen for notifications. To get started, invoke the `useEchoNotification` hook, which is used to listen for notifications. The `useEchoNotification` hook will automatically leave channels when the consuming component is unmounted: + +```js tab=React +import { useEchoNotification } from "@laravel/echo-react"; + +useEchoNotification( + `App.Models.User.${userId}`, + (notification) => { + console.log(notification.type); + }, +); +``` + +```vue tab=Vue + +``` + +By default, the hook listens to all notifications. To specify the notification types you would like to listen to, you can provide either a string or array of types to `useEchoNotification`: + +```js tab=React +import { useEchoNotification } from "@laravel/echo-react"; + +useEchoNotification( + `App.Models.User.${userId}`, + (notification) => { + console.log(notification.type); + }, + 'App.Notifications.InvoicePaid', +); +``` + +```vue tab=Vue + +``` + +You may also specify the shape of the notification payload data, providing greater type safety and editing convenience: + +```ts +type InvoicePaidNotification = { + invoice_id: number; + created_at: string; +}; + +useEchoNotification( + `App.Models.User.${userId}`, + (notification) => { + console.log(notification.invoice_id); + console.log(notification.created_at); + console.log(notification.type); + }, + 'App.Notifications.InvoicePaid', +); +``` + #### Customizing the Notification Channel From 0ef7bddbfe57e9ac22d8d20aa3134d99658e285a Mon Sep 17 00:00:00 2001 From: Cas Ebbers <617080+CasEbb@users.noreply.github.com> Date: Wed, 4 Jun 2025 16:50:55 +0200 Subject: [PATCH 2273/2609] Add `Str::match` and `Str::matchAll` to 'Strings' (#10468) --- strings.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/strings.md b/strings.md index 30b1c320938..fcacdc61309 100644 --- a/strings.md +++ b/strings.md @@ -67,6 +67,8 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::lower](#method-str-lower) [Str::markdown](#method-str-markdown) [Str::mask](#method-str-mask) +[Str::match](#method-str-match) +[Str::matchAll](#method-str-match-all) [Str::orderedUuid](#method-str-ordered-uuid) [Str::padBoth](#method-str-padboth) [Str::padLeft](#method-str-padleft) @@ -951,6 +953,48 @@ $string = Str::mask('taylor@example.com', '*', -15, 3); // tay***@example.com ``` + +#### `Str::match()` {.collection-method} + +The `Str::match` method will return the portion of a string that matches a given regular expression pattern: + +```php +use Illuminate\Support\Str; + +$result = Str::match('/bar/', 'foo bar'); + +// 'bar' + +$result = Str::match('/foo (.*)/', 'foo bar'); + +// 'bar' +``` + + +#### `Str::matchAll()` {.collection-method} + +The `Str::matchAll` method will return a collection containing the portions of a string that match a given regular expression pattern: + +```php +use Illuminate\Support\Str; + +$result = Str::matchAll('/bar/', 'bar foo bar'); + +// collect(['bar', 'bar']) +``` + +If you specify a matching group within the expression, Laravel will return a collection of the first matching group's matches: + +```php +use Illuminate\Support\Str; + +$result = Str::matchAll('/f(\w*)/', 'bar fun bar fly'); + +// collect(['un', 'ly']); +``` + +If no matches are found, an empty collection will be returned. + #### `Str::orderedUuid()` {.collection-method} From 918c798a47e81943e602eb1f33581acf66aaa3c3 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:01:17 +0300 Subject: [PATCH 2274/2609] Avoid class name conflict (#10470) --- container.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container.md b/container.md index c85b278b492..2e1068b6d27 100644 --- a/container.md +++ b/container.md @@ -298,8 +298,8 @@ In addition to the `Storage` attribute, Laravel offers `Auth`, `Cache`, `Config` namespace App\Http\Controllers; +use App\Contracts\UserRepository; use App\Models\Photo; -use App\Contracts\Repository; use App\Repositories\DatabaseRepository; use Illuminate\Container\Attributes\Auth; use Illuminate\Container\Attributes\Cache; @@ -323,7 +323,7 @@ class PhotoController extends Controller #[Config('app.timezone')] protected string $timezone, #[Context('uuid')] protected string $uuid, #[DB('mysql')] protected Connection $connection, - #[Give(DatabaseRepository::class)] protected Repository $users, + #[Give(DatabaseRepository::class)] protected UserRepository $users, #[Log('daily')] protected LoggerInterface $log, #[RouteParameter('photo')] protected Photo $photo, #[Tag('reports')] protected iterable $reports, From 27538cc5cdde4c0989ecae1cec6d3f024e5a2d01 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:01:35 +0300 Subject: [PATCH 2275/2609] Reorder Attribute casting (#10469) --- eloquent-mutators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 95b80d4fbe9..ef38a6cad6b 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -213,8 +213,8 @@ The `casts` method should return an array where the key is the name of the attri
    - `array` -- `AsUri::class` - `AsStringable::class` +- `AsUri::class` - `boolean` - `collection` - `date` From 86b56776f94e2b5e53abc80bc624f2c9df9177c6 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:52:47 +0300 Subject: [PATCH 2276/2609] Fix hyphenation of `date-based` for grammatical correctness (#10472) --- validation.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/validation.md b/validation.md index 1d4d4df2b28..7bab3e70fda 100644 --- a/validation.md +++ b/validation.md @@ -1166,7 +1166,7 @@ Instead of passing a date string to be evaluated by `strtotime`, you may specify 'finish_date' => 'required|date|after:start_date' ``` -For convenience, date based rules may be constructed using the fluent `date` rule builder: +For convenience, date-based rules may be constructed using the fluent `date` rule builder: ```php use Illuminate\Validation\Rule; @@ -1191,7 +1191,7 @@ The `afterToday` and `todayOrAfter` methods may be used to fluently express the The field under validation must be a value after or equal to the given date. For more information, see the [after](#rule-after) rule. -For convenience, date based rules may be constructed using the fluent `date` rule builder: +For convenience, date-based rules may be constructed using the fluent `date` rule builder: ```php use Illuminate\Validation\Rule; @@ -1300,7 +1300,7 @@ if ($validator->stopOnFirstFailure()->fails()) { 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`. -For convenience, date based rules may also be constructed using the fluent `date` rule builder: +For convenience, date-based rules may also be constructed using the fluent `date` rule builder: ```php use Illuminate\Validation\Rule; @@ -1325,7 +1325,7 @@ The `beforeToday` and `todayOrBefore` methods may be used to fluently express th 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`. -For convenience, date based rules may also be constructed using the fluent `date` rule builder: +For convenience, date-based rules may also be constructed using the fluent `date` rule builder: ```php use Illuminate\Validation\Rule; @@ -1395,7 +1395,7 @@ The field under validation must be equal to the given date. The dates will be pa The field under validation must match one of the given _formats_. You should use **either** `date` or `date_format` when validating a field, not both. This validation rule supports all formats supported by PHP's [DateTime](https://www.php.net/manual/en/class.datetime.php) class. -For convenience, date based rules may be constructed using the fluent `date` rule builder: +For convenience, date-based rules may be constructed using the fluent `date` rule builder: ```php use Illuminate\Validation\Rule; From c20a50a840430b7497676f50fee0314db3a54a99 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 5 Jun 2025 14:08:09 -0500 Subject: [PATCH 2277/2609] wip --- homestead.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homestead.md b/homestead.md index 14c1755e064..625ded8121f 100644 --- a/homestead.md +++ b/homestead.md @@ -36,6 +36,9 @@ ## Introduction +> [!WARNING] +> Laravel Homestead if a legacy package that is no longer actively maintained. [Laravel Sail](/docs/{{version}}/sail) may be used as a modern alternative. + Laravel strives to make the entire PHP development experience delightful, including your local development environment. [Laravel Homestead](https://github.com/laravel/homestead) is an official, pre-packaged Vagrant box that provides you a wonderful development environment without requiring you to install PHP, a web server, or any other server software on your local machine. [Vagrant](https://www.vagrantup.com) provides a simple, elegant way to manage and provision Virtual Machines. Vagrant boxes are completely disposable. If something goes wrong, you can destroy and re-create the box in minutes! From 09555e5874bc4bc2c1cf735bf6047ff0c979bddd Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 5 Jun 2025 23:57:44 +0300 Subject: [PATCH 2278/2609] Fix typo in Homestead deprecation notice (#10474) --- homestead.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homestead.md b/homestead.md index 625ded8121f..74172ccd9bc 100644 --- a/homestead.md +++ b/homestead.md @@ -37,7 +37,7 @@ ## Introduction > [!WARNING] -> Laravel Homestead if a legacy package that is no longer actively maintained. [Laravel Sail](/docs/{{version}}/sail) may be used as a modern alternative. +> Laravel Homestead is a legacy package that is no longer actively maintained. [Laravel Sail](/docs/{{version}}/sail) may be used as a modern alternative. Laravel strives to make the entire PHP development experience delightful, including your local development environment. [Laravel Homestead](https://github.com/laravel/homestead) is an official, pre-packaged Vagrant box that provides you a wonderful development environment without requiring you to install PHP, a web server, or any other server software on your local machine. From be2562cd115b34007dd2956699a52e345993eecf Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:11:37 +0300 Subject: [PATCH 2279/2609] [12.x] Refines wording related to Isolatable commands (#10475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refines wording related to Isolatable commands * Update artisan.md Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> * Update artisan.md --------- Co-authored-by: Taylor Otwell Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index 0e62cbed108..e0c386b3d04 100644 --- a/artisan.md +++ b/artisan.md @@ -243,7 +243,7 @@ class SendEmails extends Command implements Isolatable } ``` -When a command is marked as `Isolatable`, Laravel will automatically add an `--isolated` option to the command. When the command is invoked with that option, Laravel will ensure that no other instances of that command are already running. Laravel accomplishes this by attempting to acquire an atomic lock using your application's default cache driver. If other instances of the command are running, the command will not execute; however, the command will still exit with a successful exit status code: +When you mark a command as `Isolatable`, Laravel automatically makes the `--isolated` option available for the command without you needing to explicitly define it in the command's options. When the command is invoked with that option, Laravel will ensure that no other instances of that command are already running. Laravel accomplishes this by attempting to acquire an atomic lock using your application's default cache driver. If other instances of the command are running, the command will not execute; however, the command will still exit with a successful exit status code: ```shell php artisan mail:send 1 --isolated From daa0516434ac563fa872ed5012f3b978244b9e8c Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:14:10 +0300 Subject: [PATCH 2280/2609] [12.x] Add hash method to strings (#10471) * Add hash method to strings * wip * Update strings.md --------- Co-authored-by: Taylor Otwell --- strings.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/strings.md b/strings.md index fcacdc61309..38f1cd2b058 100644 --- a/strings.md +++ b/strings.md @@ -152,6 +152,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [excerpt](#method-fluent-str-excerpt) [explode](#method-fluent-str-explode) [finish](#method-fluent-str-finish) +[hash](#method-fluent-str-hash) [headline](#method-fluent-str-headline) [inlineMarkdown](#method-fluent-str-inline-markdown) [is](#method-fluent-str-is) @@ -2278,6 +2279,19 @@ $adjusted = Str::of('this/string/')->finish('/'); // this/string/ ``` + +#### `hash` {.collection-method} + +The `hash` method hashes the string using the given [algorithm](https://www.php.net/manual/en/function.hash-algos.php): + +```php +use Illuminate\Support\Str; + +$hashed = Str::of('secret')->hash(algorithm: 'sha256'); + +// '2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b' +``` + #### `headline` {.collection-method} From e38f8449286561ea7e582f8a82fd6879c13b8d91 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 6 Jun 2025 20:40:54 +0300 Subject: [PATCH 2281/2609] Improve clarity in Isolatable commands (#10479) --- artisan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artisan.md b/artisan.md index e0c386b3d04..5f114362856 100644 --- a/artisan.md +++ b/artisan.md @@ -243,7 +243,7 @@ class SendEmails extends Command implements Isolatable } ``` -When you mark a command as `Isolatable`, Laravel automatically makes the `--isolated` option available for the command without you needing to explicitly define it in the command's options. When the command is invoked with that option, Laravel will ensure that no other instances of that command are already running. Laravel accomplishes this by attempting to acquire an atomic lock using your application's default cache driver. If other instances of the command are running, the command will not execute; however, the command will still exit with a successful exit status code: +When you mark a command as `Isolatable`, Laravel automatically makes the `--isolated` option available for the command without needing to explicitly define it in the command's options. When the command is invoked with that option, Laravel will ensure that no other instances of that command are already running. Laravel accomplishes this by attempting to acquire an atomic lock using your application's default cache driver. If other instances of the command are running, the command will not execute; however, the command will still exit with a successful exit status code: ```shell php artisan mail:send 1 --isolated From 50a8281a3ccf0589d5d61db53efafe26daa877ba Mon Sep 17 00:00:00 2001 From: Katie Smith <108671517+ktsm-th@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:47:13 +0100 Subject: [PATCH 2282/2609] Add assertCount() and component() to Laravel Dusk docs (#10477) * Add assertCount to dusk.md * add component() to dusk.md * Update dusk.md * Update dusk.md --------- Co-authored-by: Taylor Otwell --- dusk.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dusk.md b/dusk.md index cc0cb689cdd..053e4a5f8c1 100644 --- a/dusk.md +++ b/dusk.md @@ -1414,6 +1414,7 @@ Dusk provides a variety of assertions that you may make against your application [assertDontSeeIn](#assert-dont-see-in) [assertSeeAnythingIn](#assert-see-anything-in) [assertSeeNothingIn](#assert-see-nothing-in) +[assertCount](#assert-count) [assertScript](#assert-script) [assertSourceHas](#assert-source-has) [assertSourceMissing](#assert-source-missing) @@ -1757,6 +1758,15 @@ Assert that no text is present within the selector: $browser->assertSeeNothingIn($selector); ``` + +#### assertCount + +Assert that elements matching the given selector appear the specified number of times: + +```php +$browser->assertCount($selector, $count); +``` + #### assertScript @@ -2505,6 +2515,16 @@ class ExampleTest extends DuskTestCase } ``` +The `component` method may be used to retrieve a browser instance scoped to the given component: + +```php +$datePicker = $browser->component(new DatePickerComponent); + +$datePicker->selectDate(2019, 1, 30); + +$datePicker->assertSee('January'); +``` + ## Continuous Integration From 6ca6c364ed7282c4ee4372f1491937fd5e61b076 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Fri, 6 Jun 2025 21:32:45 +0300 Subject: [PATCH 2283/2609] [12.x] Fix incorrect usage of `Str::replace` missing `$subject` argument (#10480) * Fix incorrect usage of Str::replace missing $subject argument * wip * Update strings.md --------- Co-authored-by: Taylor Otwell --- strings.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/strings.md b/strings.md index 38f1cd2b058..9d8c4654a85 100644 --- a/strings.md +++ b/strings.md @@ -1229,7 +1229,14 @@ $replaced = Str::replace('11.x', '12.x', $string); The `replace` method also accepts a `caseSensitive` argument. By default, the `replace` method is case sensitive: ```php -Str::replace('Framework', 'Laravel', caseSensitive: false); +$replaced = Str::replace( + 'php', + 'Laravel', + 'PHP Framework for Web Artisans', + caseSensitive: false +); + +// Laravel Framework for Web Artisans ``` From 25deab374da1113086200d4ad9c4fcc0078f48fd Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:14:15 +0300 Subject: [PATCH 2284/2609] Format middleware tables (#10482) --- middleware.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/middleware.md b/middleware.md index cddd2a0945a..2ff52e3e7b7 100644 --- a/middleware.md +++ b/middleware.md @@ -245,21 +245,21 @@ Laravel includes predefined `web` and `api` middleware groups that contain commo
    -| The `web` Middleware Group | -| --- | -| `Illuminate\Cookie\Middleware\EncryptCookies` | +| The `web` Middleware Group | +| --------------------------------------------------------- | +| `Illuminate\Cookie\Middleware\EncryptCookies` | | `Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse` | -| `Illuminate\Session\Middleware\StartSession` | -| `Illuminate\View\Middleware\ShareErrorsFromSession` | +| `Illuminate\Session\Middleware\StartSession` | +| `Illuminate\View\Middleware\ShareErrorsFromSession` | | `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` | -| `Illuminate\Routing\Middleware\SubstituteBindings` | +| `Illuminate\Routing\Middleware\SubstituteBindings` |
    -| The `api` Middleware Group | -| --- | +| The `api` Middleware Group | +| -------------------------------------------------- | | `Illuminate\Routing\Middleware\SubstituteBindings` |
    @@ -355,20 +355,20 @@ For convenience, some of Laravel's built-in middleware are aliased by default. F
    -| Alias | Middleware | -| --- | --- | -| `auth` | `Illuminate\Auth\Middleware\Authenticate` | -| `auth.basic` | `Illuminate\Auth\Middleware\AuthenticateWithBasicAuth` | -| `auth.session` | `Illuminate\Session\Middleware\AuthenticateSession` | -| `cache.headers` | `Illuminate\Http\Middleware\SetCacheHeaders` | -| `can` | `Illuminate\Auth\Middleware\Authorize` | -| `guest` | `Illuminate\Auth\Middleware\RedirectIfAuthenticated` | -| `password.confirm` | `Illuminate\Auth\Middleware\RequirePassword` | -| `precognitive` | `Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests` | -| `signed` | `Illuminate\Routing\Middleware\ValidateSignature` | -| `subscribed` | `\Spark\Http\Middleware\VerifyBillableIsSubscribed` | -| `throttle` | `Illuminate\Routing\Middleware\ThrottleRequests` or `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` | -| `verified` | `Illuminate\Auth\Middleware\EnsureEmailIsVerified` | +| Alias | Middleware | +| ------------------ | ------------------------------------------------------------------------------------------------------------- | +| `auth` | `Illuminate\Auth\Middleware\Authenticate` | +| `auth.basic` | `Illuminate\Auth\Middleware\AuthenticateWithBasicAuth` | +| `auth.session` | `Illuminate\Session\Middleware\AuthenticateSession` | +| `cache.headers` | `Illuminate\Http\Middleware\SetCacheHeaders` | +| `can` | `Illuminate\Auth\Middleware\Authorize` | +| `guest` | `Illuminate\Auth\Middleware\RedirectIfAuthenticated` | +| `password.confirm` | `Illuminate\Auth\Middleware\RequirePassword` | +| `precognitive` | `Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests` | +| `signed` | `Illuminate\Routing\Middleware\ValidateSignature` | +| `subscribed` | `\Spark\Http\Middleware\VerifyBillableIsSubscribed` | +| `throttle` | `Illuminate\Routing\Middleware\ThrottleRequests` or `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` | +| `verified` | `Illuminate\Auth\Middleware\EnsureEmailIsVerified` |
    From ef3360032ad154de60d83e0fdd1456c21f3e376a Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:17:48 +0300 Subject: [PATCH 2285/2609] [12.x] Add missing import for RetryException in retry helper example (#10483) * Add missing import for RetryException in retry helper example * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index a37f15f9852..d4135fd342c 100644 --- a/helpers.md +++ b/helpers.md @@ -2805,12 +2805,13 @@ return retry([100, 200], function () { To only retry under specific conditions, you may pass a closure as the fourth argument to the `retry` function: ```php +use App\Exceptions\TemporaryException; use Exception; return retry(5, function () { // ... }, 100, function (Exception $exception) { - return $exception instanceof RetryException; + return $exception instanceof TemporaryException; }); ``` From 7560640beadd00dfd0e68038e118af4dedeee043 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Mon, 9 Jun 2025 18:04:35 +0330 Subject: [PATCH 2286/2609] Update queries.md (#10463) --- queries.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/queries.md b/queries.md index 9a222cc6b7f..eaf8230ba6b 100644 --- a/queries.md +++ b/queries.md @@ -1163,6 +1163,14 @@ $query = DB::table('users')->orderBy('name'); $usersOrderedByEmail = $query->reorder('email', 'desc')->get(); ``` +For convenience, you may use the `reorderDesc` method to reorder the query results in descending order: + +```php +$query = DB::table('users')->orderBy('name'); + +$usersOrderedByEmail = $query->reorderDesc('email')->get(); +``` + ### Grouping From dcd137290a136645937ba21f9d2da0f68938dca5 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 9 Jun 2025 20:28:47 +0300 Subject: [PATCH 2287/2609] [12.x] Add decrypt and encrypt methods to strings (#10487) * Add decrypt and encrypt methods to strings * formatting --------- Co-authored-by: Taylor Otwell --- strings.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/strings.md b/strings.md index 9d8c4654a85..4ce05c113fc 100644 --- a/strings.md +++ b/strings.md @@ -145,8 +145,10 @@ Laravel includes a variety of functions for manipulating string values. Many of [chopEnd](#method-fluent-str-chop-end) [contains](#method-fluent-str-contains) [containsAll](#method-fluent-str-contains-all) +[decrypt](#method-fluent-str-decrypt) [deduplicate](#method-fluent-str-deduplicate) [dirname](#method-fluent-str-dirname) +[encrypt](#method-fluent-str-encrypt) [endsWith](#method-fluent-str-ends-with) [exactly](#method-fluent-str-exactly) [excerpt](#method-fluent-str-excerpt) @@ -2140,6 +2142,21 @@ $containsAll = Str::of('This is my name')->containsAll(['MY', 'NAME'], ignoreCas // true ``` + +#### `decrypt` {.collection-method} + +The `decrypt` method [decrypts](/docs/{{version}}/encryption) the encrypted string: + +```php +use Illuminate\Support\Str; + +$decrypted = $encrypted->decrypt(); + +// 'secret' +``` + +For the inverse of `decrypt`, see the [encrypt](#method-fluent-str-encrypt) method. + #### `deduplicate` {.collection-method} @@ -2186,6 +2203,19 @@ $string = Str::of('/foo/bar/baz')->dirname(2); // '/foo' ``` + +#### `encrypt` {.collection-method} + +The `encrypt` method [encrypts](/docs/{{version}}/encryption) the string: + +```php +use Illuminate\Support\Str; + +$encrypted = Str::of('secret')->encrypt(); +``` + +For the inverse of `encrypt`, see the [decrypt](#method-fluent-str-decrypt) method. + #### `endsWith` {.collection-method} From ee8f2805e6ef75d877eb8ffa20110e3b6e08b647 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 9 Jun 2025 20:42:02 +0300 Subject: [PATCH 2288/2609] [12.x] Format remaining tables (#10488) * Update authentication.md * Update pagination.md * Update passport.md * Update releases.md * Update scheduling.md --- authentication.md | 26 +++++++++++++------------- pagination.md | 44 ++++++++++++++++++++++---------------------- passport.md | 8 ++++---- releases.md | 12 ++++++------ scheduling.md | 12 ++++++------ 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/authentication.md b/authentication.md index a103d158d7d..3a5c7dd1ecf 100644 --- a/authentication.md +++ b/authentication.md @@ -825,19 +825,19 @@ Laravel dispatches a variety of [events](/docs/{{version}}/events) during the au
    -| Event Name | -| --- | -| `Illuminate\Auth\Events\Registered` | -| `Illuminate\Auth\Events\Attempting` | -| `Illuminate\Auth\Events\Authenticated` | -| `Illuminate\Auth\Events\Login` | -| `Illuminate\Auth\Events\Failed` | -| `Illuminate\Auth\Events\Validated` | -| `Illuminate\Auth\Events\Verified` | -| `Illuminate\Auth\Events\Logout` | +| Event Name | +| -------------------------------------------- | +| `Illuminate\Auth\Events\Registered` | +| `Illuminate\Auth\Events\Attempting` | +| `Illuminate\Auth\Events\Authenticated` | +| `Illuminate\Auth\Events\Login` | +| `Illuminate\Auth\Events\Failed` | +| `Illuminate\Auth\Events\Validated` | +| `Illuminate\Auth\Events\Verified` | +| `Illuminate\Auth\Events\Logout` | | `Illuminate\Auth\Events\CurrentDeviceLogout` | -| `Illuminate\Auth\Events\OtherDeviceLogout` | -| `Illuminate\Auth\Events\Lockout` | -| `Illuminate\Auth\Events\PasswordReset` | +| `Illuminate\Auth\Events\OtherDeviceLogout` | +| `Illuminate\Auth\Events\Lockout` | +| `Illuminate\Auth\Events\PasswordReset` |
    diff --git a/pagination.md b/pagination.md index 2f3af28955b..aedc189154c 100644 --- a/pagination.md +++ b/pagination.md @@ -363,28 +363,28 @@ Each paginator instance provides additional pagination information via the follo
    -| Method | Description | -| --- | --- | -| `$paginator->count()` | Get the number of items for the current page. | -| `$paginator->currentPage()` | Get the current page number. | -| `$paginator->firstItem()` | Get the result number of the first item in the results. | -| `$paginator->getOptions()` | Get the paginator options. | -| `$paginator->getUrlRange($start, $end)` | Create a range of pagination URLs. | -| `$paginator->hasPages()` | Determine if there are enough items to split into multiple pages. | -| `$paginator->hasMorePages()` | Determine if there are more items in the data store. | -| `$paginator->items()` | Get the items for the current page. | -| `$paginator->lastItem()` | Get the result number of the last item in the results. | -| `$paginator->lastPage()` | Get the page number of the last available page. (Not available when using `simplePaginate`). | -| `$paginator->nextPageUrl()` | Get the URL for the next page. | -| `$paginator->onFirstPage()` | Determine if the paginator is on the first page. | -| `$paginator->onLastPage()` | Determine if the paginator is on the last page. | -| `$paginator->perPage()` | The number of items to be shown per page. | -| `$paginator->previousPageUrl()` | Get the URL for the previous page. | -| `$paginator->total()` | Determine the total number of matching items in the data store. (Not available when using `simplePaginate`). | -| `$paginator->url(https://github.com/$page)` | Get the URL for a given page number. | -| `$paginator->getPageName()` | Get the query string variable used to store the page. | -| `$paginator->setPageName($name)` | Set the query string variable used to store the page. | -| `$paginator->through($callback)` | Transform each item using a callback. | +| Method | Description | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `$paginator->count()` | Get the number of items for the current page. | +| `$paginator->currentPage()` | Get the current page number. | +| `$paginator->firstItem()` | Get the result number of the first item in the results. | +| `$paginator->getOptions()` | Get the paginator options. | +| `$paginator->getUrlRange($start, $end)` | Create a range of pagination URLs. | +| `$paginator->hasPages()` | Determine if there are enough items to split into multiple pages. | +| `$paginator->hasMorePages()` | Determine if there are more items in the data store. | +| `$paginator->items()` | Get the items for the current page. | +| `$paginator->lastItem()` | Get the result number of the last item in the results. | +| `$paginator->lastPage()` | Get the page number of the last available page. (Not available when using `simplePaginate`). | +| `$paginator->nextPageUrl()` | Get the URL for the next page. | +| `$paginator->onFirstPage()` | Determine if the paginator is on the first page. | +| `$paginator->onLastPage()` | Determine if the paginator is on the last page. | +| `$paginator->perPage()` | The number of items to be shown per page. | +| `$paginator->previousPageUrl()` | Get the URL for the previous page. | +| `$paginator->total()` | Determine the total number of matching items in the data store. (Not available when using `simplePaginate`). | +| `$paginator->url(https://github.com/$page)` | Get the URL for a given page number. | +| `$paginator->getPageName()` | Get the query string variable used to store the page. | +| `$paginator->setPageName($name)` | Set the query string variable used to store the page. | +| `$paginator->through($callback)` | Transform each item using a callback. |
    diff --git a/passport.md b/passport.md index fc0d76c636c..068a64b3283 100644 --- a/passport.md +++ b/passport.md @@ -1312,10 +1312,10 @@ Passport raises events when issuing access tokens and refresh tokens. You may [l
    -| Event Name | -| --- | -| `Laravel\Passport\Events\AccessTokenCreated` | -| `Laravel\Passport\Events\AccessTokenRevoked` | +| Event Name | +| --------------------------------------------- | +| `Laravel\Passport\Events\AccessTokenCreated` | +| `Laravel\Passport\Events\AccessTokenRevoked` | | `Laravel\Passport\Events\RefreshTokenCreated` |
    diff --git a/releases.md b/releases.md index 5287340d392..fbbaaf9355b 100644 --- a/releases.md +++ b/releases.md @@ -23,12 +23,12 @@ For all Laravel releases, bug fixes are provided for 18 months and security fixe
    -| Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | -| --- | --- | --- | --- | --- | -| 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | -| 11 | 8.2 - 8.4 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | -| 12 | 8.2 - 8.4 | February 24th, 2025 | August 13th, 2026 | February 24th, 2027 | -| 13 | 8.3 - 8.4 | Q1 2026 | Q3 2027 | Q1 2028 | +| Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | +| ------- | --------- | ------------------- | ------------------- | -------------------- | +| 10 | 8.1 - 8.3 | February 14th, 2023 | August 6th, 2024 | February 4th, 2025 | +| 11 | 8.2 - 8.4 | March 12th, 2024 | September 3rd, 2025 | March 12th, 2026 | +| 12 | 8.2 - 8.4 | February 24th, 2025 | August 13th, 2026 | February 24th, 2027 | +| 13 | 8.3 - 8.4 | Q1 2026 | Q3 2027 | Q1 2028 |
    diff --git a/scheduling.md b/scheduling.md index c024d72dd76..31027f4d161 100644 --- a/scheduling.md +++ b/scheduling.md @@ -623,12 +623,12 @@ Laravel dispatches a variety of [events](/docs/{{version}}/events) during the sc
    -| Event Name | -| --- | -| `Illuminate\Console\Events\ScheduledTaskStarting` | -| `Illuminate\Console\Events\ScheduledTaskFinished` | +| Event Name | +| ----------------------------------------------------------- | +| `Illuminate\Console\Events\ScheduledTaskStarting` | +| `Illuminate\Console\Events\ScheduledTaskFinished` | | `Illuminate\Console\Events\ScheduledBackgroundTaskFinished` | -| `Illuminate\Console\Events\ScheduledTaskSkipped` | -| `Illuminate\Console\Events\ScheduledTaskFailed` | +| `Illuminate\Console\Events\ScheduledTaskSkipped` | +| `Illuminate\Console\Events\ScheduledTaskFailed` |
    From 11cbd00f3925862924d0bba4b371c5a772d9cfa5 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 10 Jun 2025 17:03:24 +0300 Subject: [PATCH 2289/2609] Remove outdated upgrade note from Laravel 12 docs (#10494) --- controllers.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/controllers.md b/controllers.md index 262c2b9e07b..fe6d706103b 100644 --- a/controllers.md +++ b/controllers.md @@ -165,9 +165,6 @@ public static function middleware(): array } ``` -> [!WARNING] -> Controllers implementing `Illuminate\Routing\Controllers\HasMiddleware` should not extend `Illuminate\Routing\Controller`. - ## Resource Controllers From 1a9e4f44b476b65f888676939e477046efc68d1b Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 10 Jun 2025 17:03:40 +0300 Subject: [PATCH 2290/2609] Fix punctuation (#10492) --- strings.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/strings.md b/strings.md index 4ce05c113fc..67827050dc2 100644 --- a/strings.md +++ b/strings.md @@ -470,7 +470,7 @@ $url = Str::chopEnd('laravel.com/index.php', ['/index.html', '/index.php']); #### `Str::contains()` {.collection-method} -The `Str::contains` method determines if the given string contains the given value. By default this method is case sensitive: +The `Str::contains` method determines if the given string contains the given value. By default, this method is case sensitive: ```php use Illuminate\Support\Str; @@ -526,7 +526,7 @@ $containsAll = Str::containsAll('This is my name', ['MY', 'NAME'], ignoreCase: t #### `Str::doesntContain()` {.collection-method} -The `Str::doesntContain` method determines if the given string doesn't contain the given value. By default this method is case sensitive: +The `Str::doesntContain` method determines if the given string doesn't contain the given value. By default, this method is case sensitive: ```php use Illuminate\Support\Str; @@ -2089,7 +2089,7 @@ $url = Str::of('http://laravel.com/')->chopEnd(['.com', '.io']); #### `contains` {.collection-method} -The `contains` method determines if the given string contains the given value. By default this method is case sensitive: +The `contains` method determines if the given string contains the given value. By default, this method is case sensitive: ```php use Illuminate\Support\Str; From e5abed8708ebe7e51820e3062629dbec5407607c Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 10 Jun 2025 17:03:57 +0300 Subject: [PATCH 2291/2609] [12.x] Fix hyphenation of class-based for grammatical correctness and consistency (#10491) * Update blade.md * Update pennant.md * Update validation.md * Update views.md --- blade.md | 4 ++-- pennant.md | 12 ++++++------ validation.md | 2 +- views.md | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/blade.md b/blade.md index ad0d458a75d..0a901f57547 100644 --- a/blade.md +++ b/blade.md @@ -691,9 +691,9 @@ Blade also allows you to define comments in your views. However, unlike HTML com ## 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. +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: +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: ```shell php artisan make:component Alert diff --git a/pennant.md b/pennant.md index 4f7734d4221..d8d6b403302 100644 --- a/pennant.md +++ b/pennant.md @@ -112,7 +112,7 @@ 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 php artisan pennant:feature NewApi @@ -144,7 +144,7 @@ class NewApi } ``` -If you would like to manually resolve an instance of a class based feature, you may invoke the `instance` method on the `Feature` facade: +If you would like to manually resolve an instance of a class-based feature, you may invoke the `instance` method on the `Feature` facade: ```php use Illuminate\Support\Facades\Feature; @@ -240,7 +240,7 @@ Feature::someAreInactive(['new-api', 'site-redesign']); #### Checking Class Based Features -For class based features, you should provide the class name when checking the feature: +For class-based features, you should provide the class name when checking the feature: ```php #### enum -The `Enum` rule is a class based rule that validates whether the field under validation contains a valid enum value. The `Enum` rule accepts the name of the enum as its only constructor argument. When validating primitive values, a backed Enum should be provided to the `Enum` rule: +The `Enum` rule is a class-based rule that validates whether the field under validation contains a valid enum value. The `Enum` rule accepts the name of the enum as its only constructor argument. When validating primitive values, a backed Enum should be provided to the `Enum` rule: ```php use App\Enums\ServerStatus; diff --git a/views.md b/views.md index 4904644896f..5a10f9b25d2 100644 --- a/views.md +++ b/views.md @@ -170,7 +170,7 @@ View composers are callbacks or class methods that are called when a view is ren Typically, view composers will be registered within one of your application's [service providers](/docs/{{version}}/providers). In this example, we'll assume that the `App\Providers\AppServiceProvider` will house this logic. -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/View/Composers` directory to house all of your application's view composers: +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/View/Composers` directory to house all of your application's view composers: ```php Date: Tue, 10 Jun 2025 17:27:36 +0300 Subject: [PATCH 2292/2609] Add hidden to the Context attribute in Container (#10495) --- container.md | 1 + 1 file changed, 1 insertion(+) diff --git a/container.md b/container.md index 2e1068b6d27..acd5caddd9c 100644 --- a/container.md +++ b/container.md @@ -322,6 +322,7 @@ class PhotoController extends Controller #[Cache('redis')] protected Repository $cache, #[Config('app.timezone')] protected string $timezone, #[Context('uuid')] protected string $uuid, + #[Context('ulid', hidden: true)] protected string $ulid, #[DB('mysql')] protected Connection $connection, #[Give(DatabaseRepository::class)] protected UserRepository $users, #[Log('daily')] protected LoggerInterface $log, From 99226be9199448b7bd09e1321b796f3376646d7e Mon Sep 17 00:00:00 2001 From: Amir Hossein Shokri Date: Tue, 10 Jun 2025 18:17:21 +0330 Subject: [PATCH 2293/2609] [12.x] Add make:job-middleware command to queue docs (#10490) * add make:job-middleware command to docs * Update queues.md --------- Co-authored-by: Taylor Otwell --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 3b899ebd0d0..46155dcab91 100644 --- a/queues.md +++ b/queues.md @@ -458,7 +458,7 @@ class RateLimited 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 manually add it to your job class: +You can generate a new job middleware class using the `make:job-middleware` Artisan command. 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: ```php use App\Jobs\Middleware\RateLimited; From 4af37a72d311c83ad9dfb0f9ef3b098993c4b62f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Jun 2025 10:27:52 -0500 Subject: [PATCH 2294/2609] should rescue --- broadcasting.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/broadcasting.md b/broadcasting.md index 7b6fe751ab6..98075d4b7d1 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -25,6 +25,7 @@ - [Only to Others](#only-to-others) - [Customizing the Connection](#customizing-the-connection) - [Anonymous Events](#anonymous-events) + - [Rescuing Broadcasts](#rescuing-broadcasts) - [Receiving Broadcasts](#receiving-broadcasts) - [Listening for Events](#listening-for-events) - [Leaving a Channel](#leaving-a-channel) @@ -1022,6 +1023,27 @@ Broadcast::on('orders.'.$order->id) ->send(); ``` + +### Rescuing Broadcasts + +When your application's queue server is unavailable or Laravel encounters an error while broadcasting an event, an exception is thrown that typically causes the end user to see an application error. Since event broadcasting is often supplementary to your application's core functionality, you can prevent these exceptions from disrupting the user experience by implementing the `ShouldRescue` interface on your events. + +Events that implement the `ShouldRescue` interface automatically utilize Laravel's [rescue helper function](/docs/{{version}}/helpers#method-rescue) during broadcast attempts. This helper catches any exceptions, reports them to your application's exception handler for logging, and allows the application to continue executing normally without interrupting the user's workflow: + +```php + ## Receiving Broadcasts From 58eb4b956d19a110d74c005fc9dbe49a1c11c763 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Jun 2025 10:31:48 -0500 Subject: [PATCH 2295/2609] wip --- http-client.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/http-client.md b/http-client.md index 3d9a4878cba..ca42e16d186 100644 --- a/http-client.md +++ b/http-client.md @@ -409,6 +409,12 @@ use Illuminate\Foundation\Configuration\Exceptions; }) ``` +Alternatively, you may customize the exception truncation behavior per request using the `truncateExceptionsAt` method: + +```php +return Http::truncateExceptionsAt(240)->post(/* ... */); +``` + ### Guzzle Middleware From 85d59fb3cd6171c14ab6ba7cbfa536f77fb6c9f2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Jun 2025 10:34:17 -0500 Subject: [PATCH 2296/2609] wip --- authorization.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/authorization.md b/authorization.md index fe87667dea6..b418b61703f 100644 --- a/authorization.md +++ b/authorization.md @@ -343,6 +343,24 @@ public function boot(): void } ``` +Alternatively, you may place the `UsePolicy` attribute on a model class to inform Laravel of the model's corresponding policy: + +```php + ## Writing Policies From 5070d927f4ca580c228cfa3936fd16c4ac58481d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Jun 2025 10:42:12 -0500 Subject: [PATCH 2297/2609] document compare --- eloquent-mutators.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index ef38a6cad6b..1db41cec4a8 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -15,6 +15,7 @@ - [Array / JSON Serialization](#array-json-serialization) - [Inbound Casting](#inbound-casting) - [Cast Parameters](#cast-parameters) + - [Comparing Cast Values](#comparing-cast-values) - [Castables](#castables) @@ -926,6 +927,33 @@ protected function casts(): array } ``` + +### Comparing Cast Values + +If you would like to define how two given cast values should be compared to determine if they have been changed, your custom cast class may implement the `Illuminate\Contracts\Database\Eloquent\ComparesCastableAttributes` interface. This allows you to have fine-grained control over which values Eloquent considers changed and thus saves to the database when a model is updated. + +This interface states that your class should contain a `compare` method which should return `true` if the given values are different: + +```php +/** + * Determine if the given values are equal. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param mixed $firstValue + * @param mixed $secondValue + * @return bool + */ +public function compare( + Model $model, + string $key, + mixed $firstValue, + mixed $secondValue +): bool { + return $firstValue !== $secondValue; +} +``` + ### Castables From a40ff7074b8e624bb1ef2928be0685ae7cbc81bf Mon Sep 17 00:00:00 2001 From: Sander Visser Date: Wed, 11 Jun 2025 14:59:59 +0200 Subject: [PATCH 2298/2609] Correction in eloquent-mutators.md Comparing Cast Values (#10497) --- eloquent-mutators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 1db41cec4a8..f047c4aa102 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -932,7 +932,7 @@ protected function casts(): array If you would like to define how two given cast values should be compared to determine if they have been changed, your custom cast class may implement the `Illuminate\Contracts\Database\Eloquent\ComparesCastableAttributes` interface. This allows you to have fine-grained control over which values Eloquent considers changed and thus saves to the database when a model is updated. -This interface states that your class should contain a `compare` method which should return `true` if the given values are different: +This interface states that your class should contain a `compare` method which should return `true` if the given values are considered equal: ```php /** @@ -950,7 +950,7 @@ public function compare( mixed $firstValue, mixed $secondValue ): bool { - return $firstValue !== $secondValue; + return $firstValue === $secondValue; } ``` From a51a037d97cb0b9e927993b716d0b161e7a0934a Mon Sep 17 00:00:00 2001 From: Amir Hossein Shokri Date: Wed, 11 Jun 2025 16:30:28 +0330 Subject: [PATCH 2299/2609] add proper links (#10496) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 46155dcab91..94a48954b05 100644 --- a/queues.md +++ b/queues.md @@ -475,7 +475,7 @@ public function middleware(): array ``` > [!NOTE] -> Job middleware can also be assigned to queueable event listeners, mailables, and notifications. +> Job middleware can also be assigned to [queueable event listeners](/docs/{{version}}/events#queued-event-listeners), [mailables](/docs/{{version}}/mail#queueing-mail), and [notifications](/docs/{{version}}/notifications#queueing-notifications). ### Rate Limiting From 4b842431d1c68b4bd92200432d7c42c6bab44175 Mon Sep 17 00:00:00 2001 From: Andrew Foster Date: Mon, 16 Jun 2025 23:23:14 +1000 Subject: [PATCH 2300/2609] Fix typo (#10502) --- starter-kits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starter-kits.md b/starter-kits.md index 6b054c79f4f..699d88a17f1 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -241,7 +241,7 @@ resources/views #### Traditional Livewire Components -The frontend code is located in the `resouces/views` directory, while the `app/Livewire` directory contains the corresponding backend logic for the Livewire components. +The frontend code is located in the `resources/views` directory, while the `app/Livewire` directory contains the corresponding backend logic for the Livewire components. #### Available Layouts From 66eb7257793e0aeb4bb9e5c5cb7fda03de4ea894 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:25:26 +0300 Subject: [PATCH 2301/2609] [12.x] Clarify and align storage drivers list (#10503) * Clarify and align storage drivers list * Fix hyphenation of key-based * Update filesystem.md --------- Co-authored-by: Taylor Otwell --- filesystem.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filesystem.md b/filesystem.md index d93c7f56fbb..9b069449c1e 100644 --- a/filesystem.md +++ b/filesystem.md @@ -35,7 +35,7 @@ Laravel provides a powerful filesystem abstraction thanks to the wonderful [Flys Laravel's filesystem configuration file is located at `config/filesystems.php`. Within this file, you may configure all of your filesystem "disks". Each disk represents a particular storage driver and storage location. Example configurations for each supported driver are included in the configuration file so you can modify the configuration to reflect your storage preferences and credentials. -The `local` driver interacts with files stored locally on the server running the Laravel application while the `s3` driver is used to write to Amazon's S3 cloud storage service. +The `local` driver interacts with files stored locally on the server running the Laravel application, while the `sftp` storage driver is used for SSH key-based FTP. The `s3` driver is used to write to Amazon's S3 cloud storage service. > [!NOTE] > You may configure as many disks as you like and may even have multiple disks that use the same driver. @@ -156,7 +156,7 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu 'username' => env('SFTP_USERNAME'), 'password' => env('SFTP_PASSWORD'), - // Settings for SSH key based authentication with encryption password... + // Settings for SSH key-based authentication with encryption password... 'privateKey' => env('SFTP_PRIVATE_KEY'), 'passphrase' => env('SFTP_PASSPHRASE'), From c60625a5aff0f468053aa804d48665d5379f03a8 Mon Sep 17 00:00:00 2001 From: Okan Giritli <37473473+firebed@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:47:24 +0300 Subject: [PATCH 2302/2609] [12.x] Add `php artisan horizon:clear-metrics` command (#10504) * Update horizon.md Include `php artisan horizon:clear-metrics` command * Update horizon.md --------- Co-authored-by: Taylor Otwell --- horizon.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/horizon.md b/horizon.md index e64536b70b0..b104d2b807f 100644 --- a/horizon.md +++ b/horizon.md @@ -449,6 +449,12 @@ use Illuminate\Support\Facades\Schedule; Schedule::command('horizon:snapshot')->everyFiveMinutes(); ``` +If you would like to delete all metric data, you can invoke the `horizon:clear-metrics` Artisan command: + +```shell +php artisan horizon:clear-metrics +``` + ## Deleting Failed Jobs From 3e2909319a0d6f2715207fb129f8d979b32f6528 Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:28:20 +0330 Subject: [PATCH 2303/2609] Update eloquent-mutators.md (#10506) --- eloquent-mutators.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eloquent-mutators.md b/eloquent-mutators.md index f047c4aa102..f8ef3c27789 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -214,6 +214,7 @@ The `casts` method should return an array where the key is the name of the attri
    - `array` +- `AsFluent::class` - `AsStringable::class` - `AsUri::class` - `boolean` From 3432dc8192532eb74878bda4a9f49bd28d453d3a Mon Sep 17 00:00:00 2001 From: Cedrik <48626242+CedzyC@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:19:06 +0200 Subject: [PATCH 2304/2609] Added Passport import to "Overriding Default Models" (#10505) * Added Passport import to "Overriding Default Models" * Update passport.md --------- Co-authored-by: Taylor Otwell --- passport.md | 1 + 1 file changed, 1 insertion(+) diff --git a/passport.md b/passport.md index 068a64b3283..f4173299aec 100644 --- a/passport.md +++ b/passport.md @@ -202,6 +202,7 @@ use App\Models\Passport\Client; use App\Models\Passport\DeviceCode; use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; +use Laravel\Passport\Passport; /** * Bootstrap any application services. From 118eb1970420602073990b081b18bef59582c8b3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 17 Jun 2025 12:18:10 -0500 Subject: [PATCH 2305/2609] wip --- http-tests.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/http-tests.md b/http-tests.md index c12ad817041..1c786d34c4d 100644 --- a/http-tests.md +++ b/http-tests.md @@ -1005,6 +1005,8 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertPlainCookie](#assert-plain-cookie) [assertRedirect](#assert-redirect) [assertRedirectBack](#assert-redirect-back) +[assertRedirectBackWithErrors](#assert-redirect-back-with-errors) +[assertRedirectBackWithoutErrors](#assert-redirect-back-without-errors) [assertRedirectContains](#assert-redirect-contains) [assertRedirectToRoute](#assert-redirect-to-route) [assertRedirectToSignedRoute](#assert-redirect-to-signed-route) @@ -1561,6 +1563,26 @@ Assert whether the response is redirecting back to the previous page: $response->assertRedirectBack(); ``` + +#### assertRedirectBackWithErrors + +Assert whether the response is redirecting back to the previous page and the [session has the given errors](#assert-session-has-errors): + +```php +$response->assertRedirectBackWithErrors( + array $keys = [], $format = null, $errorBag = 'default' +); +``` + + +#### assertRedirectBackWithoutErrors + +Assert whether the response is redirecting back to the previous page and the session does not contain any error messages: + +```php +$response->assertRedirectBackWithoutErrors(); +``` + #### assertRedirectContains From 013514ac88989dc577ee6bef4776849db35be151 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 17 Jun 2025 21:47:20 +0300 Subject: [PATCH 2306/2609] Clarify the behavior of collapseWithKeys method (#10501) --- collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collections.md b/collections.md index dafbc1de412..be8b7f7259f 100644 --- a/collections.md +++ b/collections.md @@ -436,7 +436,7 @@ $collapsed->all(); #### `collapseWithKeys()` {.collection-method} -The `collapseWithKeys` method flattens a collection of arrays or collections into a single collection, keeping the original keys intact: +The `collapseWithKeys` method flattens a collection of arrays or collections into a single collection, keeping the original keys intact. If the collection is already flat, it will return an empty collection: ```php $collection = collect([ From 7656ab839d7af68c06349baf5839a580f176f3c5 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:55:11 -0400 Subject: [PATCH 2307/2609] [12.x] `FailOnException` middleware (#10508) * FailOnException middleware * Update queues.md * Update queues.md * formatting * wip --------- Co-authored-by: Taylor Otwell --- queues.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/queues.md b/queues.md index 94a48954b05..affb97025e0 100644 --- a/queues.md +++ b/queues.md @@ -1428,6 +1428,64 @@ $this->fail('Something went wrong.'); > [!NOTE] > For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). + +#### Failing Jobs on Specific Exceptions + +The `FailOnException` [job middleware](#job-middleware) allows you to short-circuit retries when specific exceptions are thrown. This allows retrying on transient exceptions such as external API errors, but failing the job permanently on persistent exceptions, such as a user's permissions being revoked: + +```php +authorize('sync-chat-history'); + + $response = Http::throw()->get( + "https://chat.laravel.test/?user={$user-%3Euuid}+"); + + + // ... + } + + /** + * Get the middleware the job should pass through. + */ + public function middleware(): array + { + return [ + new FailOnException([AuthorizationException::class]) + ]; + } +} +``` + ## Job Batching From 2db745c10a3a367c6bcafb5bfe019fb5d3009ca9 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 17 Jun 2025 22:17:29 +0300 Subject: [PATCH 2308/2609] Missing semi-colon (#10509) --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index f4173299aec..d136ee02071 100644 --- a/passport.md +++ b/passport.md @@ -213,7 +213,7 @@ public function boot(): void Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); Passport::useClientModel(Client::class); - Passport::useDeviceCodeModel(DeviceCode::class) + Passport::useDeviceCodeModel(DeviceCode::class); } ``` From 35f64d38e2c076c792acc0a0edc6a50182d0a9e9 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:52:15 +0300 Subject: [PATCH 2309/2609] Update queues.md (#10512) --- queues.md | 1 - 1 file changed, 1 deletion(-) diff --git a/queues.md b/queues.md index affb97025e0..29770aac665 100644 --- a/queues.md +++ b/queues.md @@ -1470,7 +1470,6 @@ class SyncChatHistory implements ShouldQueue "https://chat.laravel.test/?user={$user-%3Euuid}"); - // ... } From f4b76d3fd9733cdc5331aed5d55ea32730e835db Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Wed, 18 Jun 2025 08:52:50 -0400 Subject: [PATCH 2310/2609] Update queues.md (#10510) --- queues.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queues.md b/queues.md index 29770aac665..223c6fd8266 100644 --- a/queues.md +++ b/queues.md @@ -1467,8 +1467,8 @@ class SyncChatHistory implements ShouldQueue $user->authorize('sync-chat-history'); $response = Http::throw()->get( - "https://chat.laravel.test/?user={$user-%3Euuid}-"); + "https://chat.laravel.test/?user={$user-%3Euuid}" + ); // ... } From b398efbb837f1c41b13016e1b010f38999ff08b7 Mon Sep 17 00:00:00 2001 From: Mohsin Ali Date: Wed, 18 Jun 2025 17:53:44 +0500 Subject: [PATCH 2311/2609] Clarify env() behavior after config caching (#10513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clarify env() behavior after configuration caching The current documentation states that “all calls to the env function will return null” once the configuration has been cached. However, this is only true for environment variables defined exclusively in the .env file. If the environment variable exists at the system level (e.g., via putenv(), server configuration, env() will still return a value after caching. This clarification helps developers understand that env() is not completely disabled after config:cache, but rather depends on the source of the environment variable. --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index d4135fd342c..186a4e81efe 100644 --- a/helpers.md +++ b/helpers.md @@ -2438,7 +2438,7 @@ $env = env('APP_ENV', 'production'); ``` > [!WARNING] -> If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. +> If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return external environment variables such as server-level or system-level environment variables or `null`. #### `event()` {.collection-method} From a9716c300bff2a7c773ed46ca14afab5860fdfbe Mon Sep 17 00:00:00 2001 From: Johan Montenij Date: Wed, 18 Jun 2025 20:44:28 +0200 Subject: [PATCH 2312/2609] Add documentation for fromBase64() (#10514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add documentation for Str::of(...)->fromBase64() * Add documentation for Str::of(...)->fromBase64() * Apply suggestions Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> * formatting --------- Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> Co-authored-by: Taylor Otwell --- strings.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/strings.md b/strings.md index 67827050dc2..10fc6be5927 100644 --- a/strings.md +++ b/strings.md @@ -52,6 +52,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [Str::endsWith](#method-ends-with) [Str::excerpt](#method-excerpt) [Str::finish](#method-str-finish) +[Str::fromBase64](#method-str-from-base64) [Str::headline](#method-str-headline) [Str::inlineMarkdown](#method-str-inline-markdown) [Str::is](#method-str-is) @@ -154,6 +155,7 @@ Laravel includes a variety of functions for manipulating string values. Many of [excerpt](#method-fluent-str-excerpt) [explode](#method-fluent-str-explode) [finish](#method-fluent-str-finish) +[fromBase64](#method-fluent-str-from-base64) [hash](#method-fluent-str-hash) [headline](#method-fluent-str-headline) [inlineMarkdown](#method-fluent-str-inline-markdown) @@ -653,6 +655,19 @@ $adjusted = Str::finish('this/string/', '/'); // this/string/ ``` + +#### `Str::fromBase64()` {.collection-method} + +The `Str::fromBase64` method decodes the given Base64 string: + +```php +use Illuminate\Support\Str; + +$decoded = Str::fromBase64('TGFyYXZlbA=='); + +// Laravel +``` + #### `Str::headline()` {.collection-method} @@ -2316,6 +2331,19 @@ $adjusted = Str::of('this/string/')->finish('/'); // this/string/ ``` + +#### `fromBase64` {.collection-method} + +The `fromBase64` method decodes the given Base64 string: + +```php +use Illuminate\Support\Str; + +$decoded = Str::of('TGFyYXZlbA==')->fromBase64(); + +// Laravel +``` + #### `hash` {.collection-method} From f0dd01fbfc7a510da582efa03c6e1b31ef188aed Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 19 Jun 2025 20:19:05 +0300 Subject: [PATCH 2313/2609] Improve consistency and clarity in Filesystem Directories (#10515) --- filesystem.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filesystem.md b/filesystem.md index 9b069449c1e..f6534104a72 100644 --- a/filesystem.md +++ b/filesystem.md @@ -730,7 +730,7 @@ Storage::disk('s3')->delete('path/file.jpg'); #### Get All Files Within a Directory -The `files` method returns an array of all of the files in a given directory. If you would like to retrieve a list of all files within a given directory including all subdirectories, you may use the `allFiles` method: +The `files` method returns an array of all files within a given directory. If you would like to retrieve a list of all files within a given directory including subdirectories, you may use the `allFiles` method: ```php use Illuminate\Support\Facades\Storage; @@ -743,7 +743,7 @@ $files = Storage::allFiles($directory); #### Get All Directories Within a Directory -The `directories` method returns an array of all the directories within a given directory. Additionally, you may use the `allDirectories` method to get a list of all directories within a given directory and all of its subdirectories: +The `directories` method returns an array of all directories within a given directory. If you would like to retrieve a list of all directories within a given directory including subdirectories, you may use the `allDirectories` method: ```php $directories = Storage::directories($directory); From b08a9df6d86e1d660dc75b4d665fa60c24b7b0f4 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 23 Jun 2025 02:26:51 +0300 Subject: [PATCH 2314/2609] Add deprecation notice for Laravel Mix (#10519) --- mix.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mix.md b/mix.md index 59ec23ea1ff..1d8259c51d4 100644 --- a/mix.md +++ b/mix.md @@ -5,6 +5,9 @@ ## Introduction +> [!WARNING] +> Laravel Mix is a legacy package that is no longer actively maintained. [Vite](/docs/{{version}}/vite) may be used as a modern alternative. + [Laravel Mix](https://github.com/laravel-mix/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: From 656126b66762040a65ab5857fb3fb764a06cd5e3 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Mon, 23 Jun 2025 02:27:06 +0300 Subject: [PATCH 2315/2609] Remove deprecated mix() helper (#10520) --- helpers.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/helpers.md b/helpers.md index 186a4e81efe..f4c7b76660a 100644 --- a/helpers.md +++ b/helpers.md @@ -134,7 +134,6 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [config_path](#method-config-path) [database_path](#method-database-path) [lang_path](#method-lang-path) -[mix](#method-mix) [public_path](#method-public-path) [resource_path](#method-resource-path) [storage_path](#method-storage-path) @@ -1952,15 +1951,6 @@ $path = lang_path('en/messages.php'); > [!NOTE] > By default, the Laravel application skeleton does not include the `lang` directory. If you would like to customize Laravel's language files, you may publish them via the `lang:publish` Artisan command. - -#### `mix()` {.collection-method} - -The `mix` function returns the path to a [versioned Mix file](/docs/{{version}}/mix): - -```php -$path = mix('css/app.css'); -``` - #### `public_path()` {.collection-method} From 84b720160d5ee80455b6b562df5f734b03a68ccb Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sun, 22 Jun 2025 18:35:20 -0500 Subject: [PATCH 2316/2609] only use and document the `limit` and `offset` methods (#10516) `skip()` and `take()` are alias methods to `offset()` and `limit()`. IMO we should only use and document the main methods, not the aliases. This will encourage more performant coding, just like we now do in `laravel/framework`. IF it's decided we still want to mention the aliases, I'd recommended listing them second as the alternatives. I can make that change if desired. --- eloquent.md | 2 +- queries.md | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/eloquent.md b/eloquent.md index 8c92e4d50cc..acea301ef73 100644 --- a/eloquent.md +++ b/eloquent.md @@ -431,7 +431,7 @@ The Eloquent `all` method will return all of the results in the model's table. H ```php $flights = Flight::where('active', 1) ->orderBy('name') - ->take(10) + ->limit(10) ->get(); ``` diff --git a/queries.md b/queries.md index eaf8230ba6b..02053a8f1f8 100644 --- a/queries.md +++ b/queries.md @@ -1210,16 +1210,7 @@ To build more advanced `having` statements, see the [havingRaw](#raw-methods) me ### Limit and Offset - -#### 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: - -```php -$users = DB::table('users')->skip(10)->take(5)->get(); -``` - -Alternatively, you may use the `limit` and `offset` methods. These methods are functionally equivalent to the `take` and `skip` methods, respectively: +You may use the `limit` and `offset` methods to limit the number of results returned from the query or to skip a given number of results in the query: ```php $users = DB::table('users') From f7e7991077d9da880dfa8df53703c7db3f592198 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 24 Jun 2025 01:30:05 +0300 Subject: [PATCH 2317/2609] [12.x] Fix hyphenation of production-ready for grammatical correctness and consistency (#10525) * Update frontend.md * Update vite.md --- frontend.md | 2 +- vite.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend.md b/frontend.md index 2c8d9802cda..31da734876e 100644 --- a/frontend.md +++ b/frontend.md @@ -174,7 +174,7 @@ If you would like to build your frontend using Inertia and Vue / React, you can ## Bundling Assets -Regardless of whether you choose to develop your frontend using Blade and Livewire or Vue / React and Inertia, you will likely need to bundle your application's CSS into production ready assets. Of course, if you choose to build your application's frontend with Vue or React, you will also need to bundle your components into browser ready JavaScript assets. +Regardless of whether you choose to develop your frontend using Blade and Livewire or Vue / React and Inertia, you will likely need to bundle your application's CSS into production-ready assets. Of course, if you choose to build your application's frontend with Vue or React, you will also need to bundle your components into browser ready JavaScript assets. By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. Vite provides lightning-fast build times and near instantaneous Hot Module Replacement (HMR) during local development. In all new Laravel applications, including those using our [starter kits](/docs/{{version}}/starter-kits), you will find a `vite.config.js` file that loads our light-weight Laravel Vite plugin that makes Vite a joy to use with Laravel applications. diff --git a/vite.md b/vite.md index 6e84c64b4cb..5b970e2da04 100644 --- a/vite.md +++ b/vite.md @@ -34,7 +34,7 @@ ## Introduction -[Vite](https://vitejs.dev) is a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. When building applications with Laravel, you will typically use Vite to bundle your application's CSS and JavaScript files into production ready assets. +[Vite](https://vitejs.dev) is a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. When building applications with Laravel, you will typically use Vite to bundle your application's CSS and JavaScript files into production-ready assets. Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production. From c74eeb16593f20ac46d14f210225749e27351c0d Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 24 Jun 2025 01:32:20 +0300 Subject: [PATCH 2318/2609] Remove Mix from Vite docs (#10523) --- vite.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/vite.md b/vite.md index 5b970e2da04..805092d3b22 100644 --- a/vite.md +++ b/vite.md @@ -38,21 +38,6 @@ Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production. -> [!NOTE] -> Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). - - -#### Choosing Between Vite and Laravel Mix - -Before transitioning to Vite, new Laravel applications utilized [Mix](https://laravel-mix.com/), which is powered by [webpack](https://webpack.js.org/), when bundling assets. Vite focuses on providing a faster and more productive experience when building rich JavaScript applications. If you are developing a Single Page Application (SPA), including those developed with tools like [Inertia](https://inertiajs.com), Vite will be the perfect fit. - -Vite also works well with traditional server-side rendered applications with JavaScript "sprinkles", including those using [Livewire](https://livewire.laravel.com). However, it lacks some features that Laravel Mix supports, such as the ability to copy arbitrary assets into the build that are not referenced directly in your JavaScript application. - - -#### Migrating Back to Mix - -Have you started a new Laravel application using our Vite scaffolding but need to move back to Laravel Mix and webpack? No problem. Please consult our [official guide on migrating from Vite to Mix](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-vite-to-laravel-mix). - ## Installation & Setup From 46c13471056c6815c653834b256a4050c097a9aa Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 24 Jun 2025 01:33:09 +0300 Subject: [PATCH 2319/2609] Fix order of sections in HTTP Client docs (#10524) --- http-client.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/http-client.md b/http-client.md index ca42e16d186..8235b97a7bd 100644 --- a/http-client.md +++ b/http-client.md @@ -701,27 +701,6 @@ Http::fake(function (Request $request) { }); ``` - -### Preventing Stray Requests - -If you would like to ensure that all requests sent via the HTTP client have been faked throughout your individual test or complete test suite, you can call the `preventStrayRequests` method. After calling this method, any requests that do not have a corresponding fake response will throw an exception rather than making the actual HTTP request: - -```php -use Illuminate\Support\Facades\Http; - -Http::preventStrayRequests(); - -Http::fake([ - 'github.com/*' => Http::response('ok'), -]); - -// An "ok" response is returned... -Http::get('https://github.com/laravel/framework'); - -// An exception is thrown... -Http::get('https://laravel.com/'); -``` - ### Inspecting Requests @@ -823,6 +802,27 @@ $recorded = Http::recorded(function (Request $request, Response $response) { }); ``` + +### Preventing Stray Requests + +If you would like to ensure that all requests sent via the HTTP client have been faked throughout your individual test or complete test suite, you can call the `preventStrayRequests` method. After calling this method, any requests that do not have a corresponding fake response will throw an exception rather than making the actual HTTP request: + +```php +use Illuminate\Support\Facades\Http; + +Http::preventStrayRequests(); + +Http::fake([ + 'github.com/*' => Http::response('ok'), +]); + +// An "ok" response is returned... +Http::get('https://github.com/laravel/framework'); + +// An exception is thrown... +Http::get('https://laravel.com/'); +``` + ## Events From 60cbbad87c35f694cba0b4d69716df58e13c0bcd Mon Sep 17 00:00:00 2001 From: Kevin Bui Date: Tue, 24 Jun 2025 08:47:45 +1000 Subject: [PATCH 2320/2609] [12.x] Add the failed method to queued mailables. (#10518) * Add the failed method to queued mailables. * formatting --------- Co-authored-by: Taylor Otwell --- mail.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mail.md b/mail.md index a59c7e9d85f..32b5551871c 100644 --- a/mail.md +++ b/mail.md @@ -1076,6 +1076,33 @@ class OrderShipped extends Mailable implements ShouldQueue > [!NOTE] > To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). + +#### Queued Email Failures + +When a queued email fails, the `failed` method on the queued mailable class will be invoked if it has been defined. The `Throwable` instance that caused the queued email to fail will be passed to the `failed` method: + +```php + ## Rendering Mailables From 15719cbad691f3e939012c75d9038d4b44672089 Mon Sep 17 00:00:00 2001 From: Amir Hossein Shokri Date: Tue, 24 Jun 2025 19:37:47 +0330 Subject: [PATCH 2321/2609] improve queue work command docs (#10531) --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 223c6fd8266..9bda3d92f5b 100644 --- a/queues.md +++ b/queues.md @@ -1933,7 +1933,7 @@ php artisan queue:work > [!NOTE] > To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. -You may include the `-v` flag when invoking the `queue:work` command if you would like the processed job IDs to be included in the command's output: +You may include the `-v` flag when invoking the `queue:work` command if you would like the processed job IDs, job connection names, and job queue names to be included in the command's output: ```shell php artisan queue:work -v From d0c3d31bb5b10774ead42265058172598de1ff67 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Jun 2025 18:08:22 +0200 Subject: [PATCH 2322/2609] wip --- queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queues.md b/queues.md index 9bda3d92f5b..f15c69c8ca6 100644 --- a/queues.md +++ b/queues.md @@ -1933,7 +1933,7 @@ php artisan queue:work > [!NOTE] > To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. -You may include the `-v` flag when invoking the `queue:work` command if you would like the processed job IDs, job connection names, and job queue names to be included in the command's output: +You may include the `-v` flag when invoking the `queue:work` command if you would like the processed job IDs, connection names, and queue names to be included in the command's output: ```shell php artisan queue:work -v From 128f9eec5d54d10e0ac79b51b2737b2caa5c1965 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:08:48 +0300 Subject: [PATCH 2323/2609] Missing semi-colon (#10529) --- helpers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index f4c7b76660a..7b1792f8538 100644 --- a/helpers.md +++ b/helpers.md @@ -2085,7 +2085,7 @@ The `uri` function generates a [fluent URI instance](#uri) for the given URI: ```php $uri = uri('https://example.com/') ->withPath('/users') - ->withQuery(['page' => 1]) + ->withQuery(['page' => 1]); ``` If the `uri` function is given an array containing a callable controller and method pair, the function will create a `Uri` instance for the controller method's route path: @@ -2093,7 +2093,7 @@ If the `uri` function is given an array containing a callable controller and met ```php use App\Http\Controllers\UserController; -$uri = uri([UserController::class, 'show'], ['user' => $user]) +$uri = uri([UserController::class, 'show'], ['user' => $user]); ``` If the controller is invokable, you may simply provide the controller class name: From 1615dd424f6a7823c54fcdad1522ed367b90ff4d Mon Sep 17 00:00:00 2001 From: Jay <602425+jshah4517@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:09:13 +0100 Subject: [PATCH 2324/2609] [12.x] Allow latest version of predis (#10528) --- redis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redis.md b/redis.md index 1991d667673..1fdb268af92 100644 --- a/redis.md +++ b/redis.md @@ -20,7 +20,7 @@ Before using Redis with Laravel, we encourage you to install and use the [PhpRed 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: ```shell -composer require predis/predis:^2.0 +composer require predis/predis ``` From a15bc84b1841e692f4e07f0a743dcad9104a8c69 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:09:46 +0300 Subject: [PATCH 2325/2609] [12.x] Format the Queued Email Failures code (#10527) * Format the code with Pint * wip --- mail.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mail.md b/mail.md index 32b5551871c..ceb50b8e42f 100644 --- a/mail.md +++ b/mail.md @@ -1093,6 +1093,8 @@ use Throwable; class OrderDelayed extends Mailable implements ShouldQueue { + use SerializesModels; + /** * Handle a queued email's failure. */ From 490845661d09402f78e083d6d933fae972d031e9 Mon Sep 17 00:00:00 2001 From: Eszter Czotter Date: Tue, 24 Jun 2025 18:10:07 +0200 Subject: [PATCH 2326/2609] Fix Paddle documentation and dashboard urls (#10526) --- cashier-paddle.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cashier-paddle.md b/cashier-paddle.md index 6f2cba862a0..3b8562e5248 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -92,7 +92,7 @@ php artisan migrate ### Paddle Sandbox -During local and staging development, you should [register a Paddle Sandbox account](https://sandbox-login.paddle.com/signup). This account will give you a sandboxed environment to test and develop your applications without making actual payments. You may use Paddle's [test card numbers](https://developer.paddle.com/concepts/payment-methods/credit-debit-card) to simulate various payment scenarios. +During local and staging development, you should [register a Paddle Sandbox account](https://sandbox-login.paddle.com/signup). This account will give you a sandboxed environment to test and develop your applications without making actual payments. You may use Paddle's [test card numbers](https://developer.paddle.com/concepts/payment-methods/credit-debit-card#test-payment-method) to simulate various payment scenarios. When using the Paddle Sandbox environment, you should set the `PADDLE_SANDBOX` environment variable to `true` within your application's `.env` file: @@ -146,7 +146,7 @@ PADDLE_SANDBOX=true The `PADDLE_SANDBOX` environment variable should be set to `true` when you are using [Paddle's Sandbox environment](#paddle-sandbox). The `PADDLE_SANDBOX` variable should be set to `false` if you are deploying your application to production and are using Paddle's live vendor environment. -The `PADDLE_RETAIN_KEY` is optional and should only be set if you're using Paddle with [Retain](https://developer.paddle.com/paddlejs/retain). +The `PADDLE_RETAIN_KEY` is optional and should only be set if you're using Paddle with [Retain](https://developer.paddle.com/concepts/retain/overview). ### Paddle JS @@ -212,7 +212,7 @@ public function boot(): void > [!NOTE] > Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should [configure Paddle's webhook handling](#handling-paddle-webhooks). -Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://www.paddle.com/billing/checkout), you can easily build modern, robust payment integrations. +Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://developer.paddle.com/concepts/sell/overlay-checkout), you can easily build modern, robust payment integrations. To charge customers for non-recurring, single-charge products, we'll utilize Cashier to charge customers with Paddle's Checkout Overlay, where they will provide their payment details and confirm their purchase. Once the payment has been made via the Checkout Overlay, the customer will be redirected to a success URL of your choosing within your application: @@ -318,7 +318,7 @@ Please refer to Paddle's documentation for more information on the [data contain > [!NOTE] > Before utilizing Paddle Checkout, you should define Products with fixed prices in your Paddle dashboard. In addition, you should [configure Paddle's webhook handling](#handling-paddle-webhooks). -Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://www.paddle.com/billing/checkout), you can easily build modern, robust payment integrations. +Offering product and subscription billing via your application can be intimidating. However, thanks to Cashier and [Paddle's Checkout Overlay](https://developer.paddle.com/concepts/sell/overlay-checkout), you can easily build modern, robust payment integrations. To learn how to sell subscriptions using Cashier and Paddle's Checkout Overlay, let's consider the simple scenario of a subscription service with a basic monthly (`price_basic_monthly`) and yearly (`price_basic_yearly`) plan. These two prices could be grouped under a "Basic" product (`pro_basic`) in our Paddle dashboard. In addition, our subscription service might offer an Expert plan as `pro_expert`. @@ -1340,7 +1340,7 @@ Paddle can notify your application of a variety of events via webhooks. By defau By default, this controller will automatically handle canceling subscriptions that have too many failed charges, 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. -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: +To ensure your application can handle Paddle webhooks, be sure to [configure the webhook URL in the Paddle control panel](https://vendors.paddle.com/notifications-v2). 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: - Customer Updated - Transaction Completed @@ -1425,7 +1425,7 @@ 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 secure your webhooks, you may use [Paddle's webhook signatures](https://developer.paddle.com/webhooks/signature-verification). 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_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file. The webhook secret may be retrieved from your Paddle account dashboard. From f22bb81a2bb997441069324a588de198cbc5c2e9 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:11:51 +0300 Subject: [PATCH 2327/2609] [12.x] Add proper link (#10530) * Add proper links * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helpers.md b/helpers.md index 7b1792f8538..c7b24fcde65 100644 --- a/helpers.md +++ b/helpers.md @@ -2131,6 +2131,8 @@ $full = url()->full(); $previous = url()->previous(); ``` +For more information on working with the `url` function, consult the [URL generation documentation](/docs/{{version}}/urls#generating-urls). + ## Miscellaneous From 2bcd0ef8a85256239b1aad340569c8e42f59ad0e Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:35:59 +0300 Subject: [PATCH 2328/2609] Redirect helper last argument (#10536) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index c7b24fcde65..6056d362026 100644 --- a/helpers.md +++ b/helpers.md @@ -2653,7 +2653,7 @@ $policy = policy(App\Models\User::class); The `redirect` function returns a [redirect HTTP response](/docs/{{version}}/responses#redirects), or returns the redirector instance if called with no arguments: ```php -return redirect($to = null, $status = 302, $headers = [], $https = null); +return redirect($to = null, $status = 302, $headers = [], $secure = null); return redirect('/home'); From 8085ccfb1e01266100b897eeebe2018e2c3d923e Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:38:21 +0300 Subject: [PATCH 2329/2609] [12.x] Add broadcast_if and broadcast_unless methods to helpers (#10533) * Add broadcast_if and broadcast_unless to helpers * add links * Update helpers.md --------- Co-authored-by: Taylor Otwell --- helpers.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/helpers.md b/helpers.md index 6056d362026..044b2b9f9c0 100644 --- a/helpers.md +++ b/helpers.md @@ -170,6 +170,8 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [bcrypt](#method-bcrypt) [blank](#method-blank) [broadcast](#method-broadcast) +[broadcast_if](#method-broadcast-if) +[broadcast_unless](#method-broadcast-unless) [cache](#method-cache) [class_uses_recursive](#method-class-uses-recursive) [collect](#method-collect) @@ -2256,6 +2258,28 @@ broadcast(new UserRegistered($user)); broadcast(new UserRegistered($user))->toOthers(); ``` + +#### `broadcast_if()` {.collection-method} + +The `broadcast_if` function [broadcasts](/docs/{{version}}/broadcasting) the given [event](/docs/{{version}}/events) to its listeners if a given boolean expression evaluates to `true`: + +```php +broadcast_if($user->isActive(), new UserRegistered($user)); + +broadcast_if($user->isActive(), new UserRegistered($user))->toOthers(); +``` + + +#### `broadcast_unless()` {.collection-method} + +The `broadcast_unless` function [broadcasts](/docs/{{version}}/broadcasting) the given [event](/docs/{{version}}/events) to its listeners if a given boolean expression evaluates to `false`: + +```php +broadcast_unless($user->isBanned(), new UserRegistered($user)); + +broadcast_unless($user->isBanned(), new UserRegistered($user))->toOthers(); +``` + #### `cache()` {.collection-method} From 388799f067a30bc0e0996e8d33fd1576548d1ece Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:44:41 +0300 Subject: [PATCH 2330/2609] Improve consistency in _if and _unless functions (#10537) --- helpers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.md b/helpers.md index 044b2b9f9c0..683b6ec2111 100644 --- a/helpers.md +++ b/helpers.md @@ -2702,7 +2702,7 @@ report('Something went wrong.'); #### `report_if()` {.collection-method} -The `report_if` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if the given condition is `true`: +The `report_if` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if a given boolean expression evaluates to `true`: ```php report_if($shouldReport, $e); @@ -2713,7 +2713,7 @@ report_if($shouldReport, 'Something went wrong.'); #### `report_unless()` {.collection-method} -The `report_unless` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if the given condition is `false`: +The `report_unless` function will report an exception using your [exception handler](/docs/{{version}}/errors#handling-exceptions) if a given boolean expression evaluates to `false`: ```php report_unless($reportingDisabled, $e); From 2bb4c0c6f50cea2d007915bff95d507d7803adb6 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:15:35 +0300 Subject: [PATCH 2331/2609] Rename the person parameter to users (#10545) --- validation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validation.md b/validation.md index 863ff1a6c5e..80930f03040 100644 --- a/validation.md +++ b/validation.md @@ -2471,8 +2471,8 @@ You may also validate each element of an array. For example, to validate that ea ```php $validator = Validator::make($request->all(), [ - 'person.*.email' => 'email|unique:users', - 'person.*.first_name' => 'required_with:person.*.last_name', + 'users.*.email' => 'email|unique:users', + 'users.*.first_name' => 'required_with:users.*.last_name', ]); ``` @@ -2480,8 +2480,8 @@ Likewise, you may use the `*` character when specifying [custom validation messa ```php 'custom' => [ - 'person.*.email' => [ - 'unique' => 'Each person must have a unique email address', + 'users.*.email' => [ + 'unique' => 'Each user must have a unique email address', ] ], ``` From 82a09c0ec769132805cc789ec9ae8848faddf3b3 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:15:55 +0300 Subject: [PATCH 2332/2609] Fix wording in Arr::forget method (#10544) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 683b6ec2111..49a4372cc51 100644 --- a/helpers.md +++ b/helpers.md @@ -482,7 +482,7 @@ $value = Arr::float($array, 'name'); #### `Arr::forget()` {.collection-method} -The `Arr::forget` method removes a given key / value pair from a deeply nested array using "dot" notation: +The `Arr::forget` method removes a given key / value pairs from a deeply nested array using "dot" notation: ```php use Illuminate\Support\Arr; From 5882483ea1afa05fc6eccfe36751c5ee238f44a2 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:16:51 +0300 Subject: [PATCH 2333/2609] Remove outdated PHP version warning from Octane (#10541) --- octane.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/octane.md b/octane.md index 7e5fe5eee14..7ed30a9264f 100644 --- a/octane.md +++ b/octane.md @@ -47,9 +47,6 @@ php artisan octane:install ## Server Prerequisites -> [!WARNING] -> Laravel Octane requires [PHP 8.1+](https://php.net/releases/). - ### FrankenPHP From ce76238475535356a60631a0bfc7fa6be972992d Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:17:08 +0300 Subject: [PATCH 2334/2609] Improve clarity (#10542) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index 49a4372cc51..f07bbefb058 100644 --- a/helpers.md +++ b/helpers.md @@ -3415,7 +3415,7 @@ Sleep::assertNeverSlept(); Sleep::assertInsomniac(); ``` -Sometimes it may be useful to perform an action whenever a fake sleep occurs in your application code. To achieve this, you may provide a callback to the `whenFakingSleep` method. In the following example, we use Laravel's [time manipulation helpers](/docs/{{version}}/mocking#interacting-with-time) to instantly progress time by the duration of each sleep: +Sometimes it may be useful to perform an action whenever a fake sleep occurs. To achieve this, you may provide a callback to the `whenFakingSleep` method. In the following example, we use Laravel's [time manipulation helpers](/docs/{{version}}/mocking#interacting-with-time) to instantly progress time by the duration of each sleep: ```php use Carbon\CarbonInterval as Duration; From 944a3fbc1aa83941a81b05b75f7ef2885f069564 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:17:19 +0300 Subject: [PATCH 2335/2609] Minor language update (#10540) --- helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers.md b/helpers.md index f07bbefb058..04eb2db2cfb 100644 --- a/helpers.md +++ b/helpers.md @@ -3072,7 +3072,7 @@ To invoke a callback more than once, you may specify the number of iterations th Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms ``` -Sometimes, you may want to benchmark the execution of a callback while still obtaining the value returned by the callback. The `value` method will return a tuple containing the value returned by the callback and the amount of milliseconds it took to execute the callback: +Sometimes, you may want to benchmark the execution of a callback while still obtaining the value returned by the callback. The `value` method will return a tuple containing the value returned by the callback and the number of milliseconds it took to execute the callback: ```php [$count, $duration] = Benchmark::value(fn () => User::count()); From 873c949f1a40e4d99bd76389a9901ac39b451dc0 Mon Sep 17 00:00:00 2001 From: Kevin Bui Date: Sun, 29 Jun 2025 03:21:17 +1000 Subject: [PATCH 2336/2609] [12.x] Add the orderByDesc method (#10539) * Add the orderByDesc method. * Update queries.md --------- Co-authored-by: Taylor Otwell --- queries.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/queries.md b/queries.md index 02053a8f1f8..74c9a742ecb 100644 --- a/queries.md +++ b/queries.md @@ -1122,6 +1122,14 @@ $users = DB::table('users') ->get(); ``` +The sort direction is optional, and is ascending by default. If you want to sort in descending order, you can specify the second parameter for the `orderBy` method, or just use `orderByDesc`: + +```php +$users = DB::table('users') + ->orderByDesc('verified_at') + ->get(); +``` + #### The `latest` and `oldest` Methods From fa5ace397db97fb06e50fdbe630a9bd0080ce30d Mon Sep 17 00:00:00 2001 From: Jesper Noordsij <45041769+jnoordsij@users.noreply.github.com> Date: Tue, 1 Jul 2025 17:48:37 +0200 Subject: [PATCH 2337/2609] Add --dev flag for installing Pail (#10554) --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index 8ec10a49b8e..c80bdc017a8 100644 --- a/logging.md +++ b/logging.md @@ -534,7 +534,7 @@ Laravel Pail is a package that allows you to easily dive into your Laravel appli To get started, install Pail into your project using the Composer package manager: ```shell -composer require laravel/pail +composer require --dev laravel/pail ``` From 390016b5854f02b3e6eecc718241808105e8c978 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Tue, 1 Jul 2025 19:04:35 +0300 Subject: [PATCH 2338/2609] Fix grammatical inconsistency (#10550) --- blade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blade.md b/blade.md index 0a901f57547..f34f1a220f3 100644 --- a/blade.md +++ b/blade.md @@ -161,7 +161,7 @@ Sometimes you may pass an array to your view with the intention of rendering it ``` -However, instead of manually calling `json_encode`, you may use the `Illuminate\Support\Js::from` method directive. The `from` method accepts the same arguments as PHP's `json_encode` function; however, it will ensure that the resulting JSON is properly escaped for inclusion within HTML quotes. The `from` method will return a string `JSON.parse` JavaScript statement that will convert the given object or array into a valid JavaScript object: +However, instead of manually calling `json_encode`, you may use the `Illuminate\Support\Js::from` method directive. The `from` method accepts the same arguments as PHP's `json_encode` function; however, it will ensure that the resulting JSON has been properly escaped for inclusion within HTML quotes. The `from` method will return a string `JSON.parse` JavaScript statement that will convert the given object or array into a valid JavaScript object: ```blade +@endPushOnce + + +@pushOnce('scripts', 'chart.js') + +@endPushOnce +``` + ### Raw PHP